Skip to content

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

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

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

Conversation

@anth-volk

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

Copy link
Copy Markdown
Contributor

Fixes #784

Same-Repository Draft PR Check

  • This PR is a draft.
  • This PR is from a branch in PolicyEngine/policyengine-sim-api, not a personal fork.

Summary

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

  • Rules: ruff.toml gains S, T20, LOG, G, DTZ, ERA, TD, FIX and EXE. All 244 findings at the base are fixed or kept with an inline reason, one commit per family. ERA, TD, FIX and EXE had no findings.
  • Behaviour changes (all deliberate):
    • Gateway auth misconfiguration log (G201): it now uses logger.exception("Gateway auth misconfigured"). Before, it logged "Gateway auth misconfigured: Gateway auth misconfigured: set GATEWAY_AUTH_ISSUER …", repeating the message; the traceback still ends with the full RuntimeError message. The 503 response is unchanged.
    • Logger name (LOG002): the executor's simulation.py uses getLogger(__name__) instead of getLogger(__file__).
    • Gateway image smoke: its local entrypoint raises RuntimeError with a message instead of failing a bare assert. The smoke script and the integration test only check the exit code.
    • Six f-string log calls become %s arguments with identical text (G004).
  • Asserts outside tests (S101):
    • Contract spm.py (4): the asserts couldn't fail, because _require_complete_selection already raises ValueError when either value is missing. It now returns them, so nothing new is raised and Pydantic validators still see ValueError.
    • Executor stage12_qualification.py: the closure captures a non-optional runtime instead of asserting it.
    • Test double in fixtures/: raises AssertionError explicitly, as the repo's other fakes do.
    • authenticated_tests: this is the deployed-service pytest suite (run by simulation-deploy.reusable.yml), which the **/tests/** glob didn't match. It gets the same exemption (ANN, D1, PLR2004, S101), registered as sim.quality.deployed-tests-ruff-exemptions.
    • Nothing in the repo runs Python with -O.
  • Prints in commands (T201): command-line scripts and Modal local entrypoints keep their output. Short ones carry an inline reason; update_version_registry.py (22 prints) and .github/scripts/update_policyengine_package.py route output through one helper. No application code had a debug print.
  • Subprocess calls (S603, S607): each test module that runs repository scripts now does it through one helper with one reasoned suppression, for example _run_bash in test_modal_scripts.py (32 calls). Executables still come from the child's PATH, since several tests pass their own env.
  • Datetimes (DTZ): every value stays naive, matching .py's ParameterValue dates and its own period parsing. The 11 naive test dates go through one _naive_policy_date helper.
  • Other security findings: no vulnerability found.
    • Fixed: the GCP metadata probe builds its Request inline, with identical URL, headers and method.
    • Kept with a reason:
      • fake tokens in tests, now module constants;
      • Modal secret and display names;
      • the Modal container's fixed /tmp/policyengine-data paths;
      • image tests that exec repository-owned app source;
      • tests that pickle objects they built;
      • the Hugging Face Hub URL built from HF_ENDPOINT and quoted path parts.
    • S110: each try/except/pass already carried a reasoned BLE001 suppression from group 5, which now lists S110 too.
  • Modal images: code changes in the executor image's simulation.py, simulation_runtime.py, stage12_qualification.py and update_version_registry.py, and in the shared contract, observability and fastapi libraries. src/modal/app.py changes only in comments.
  • Registry: sim.quality.ruff-lint now lists the nine families. The shared rules tool is synced from the Public API's group 6 (Enable ruff security, logging and debugging rules (S, T20, LOG, G, DTZ, ERA, TD, FIX, EXE) policyengine-api#3915), so it's byte-identical again (tool sha256 1c4bbb2a93f8).

Testing

  • All seven projects' test suites pass, with the same counts as the base:

    Project Passed
    fastapi 14
    contract 145
    observability 30
    stage12 12
    entry 224
    gateway 214
    executor 744
  • The gateway golden OpenAPI test passes, and the golden file is unchanged.

  • ruff check (0.15.22) is clean repo-wide, and ruff format --check is unchanged.

  • The rules guard, the ci-scripts guard and the rules tool unittest pass.

  • The integration smoke tests need real Modal, so they were only collected.

  • CI/CD: no workflow changed, and no environment variable was added or rewired.

  • 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 #754). Its full run starts once the PRs below it merge.

🤖 Generated with Claude Code

anth-volk and others added 10 commits October 14, 2026 21:45
Pass logging arguments instead of f-strings (G004); each message renders
to the same text. Name the economy-comparison router's logger after its
module rather than its file path (LOG002). Log the gateway's auth
misconfiguration with logger.exception and a fixed message (G201): the
traceback already ends with the exception and its message, which the old
call also repeated in the message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine.py parses policy period keys with strptime and no zone, so
its ParameterValue dates are naive, and the executor's period parsing and
the tests that compare against those dates must stay naive too. Mark each
kept naive datetime with a reasoned DTZ suppression, and route the policy
tests' expected dates through one helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deployed-service test suite in authenticated_tests is a pytest suite
that the tests glob misses, so give it the same test exemptions.

Elsewhere, drop the asserts that only narrowed types: the SPM selection
check now returns the hash and scenario it has already proved present,
and the qualification runner closes over a non-optional runtime. The
gateway image smoke raises RuntimeError when its report is incomplete, as
the executor's static-file smoke does, and the fake model output raises
AssertionError explicitly when the adapter asks for a mode other than
JSON.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every print the rule finds belongs to a command-line script or a Modal
local entrypoint, whose stdout is its output, so keep each print. Files
with one to three prints mark each with a reasoned suppression; the
routing-state publisher and the policyengine package updater send their
output through one small helper that holds the only print.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each test module that runs repository scripts now does so through one
helper that holds the only subprocess call, with reasoned suppressions:
the scripts are repository-owned, their arguments are built by the tests,
and bash and uv come from PATH as they do in the workflows. Resolving the
executables in Python instead could pick a different binary, because
subprocess searches the child's PATH, and some tests run scripts with an
empty environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build the GCP metadata request inline from its constant http URL, with
the same Metadata-Flavor header, so S310 can see the scheme. Keep every
other finding with a reasoned suppression:

- S105/S106: fake tokens in tests (one module constant per test module
  where a value repeats), and names or fingerprints that only look like
  secrets.
- S108: the executor's fixed data directories inside the Modal container,
  and a lookup path the tests' fakes only pass along.
- S110: add S110 to the existing reasoned BLE001 suppressions.
- S102: the image tests execute repository-owned app source as Modal's
  /root/app.py.
- S301: the SPM tests round-trip errors they built or raised.
- S310: the Hub API request URL is built from the HF_ENDPOINT base and
  quoted path parts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Select DTZ, ERA, EXE, FIX, G, LOG, S, T20 and TD. The earlier commits on
this branch fixed or suppressed, with a reason, every finding outside the
shared scripts/rules tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sim.quality.ruff-lint now lists DTZ, ERA, EXE, FIX, G, LOG, S, T20 and
TD. The authenticated_tests exemption gets its own rule, since a ruff
per-file-ignore rule names one pattern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared scripts/rules tool takes the Public API's group 6 fixes:
explicit checks instead of asserts, and reasoned suppressions for its
git and shellcheck subprocess calls, prints and calendar dates. The
rendered rules page picks up the tool's new hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#743 and #744 landed on main after this stack branched. Group 6, which
this PR enables, flags their new code; each fix follows this PR's
conventions:

- T201: the routing publisher's new commands report through its _report
  helper, the package updater's dispatch check through _write_result, and
  the sentinel CLI gets one _report helper that holds its only print.
- S603/S607: the deployment-asset tests route #744's new bash calls
  through _run_bash, which now takes cwd; the rollback tests and the
  package-update helper tests get one helper each that holds the only
  subprocess call, with reasoned suppressions.
- S310: the sentinel's HTTP helper opens the deployed entrypoint or the
  test auth issuer URL that the workflow passes; reasoned suppressions.
- S106: a fake bearer token in the sentinel tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk force-pushed the ruff-security-logging branch from ab3ad4d to 1836b2e Compare October 14, 2026 17:56

This branch has not been 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

Development

Successfully merging this pull request may close these issues.

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

1 participant