Skip to content

update_household UPDATE misses country_id predicate, cross-country overwrite possible #3447

@MaxGhenis

Description

@MaxGhenis

Summary

update_household issues UPDATE household SET ... WHERE id = ? without scoping to country_id. Because household IDs are not partitioned per country, a valid PUT from one country can silently overwrite a row that actually belongs to another country — most visibly corrupting api_version to the wrong country's package version.

Location

policyengine_api/services/household_service.py:110-119

What goes wrong

household_hash: str = hash_object(household_json)
api_version: str = COUNTRY_PACKAGE_VERSIONS.get(country_id)

database.query(
    f"UPDATE household SET household_json = ?, household_hash = ?, label = ?, api_version = ? WHERE id = ?",
    (
        json.dumps(household_json),
        household_hash,
        label,
        api_version,
        household_id,
    ),
)

If a caller PUTs /uk/household/<id> where <id> is actually a us row, the row is rewritten with api_version from COUNTRY_PACKAGE_VERSIONS["uk"]. Subsequent reads that assume country_id matching will return inconsistent data.

Suggested fix

Add the country predicate to WHERE, and verify the update affected exactly one row before returning success:

result = database.query(
    "UPDATE household SET household_json = ?, household_hash = ?, label = ?, api_version = ? "
    "WHERE id = ? AND country_id = ?",
    (
        json.dumps(household_json),
        household_hash,
        label,
        api_version,
        household_id,
        country_id,
    ),
)
# raise NotFound if 0 rows updated

Severity

High.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions