Skip to content

Missing project/integration scoping in sub-resource database queries (IDOR) #3660

@lighthousekeeper1212

Description

@lighthousekeeper1212

Summary

Several integration sub-resource database queries in db/sql/integration.go accept projectID and integrationID parameters but do not use them in the SQL WHERE clause, querying only by the sub-resource's own ID. This means the project scoping enforced by the middleware chain is not reflected at the database layer.

Affected Functions

1. GetIntegrationExtractValue (line ~204)

func (d *SqlDb) GetIntegrationExtractValue(projectID int, valueID int, integrationID int) (value db.IntegrationExtractValue, err error) {
    query, args, err := squirrel.Select("v.*").
        From("project__integration_extract_value as v").
        Where(squirrel.Eq{"id": valueID}).  // only filters by valueID
        OrderBy("v.id").
        ToSql()

Both projectID and integrationID are received but unused.

2. GetIntegrationMatcher (line ~295)
Same pattern — queries WHERE id = matcherID only, ignoring projectID and integrationID.

3. UpdateIntegrationExtractValue (line ~237)

_, err = d.exec(
    "update project__integration_extract_value set ... where `id`=?",
    ...
    integrationExtractValue.ID)

Updates by ID only without checking the value belongs to the expected integration.

4. GetIntegrationMatchers (line ~279)
Queries by integration_id only without project scoping.

Secure Pattern for Comparison

The parent GetIntegration function correctly scopes by project:

func (d *SqlDb) GetIntegration(projectID int, integrationID int) (integration db.Integration, err error) {
    err = d.getObject(projectID, db.IntegrationProps, integrationID, &integration)

Similarly, DeleteIntegrationExtractValue uses deleteObjectByReferencedID which validates the integration_id relationship.

Impact

A user with access to Project A could read or modify integration extract values and matchers belonging to Project B by guessing or enumerating the sequential integer IDs in API requests like:

  • GET /api/project/{projectA_id}/integrations/{integrationA_id}/values/{valueFromProjectB_id}
  • PUT /api/project/{projectA_id}/integrations/{integrationA_id}/matchers/{matcherFromProjectB_id}

The IntegrationMiddleware validates that the integration belongs to the project, but the sub-resource queries don't validate that the value/matcher belongs to the integration.

Suggested Fix

Add integrationID filtering to all sub-resource queries:

// GetIntegrationExtractValue - add integration_id check
Where(squirrel.Eq{"id": valueID, "integration_id": integrationID}).

// GetIntegrationMatcher - add integration_id check  
Where(squirrel.Eq{"id": matcherID, "integration_id": integrationID}).

// UpdateIntegrationExtractValue - add integration_id check
"... where integration_id=? AND `id`=?"

// GetIntegrationMatchers - add project scoping via join or sub-query

This is a 4-line fix that brings these queries in line with the secure patterns already used elsewhere in the codebase.

Environment

  • Version: Latest (main branch as of Feb 2026)
  • File: db/sql/integration.go
  • 8 API endpoints affected across values and matchers CRUD operations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions