Repository navigation
Conversation
Every third-party action is pinned to a full commit SHA with a version comment, each checked against its upstream tag. dependency-review fails PRs that add high-severity vulnerable dependencies. Dependabot updates actions and pip weekly. ci.yml gains a gate-tests job for the security review gate added next.
pull_request_target workflow that runs the reviewer from the default branch only and reads the PR as API data. Deterministic pre-checks (hidden unicode, binaries, size, workflow spoofing and pull_request_target, pipe to shell) hard fail. Sensitive paths switch to strict mode. Two passes through the pinned Claude Code CLI with all tools disabled: reviewer, then an adversarial verifier. Fail closed on any error. One updated PR comment. Fixtures are real commits from this repo's history plus benign changes. scripts/setup_ruleset.sh builds the master ruleset; it is not applied.
…to-shell, numstat binary detection The gate's own diff tripped its pre-checks: literal hidden characters in the sanitiser regex and a test, curl|sh named in prose, and the text 'Binary files' in sources.py. Adds --exclude for local runs.
…t check GitHub Actions is no longer a bypass actor: it would let any workflow with contents: write push to master. The only bypass on master and on the new refs/tags/v* ruleset (creation, update, deletion blocked) is DeployKey. The API takes no id for it, so the script requires --deploy-key-id to be the repository's only write deploy key. Required checks now include the PR title lint job when pr-title-lint.yml is on master.
Wider invisible-character set (tag block, word joiners, fillers) and commit messages checked; workflow_run treated like pull_request_target; any mention of security-review in another workflow fails; more sensitive paths; the model check requires exactly the pinned model; a confirmation beats a refutation among duplicate assessments. The fixture exemption is dropped from the prompts since the fixtures are real historical code.
…paths .github/, Dockerfile, add-on config, pyproject.toml, HACS manifests, scripts/, run.sh and the systemd unit are owned by @odtgit. A second master ruleset, owner-review, requires one approval, code owner review and last-push approval. It bypasses for the admin role on pull requests only and for the release deploy key; master-protection keeps the deploy key as its only bypass, so the owner's PRs still need security-review. The script reads back that role 5 resolves to admin after applying.
Owner
Author
|
Dropped: rescoped as over-engineered for this repo. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a required
security-reviewcheck: a deterministic plus two-pass adversarial AI review of every PR, fail closed, before anything reaches master and the auto-release to PyPI, GHCR and Home Assistant users. Also adds SHA pinning across all workflows, dependency review, Dependabot and a ruleset script.Design
.github/workflows/security-review.ymlonpull_request_target(opened, synchronize, reopened, ready_for_review). One concurrency group per PR with cancel-in-progress.security-review(contents: read,pull-requests: read) runs.github/security-review/review.py.security-review-comment(pull-requests: write, no Claude token) creates or updates one PR comment.policy.py) run first and hard fail:pull_request_targetadded outside the gate,curl|shin sensitive files, or any other workflow defining a job namedsecurity-review(spoofing).github/**, Dockerfile, compose, add-on config, pyproject, requirements,custom_components/*/manifest.json,hacs.json,run.sh,scripts/**,*.service,.gitattributesandSECURITY.md.claude-opus-5-5at high effort:verdict.py) is FAIL on:Trust boundary
.github/security-reviewatgithub.sha. A PR cannot weaken the gate that reviews it.CLAUDE_CODE_OAUTH_TOKENthrough the Claude Code CLI (a subscription OAuth token, not an API key).claude -p --tools "" --strict-mcp-config --setting-sources "" --disable-slash-commands --permission-mode dontAsk --max-turns 1 --json-schema ... --output-format stream-json.GITHUB_TOKEN.llm.pyfails closed unless the init event lists only theStructuredOutputsink and no MCP servers, and no other tool is called. Real runs confirmed the tool list is['StructuredOutput']and that an instruction totoucha file did nothing.install_claude.sh, with no install scripts. The sha256 was cross-checked against npm's sha512 integrity.Supply chain
ci.ymlandrelease.yml, and in the new workflows, is pinned to a full SHA with# vX.Y.Z. Each SHA was verified withgh apiagainst the tag (and the moving major tag where one exists).pypa/gh-action-pypi-publishrelease/v1is pinned to v1.14.2, which is the same commit.dependency-reviewworkflow uses dependency-review-action v5.0.0 withfail-on-severity: high..github/dependabot.yml: github-actions (grouped) and pip, weekly. Dependabot PRs go through the same gate.scripts/setup_ruleset.sh --deploy-key-id ID(not run):master: requires a PR, squash only and linear history; blocks force pushes and deletion; applies to admins.security-review,dependency-review, lint, typecheck, gate-tests,test (3.10..3.13),lint-pr-titlefrom ci: automatic releases on merge to master #24'spr-title-lint.yml, and tests-ha/hassfest/hacs when present. All are read from master's workflows at run time;--workflows-from DIRpreviews unmerged workflows.release-tagsruleset onrefs/tags/v*blocks creation, update and deletion. ci: automatic releases on merge to master #24 does not add one.DeployKey. It is not GitHub Actions (15368, verified via the API), because that would let anycontents: writeworkflow push. The rulesets API requiresactor_id: nullfor DeployKey, so the bypass covers every write deploy key. The script therefore refuses to apply unless the given key is the only write deploy key.--dry-runprints all three payloads.owner-review, a second master ruleset:pull_requestmode, so the owner's own PRs merge without self-approval; plus the deploy key, becauserelease:commits touch owned files and would otherwise be blockedmaster-protectionhas no admin bypass, so the owner's PRs still needsecurity-reviewadmin..github/CODEOWNERS(@odtgit) covers/.github/,/Dockerfile,/config.yaml,/repository.yaml,/pyproject.toml,/custom_components/*/manifest.json,/hacs.json,/scripts/,/run.shand/evmqtt.service.Fixtures and results
Real historical changes from this repo, snapshotted as inert
*.fxfiles withfixture_from_git.py. No synthetic malware was written or tested. Run with the real model viaevaluate.py:c 13:* rmwCosts are the CLI's list-price estimate; the gate runs on the subscription token. The fixtures ran in parallel, so the times are per fixture for both passes. The results held across the last three runs.
hist_event_keys_recordedwas only rated low (a PASS, so a miss). I tuned the prompts, not the fixtures, to treat keystrokes persisted to the HA recorder or other storage as high. All five now match.pull_request_target, size limits), the verdict logic with mocked LLM output, fail-closed CLI stream parsing through a fake CLI binary, and comment sanitising and upsert (a spoofed marker from another user is ignored).curl|shnamed in prose tripping the regexfrom_gitbinary detection being fooled by the text "Binary files" in its own sourceThis PR itself
The gate does not run on this PR:
pull_request_targettakes the workflow from master, where it does not exist yet. Ran locally instead with the pinned CLI and the OAuth token:tests/fixtures/: self-review results, in order:workflow_run. All of them are fixed in later commits, except that the fixtures are excluded locally only because of the size limit..github/security-reviewpaths (a false positive). That is fixed too, and the pre-checks now pass on this diff.Owner steps
CLAUDE_CODE_OAUTH_TOKENis already set. Only if Dependabot PRs fail with "secret is missing": add the same value as a Dependabot secret.pull_request_target: GitHub docs say those workflows "will always run, regardless of approval settings". That is intended here, since the gate code is base-owned. The setting matters forci.yml/dependency-review.yml, and it stops a fork's ownpull_requestworkflow from spoofing thesecurity-reviewname before you have read the gate comment.RELEASE_DEPLOY_KEYin environmentrelease, restricted to master), and make sure it is the only write deploy key.scripts/setup_ruleset.sh --deploy-key-id <id> --dry-run, review the JSON, then run it without--dry-run.security-review. The docs confirm rules aggregate and bypass lists are per ruleset, but not in so many words that bypassing one ruleset leaves the others in force.Residual risks
pull_request_targetruns without approval, so anyone can spend subscription usage. This is bounded by the limits, draft deferral and cancellation.v*tags.required_approving_review_count: 1applies to every non-owner PR, not only to owned paths.pip installof tools is unpinned.Details in
SECURITY.md.https://claude.ai/code/session_01TJskTZQqRHiHQBc8kE5Lr2