Skip to content

Enable ruff security, logging and debugging rules (S, T20, LOG, G, DTZ, ERA, TD, FIX, EXE) - #3915

Draft
anth-volk wants to merge 11 commits into
ruff-exceptionsfrom
ruff-security-logging
Draft

anth-volk wants to merge 11 commits into
ruff-exceptionsfrom
ruff-security-logging

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #3914

Also fixes #3909: the country validator's print("Validating country") is removed.

Summary

Phase 1 step 1.8, group 6: security, logging and debugging leftovers. Stacked on #3913.

  • Rules: select gains S, T20, LOG, G, DTZ, ERA, TD, FIX and EXE. All 262 findings at the base are fixed or kept with an inline reason, one commit per family.
  • Behaviour changes (three kinds, all deliberate):
    • Debug prints removed (T201): the country validator (validate_country prints "Validating country" on every request #3909), plus the create, get and update prints in the report output and simulation routes. The two update prints become logger.debug calls on a module logger, because their ids come from the request body.
    • Logger name (LOG015): the household calculation routes log their three errors on a module logger instead of the root logger. The message text is unchanged, and production records still reach the root observability handler.
    • Nothing else: error responses, message text and published schemas are unchanged.
  • Asserts outside tests (S101, 10): each type-narrowing assert in the v2 household, policy and user-household services, and two in the rules tool, becomes an explicit check that raises TypeError. None runs inside a Pydantic validator, and nothing catches AssertionError. Every check was already guaranteed by an earlier one, so none is expected to fire; the v2 routes and the mirror paths treat either type the same way. No production command runs Python with -O, so these asserts were live.
  • Prints in commands (T201): command-line scripts keep their output. Short ones carry an inline reason; capture_migration_baseline.py, smoke_test_local_boot.py and the rules tool's CLI route output through one _report helper. Application code keeps the api.py startup timings, the gateway-auth warning on stderr and the JSON evidence of the migration qualification commands.
  • Datetimes (DTZ, 21): no value switches between naive and aware. The catalog extraction builds its aware UTC value directly; the other 20 stay naive with a reason (naive database columns, the local calendar year, calendar-day review dates in the rules tool, and test values for those).
  • Security (S): no vulnerability found.
    • Fixed: hashlib.sha1(..., usedforsecurity=False) (same digest), and the GPT-4 experiment parses embeddings with ast.literal_eval instead of eval.
    • Kept with a reason: fake secrets in tests (now mostly module constants with one suppression each), environment-variable and Secret Manager names, tests running this repository's scripts and git, fixed https and loopback URLs, table names from a fixed tuple, a seeded load-test mix, and the experiment's requests calls, where a timeout would change behaviour.
    • S110: each try/except/pass already carried a reasoned BLE001 suppression from group 5, which now lists S110 too.
  • Leftovers: 32 lines of commented-out code are deleted (29 in tests/to_refactor/python/test_policy.py). The two TODOs become plain notes; the user-profile one keeps its link to Utilize a technology like Marshmallow to validate inbound requests #2054 and says it was closed. Six Python scripts with a shebang and a __main__ entry point are now executable.
  • Registry: api.quality.ruff-lint now lists the nine families. The shared rules tool changed (explicit checks and reasoned suppressions), so its hash is now 1c4bbb2a93f8; sim takes the same copy in Enable ruff security, logging and debugging rules (S, T20, LOG, G, DTZ, ERA, TD, FIX, EXE) policyengine-sim-api#785.

Testing

  • pytest tests/unit tests/contract, to_refactor and the dedupe integration test (the make test set): 2,420 passed, the same as the base.
  • make typecheck-v2 is clean. mypy on the 78 changed files outside that gate gives the same output as the base.
  • ruff check and ruff format --check (0.15.12) are clean repo-wide.
  • Every Pydantic model schema (230 models, 100 adapters), the FastAPI OpenAPI with every native router enabled, the /v2/openapi.json preview and the legacy spec are byte-identical to the base.
  • The quality guards, the rules guard and the rules tool unittest (75 tests) pass.
  • CI/CD: no workflow changed. Four .github scripts changed only in comments, output helpers or file mode, and the environment variables they read are unchanged.
  • CI: this PR is above the bottom of its stack, so it runs only CI complete, which fails and says why (step 0.7, Make CI enforcement trustworthy before the rules registry #3887). Its full run starts once the PRs below it merge.

🤖 Generated with Claude Code

anth-volk and others added 11 commits October 13, 2026 21:29
Delete the commented-out TestPolicySearch class in the to-refactor policy
tests, along with the note that introduced it, the commented-out profiler
import in the Flask app and the commented-out service imports in the
services package. The GPT-4 experiment's example reform is reworded as
prose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each of these six scripts has a python3 shebang and a __main__ entry
point, so it is meant to be run directly. Git now records them as
executable; running them through python is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user-profile update note keeps its link to issue #2054, which was
closed as not planned, and now says so. The simulation serializer note
explains that its JSON-in-a-string projection serves only the v1
contract.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three logging.exception calls in the household routes used the root
logger. They now use a module logger, so the records are named after
policyengine_api.routes.household_routes; the message text and traceback
are unchanged. The module keeps its existing structured logger for
log_struct.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog extractor builds a date's midnight with tzinfo=UTC directly
instead of attaching UTC afterwards; the result is the same aware value.

Every other naive value stays naive, with an inline reason: reform
impact start_time and report run timestamps are naive columns (and the
runtime cache's datetime.min fallback must compare with them), economy
start_time has always held local time, the default year and household
period follow the host's local calendar, and the rules registry checks
review and expiry dates against the local date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The eight asserts in the v2 household, policy and user-household
services and the two in the rules ci-scripts guard now raise TypeError
when the narrowed value has the wrong type. None runs inside a Pydantic
validator and nothing catches AssertionError: the v2 routes return their
generic 500 and the mirror paths report an "unexpected" failure for
either type, and the rules callers check for a missing [ci_scripts]
table first. The checks also hold under python -O, which strips asserts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
validate_country no longer prints "Validating country" on every request
(#3909). The simulation and report routes no longer print on create and
get, where the URL already carries the country and id; their update
messages, whose id comes from the request body, become debug records on
a module logger with the same text.

The remaining prints are output by design and carry a reason: the CLI
entry points' JSON evidence and results, the startup timings and the
unauthenticated simulation client warning on stderr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Printing is these scripts' job. The four scripts with more than three
prints (the version bump, the migration baseline capture, the local boot
smoke test and the rules CLI) route their output through one _report
helper. Three of those helpers' prints carry the suppression; the version
bump's helper passes its file argument through, which T201 does not flag,
and its stderr messages still go to stderr. Every other print carries an
inline reason, as does the catalog publication qualification test, whose
printed evidence is its report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Behaviour-identical fixes: the runtime cache version's sha1 is marked
usedforsecurity=False (the digest is unchanged), and the GPT-4
experiment parses its embedding strings with ast.literal_eval.

Test modules reuse one constant per fake credential or claim token
instead of repeating the literal, and the Cloud Run deploy-script tests
run bash through one helper.

Every other finding keeps its code with an inline reason: try/except/pass
on the deliberate telemetry and logging catch-alls (S110, alongside their
BLE001 reason), environment-variable and Secret Manager names and test
sentinels (S105, S106), fixed repository commands (S603, S607), fixed
https and loopback URLs (S310), the experiment's untimed requests
(S113), the load test's seeded random mix (S311) and fixed table names
in a test query (S608).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Select flake8-datetimez (DTZ), eradicate (ERA), flake8-executable (EXE),
flake8-fixme (FIX), flake8-logging-format (G), flake8-logging (LOG),
flake8-bandit (S), flake8-print (T20) and flake8-todos (TD). Tests
follow all nine: their exemptions stay limited to S101, D1, ANN and
PLR2004.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
api.quality.ruff-lint now lists DTZ, ERA, EXE, FIX, G, LOG, S, T20 and
TD. The rendered rules page also picks up the rules tool's new hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant