ghostrules 0.3.0__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.ghostrules.lock +7 -7
- ghostrules-0.4.1/.github/dependabot.yml +10 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/workflows/ci.yml +2 -2
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/workflows/corpus.yml +10 -9
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/workflows/release.yml +27 -6
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/workflows/self-test.yml +8 -2
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.gitignore +3 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/CHANGELOG.md +141 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/CLAUDE.md +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/CONTRIBUTING.md +15 -1
- ghostrules-0.4.1/PKG-INFO +245 -0
- ghostrules-0.4.1/README.md +185 -0
- ghostrules-0.4.1/action-requirements.in +3 -0
- ghostrules-0.4.1/action-requirements.txt +60 -0
- ghostrules-0.4.1/action.yml +320 -0
- ghostrules-0.4.1/assets/banner-dark.png +0 -0
- ghostrules-0.4.1/assets/banner-light.png +0 -0
- ghostrules-0.4.1/assets/logo.svg +7 -0
- ghostrules-0.4.1/assets/social-preview.png +0 -0
- ghostrules-0.4.1/bench/.cache-run.log +1 -0
- ghostrules-0.4.1/bench/README.md +44 -0
- ghostrules-0.4.1/bench/RESULTS.md +103 -0
- ghostrules-0.4.1/bench/attacks/context-and-config.toml +246 -0
- ghostrules-0.4.1/bench/attacks/prose.toml +463 -0
- ghostrules-0.4.1/bench/attacks/sources.toml +42 -0
- ghostrules-0.4.1/bench/attacks/unicode.toml +212 -0
- ghostrules-0.4.1/bench/attacks/w5.toml +237 -0
- ghostrules-0.4.1/bench/heldout-labels.toml +130 -0
- ghostrules-0.4.1/bench/heldout-results.json +116 -0
- ghostrules-0.4.1/bench/heldout.toml +315 -0
- ghostrules-0.4.1/bench/lib.py +220 -0
- ghostrules-0.4.1/bench/run.py +357 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/AGENT_MODELS.md +69 -4
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/DECISIONS.md +255 -3
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/RULES.md +127 -2
- ghostrules-0.3.0/README.md → ghostrules-0.4.1/docs/USAGE.md +260 -210
- ghostrules-0.4.1/docs/design/v0.3/CORPUS.md +82 -0
- ghostrules-0.4.1/docs/design/v0.3/PLAN.md +40 -0
- ghostrules-0.4.1/docs/design/v0.3/PLAYGROUND.md +53 -0
- ghostrules-0.4.1/docs/design/v0.3/STUDY.md +56 -0
- ghostrules-0.4.1/docs/design/v0.4/REMEDIATION.md +186 -0
- ghostrules-0.4.1/docs/v0.2.1/FIXES.md +116 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/pyproject.toml +1 -0
- ghostrules-0.4.1/release-notes.md +27 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/scripts/gen_rules_table.py +13 -15
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/__init__.py +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/__init__.py +11 -1
- ghostrules-0.4.1/src/ghostrules/agents/codex.py +122 -0
- ghostrules-0.4.1/src/ghostrules/agents/gemini_cli.py +97 -0
- ghostrules-0.4.1/src/ghostrules/baseline.py +70 -0
- ghostrules-0.4.1/src/ghostrules/changeset.py +35 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/cli.py +231 -7
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/config.py +13 -3
- ghostrules-0.4.1/src/ghostrules/context.py +59 -0
- ghostrules-0.4.1/src/ghostrules/firstparty.py +83 -0
- ghostrules-0.4.1/src/ghostrules/gitref.py +109 -0
- ghostrules-0.4.1/src/ghostrules/ignores.py +36 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/models.py +28 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/playground.py +4 -3
- ghostrules-0.4.1/src/ghostrules/reach.py +274 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/common.py +3 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/json_.py +1 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/sarif.py +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/__init__.py +11 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/agent_settings.py +54 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/context_rules.py +14 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/directive.py +6 -4
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/injection.py +45 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/known_installers.py +19 -3
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/patterns.py +2 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/registry.py +35 -3
- ghostrules-0.4.1/src/ghostrules/rules/scanner_config.py +165 -0
- ghostrules-0.4.1/src/ghostrules/rules/scripts.py +362 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/unicode.py +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/scanner.py +253 -18
- ghostrules-0.4.1/src/ghostrules/schemas/context.schema.json +107 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/targets.py +8 -0
- ghostrules-0.4.1/src/ghostrules/visibility.py +85 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/analyze.py +2 -2
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts.py +2 -2
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/precision_by_rule.csv +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/label_classes.py +13 -10
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/labels.toml +249 -249
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/report.md +93 -32
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/report.py +91 -35
- ghostrules-0.4.1/tests/pr_helpers.py +121 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_agents_others.py +1 -1
- ghostrules-0.4.1/tests/test_baseline.py +125 -0
- ghostrules-0.4.1/tests/test_bench.py +197 -0
- ghostrules-0.4.1/tests/test_bench_heldout.py +32 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_ci_files.py +420 -14
- ghostrules-0.4.1/tests/test_codex_gemini.py +275 -0
- ghostrules-0.4.1/tests/test_context_api.py +59 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_corpus_tools.py +50 -2
- ghostrules-0.4.1/tests/test_crlf.py +26 -0
- ghostrules-0.4.1/tests/test_cross_sentence.py +44 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_directive.py +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_docs.py +131 -51
- ghostrules-0.4.1/tests/test_gr030.py +205 -0
- ghostrules-0.4.1/tests/test_hidden_ignores.py +106 -0
- ghostrules-0.4.1/tests/test_hook_scripts.py +289 -0
- ghostrules-0.4.1/tests/test_lock_base.py +98 -0
- ghostrules-0.4.1/tests/test_noise_fixes.py +174 -0
- ghostrules-0.4.1/tests/test_pr_mode.py +207 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_precision_study.py +1 -1
- ghostrules-0.4.1/tests/test_referenced_files.py +116 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_reporters.py +1 -1
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_rules_injection.py +4 -3
- ghostrules-0.4.1/tests/test_script_own_service.py +211 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_study.py +2 -2
- ghostrules-0.4.1/tests/test_visibility.py +152 -0
- ghostrules-0.3.0/PKG-INFO +0 -603
- ghostrules-0.3.0/action.yml +0 -146
- ghostrules-0.3.0/release-notes.md +0 -50
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.ghostrules.toml +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.gitattributes +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/ISSUE_TEMPLATE/false-positive.yml +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/ISSUE_TEMPLATE/new-rule.yml +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.github/workflows/pages.yml +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/.pre-commit-hooks.yaml +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/LICENSE +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/SECURITY.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/demo/README.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/demo/demo.gif +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/demo/demo.tape +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/demo/imports.gif +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/demo/imports.tape +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/demo/lock.tape +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/design/PLAN.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/design/SPEC.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/design/v0.1.1/GR012-FIX.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/design/v0.2/PLAN.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/design/v0.2/RULES.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/docs/design/v0.2/SPEC.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/clean-repo/.claude/settings.json +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/clean-repo/.mcp.json +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/clean-repo/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/clean-repo/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/clean-repo/README.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/.claude/rules/api.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/.env +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/README.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/docs/setup.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/docs/style.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/shared/team-rules.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/src/api/users.ts +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/import-chain-repo/src/legacy/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/.claude/settings.json +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/.claude/skills/setup/SKILL.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/.cursorrules +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/.github/copilot-instructions.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/.mcp.json +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/examples/poisoned-repo/README.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/scripts/build_site.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/scripts/changelog_section.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/scripts/make_demo.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/scripts/make_fixtures.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/scripts/set_owner.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/_compat.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/base.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/claude_code.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/copilot_cli.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/cursor.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/generic.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/agents/repo.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/clean.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/context_command.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/contextscan.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/globs.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/imports.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/jsonloc.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/lock.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/markdown_utils.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/mcp_config.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/py.typed +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/__init__.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/context.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/context_data.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/lock.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/markdown.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reporters/table.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reveal.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/reveal_data.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/helpers.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/rules/mcp.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/scancontext.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/secrets.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/sensitive.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/src/ghostrules/unicode_utils.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/01-adoption-dark.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/01-adoption-light.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/02-tools-per-repo-dark.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/02-tools-per-repo-light.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/03-claude-agents-dark.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/03-claude-agents-light.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/04-what-agents-read-dark.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/04-what-agents-read-light.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/05-risk-dark.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/05-risk-light.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/06-precision-dark.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/charts/06-precision-light.svg +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/adoption_families.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/claude_agents_relation.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/conditional_rules.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/findings_by_rule.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/headline.json +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/imports.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/instruction_file_lines.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/numbers.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/precision_runs.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/risk_indicators.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/sample.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/skills_concentration.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/data/tools_per_repo.csv +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/study/features.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/conftest.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/corpus_fakes.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/data/sarif-schema-2.1.0.json +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr001_flag_forged/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr001_flag_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr001_tags/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr002_bidi/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr002_rtl_medium/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_bom_midfile/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_bom_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_emoji_zwj_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_indic_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_persian_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_soft_hyphen/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_zw_file_high/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_zw_line_high/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_zw_medium/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr004_vs_bytes/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr004_vs_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_benign_ok/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_comment_imperative/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_comment_injection/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_comment_multiline/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_in_code_block_ok/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_short_ok/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_unterminated/CLAUDE.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr006_ok/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr006_url/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr006_word/AGENTS.md +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/secret_samples.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_agents_claude.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_agents_repo.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_cli.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_config.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_context_command.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_core_without_cli_deps.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_corpus_packaging.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_demo.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_globs.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_imports.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_lock.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_markdown_utils.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_mcp_config.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_models.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_patterns.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_playground.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_precision_v021.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_precision_v030.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_rules_config.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_rules_unicode.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_scan_imports.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_scanner.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_secret_masking.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_secrets.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_symlink_alias.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_targets.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/test_unicode_utils.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/tests/web/test_playground_smoke.py +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/web/app.js +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/web/index.html +0 -0
- {ghostrules-0.3.0 → ghostrules-0.4.1}/web/style.css +0 -0
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
"claude-code": [
|
|
4
4
|
{
|
|
5
5
|
"conditions": [],
|
|
6
|
-
"lines": "2a3d3f1fb26381e9 e3b0c44298fc1c14 ddabff43c0a4efe5 6aa552cc1598dcb7 be186e6b970ab643 fb78540ec32b52f9 e3b0c44298fc1c14
|
|
6
|
+
"lines": "2a3d3f1fb26381e9 e3b0c44298fc1c14 ddabff43c0a4efe5 6aa552cc1598dcb7 be186e6b970ab643 fb78540ec32b52f9 e3b0c44298fc1c14 662c42577f74cece e3b0c44298fc1c14 85b7ae55df076ab0 2f6995b0ecd75c8d e3b0c44298fc1c14 5f2235a1b010f82c 56a4406ad70b7f0a ba2cd86d7e76eff5 a7b723b74ee03fc8 e3b0c44298fc1c14 782729a560418c17 36a8e590ff4c085c e3b0c44298fc1c14 9f4e3cc48918efc0 efeeec23855e4659 2f23b1dcff8841e8 c3abd357eb574eb6 76881ea91e0040bd e3b0c44298fc1c14 36cd3f2bb454f575 ad332335cfaa348c a58865feac87f52b 052081156966d443 5654f9fffd4a2a7a bd0710fd0315ecbb e4d8cec4158bb770 f1b901847390b0ed e3b0c44298fc1c14 966a8946115b377a 5b59b2f749ffabac 9bf96d3c4c385073 43d8bbb16372109c 5226bbb3f5f9e13d add6d744d3ac0c02 3dafca094d79548b 21a3cd5236dfd642 79e12b3b50117933 9cb81024d1184666 f1e7b346d65f35c5 e3b0c44298fc1c14 60ab6be4219694a0 3a1c5dcc5281208a e256f3fb8239bf8c 10d8918d097bebb5 ff85c33a894fab39 10f274c7d25257d2 6216cf539fa87bd8 e3b0c44298fc1c14 7136000e494604c0 aa8d4cf82644ac63 f75f6ee8f0d942eb 1f59b655e6071114 da29957f3e8be475",
|
|
7
7
|
"load": "always",
|
|
8
8
|
"path": "CLAUDE.md",
|
|
9
|
-
"raw_sha256": "
|
|
10
|
-
"seen_sha256": "
|
|
9
|
+
"raw_sha256": "d989296d3afb40ba35cdf955a9dcc549df351fa4a5b0ee4585475e94e86e774c",
|
|
10
|
+
"seen_sha256": "d989296d3afb40ba35cdf955a9dcc549df351fa4a5b0ee4585475e94e86e774c",
|
|
11
11
|
"via": []
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
"copilot-cli": [
|
|
15
15
|
{
|
|
16
16
|
"conditions": [],
|
|
17
|
-
"lines": "2a3d3f1fb26381e9 e3b0c44298fc1c14 ddabff43c0a4efe5 6aa552cc1598dcb7 be186e6b970ab643 fb78540ec32b52f9 e3b0c44298fc1c14
|
|
17
|
+
"lines": "2a3d3f1fb26381e9 e3b0c44298fc1c14 ddabff43c0a4efe5 6aa552cc1598dcb7 be186e6b970ab643 fb78540ec32b52f9 e3b0c44298fc1c14 662c42577f74cece e3b0c44298fc1c14 85b7ae55df076ab0 2f6995b0ecd75c8d e3b0c44298fc1c14 5f2235a1b010f82c 56a4406ad70b7f0a ba2cd86d7e76eff5 a7b723b74ee03fc8 e3b0c44298fc1c14 782729a560418c17 36a8e590ff4c085c e3b0c44298fc1c14 9f4e3cc48918efc0 efeeec23855e4659 2f23b1dcff8841e8 c3abd357eb574eb6 76881ea91e0040bd e3b0c44298fc1c14 36cd3f2bb454f575 ad332335cfaa348c a58865feac87f52b 052081156966d443 5654f9fffd4a2a7a bd0710fd0315ecbb e4d8cec4158bb770 f1b901847390b0ed e3b0c44298fc1c14 966a8946115b377a 5b59b2f749ffabac 9bf96d3c4c385073 43d8bbb16372109c 5226bbb3f5f9e13d add6d744d3ac0c02 3dafca094d79548b 21a3cd5236dfd642 79e12b3b50117933 9cb81024d1184666 f1e7b346d65f35c5 e3b0c44298fc1c14 60ab6be4219694a0 3a1c5dcc5281208a e256f3fb8239bf8c 10d8918d097bebb5 ff85c33a894fab39 10f274c7d25257d2 6216cf539fa87bd8 e3b0c44298fc1c14 7136000e494604c0 aa8d4cf82644ac63 f75f6ee8f0d942eb 1f59b655e6071114 da29957f3e8be475",
|
|
18
18
|
"load": "always",
|
|
19
19
|
"path": "CLAUDE.md",
|
|
20
|
-
"raw_sha256": "
|
|
21
|
-
"seen_sha256": "
|
|
20
|
+
"raw_sha256": "d989296d3afb40ba35cdf955a9dcc549df351fa4a5b0ee4585475e94e86e774c",
|
|
21
|
+
"seen_sha256": "d989296d3afb40ba35cdf955a9dcc549df351fa4a5b0ee4585475e94e86e774c",
|
|
22
22
|
"via": []
|
|
23
23
|
}
|
|
24
24
|
],
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"generic": []
|
|
27
27
|
},
|
|
28
28
|
"configs": [],
|
|
29
|
-
"generator": "ghostrules 0.
|
|
29
|
+
"generator": "ghostrules 0.4.1",
|
|
30
30
|
"lock_version": 1
|
|
31
31
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Keeps the commit-pinned actions in .github/workflows and action.yml up to date: each update is a
|
|
2
|
+
# pull request that changes the SHA and its version comment.
|
|
3
|
+
version: 2
|
|
4
|
+
updates:
|
|
5
|
+
- package-ecosystem: github-actions
|
|
6
|
+
directory: /
|
|
7
|
+
schedule:
|
|
8
|
+
interval: weekly
|
|
9
|
+
commit-message:
|
|
10
|
+
prefix: "chore"
|
|
@@ -14,8 +14,8 @@ jobs:
|
|
|
14
14
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
15
15
|
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@v4
|
|
18
|
-
- uses: actions/setup-python@v5
|
|
17
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
18
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
19
19
|
with:
|
|
20
20
|
python-version: ${{ matrix.python-version }}
|
|
21
21
|
- name: Install
|
|
@@ -21,8 +21,8 @@ jobs:
|
|
|
21
21
|
env:
|
|
22
22
|
GH_TOKEN: ${{ github.token }}
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@v4
|
|
25
|
-
- uses: actions/setup-python@v5
|
|
24
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
25
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
26
26
|
with:
|
|
27
27
|
python-version: "3.12"
|
|
28
28
|
- name: Install ghostrules from this checkout
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
run: python corpus/badge.py --out corpus-public
|
|
37
37
|
- name: Upload results
|
|
38
38
|
if: always()
|
|
39
|
-
uses: actions/upload-artifact@v4
|
|
39
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
40
40
|
with:
|
|
41
41
|
name: corpus-results
|
|
42
42
|
path: |
|
|
@@ -46,27 +46,28 @@ jobs:
|
|
|
46
46
|
if-no-files-found: ignore
|
|
47
47
|
|
|
48
48
|
publish:
|
|
49
|
-
# Only the default branch publishes; the badge reads corpus/badge.json from
|
|
49
|
+
# Only the default branch publishes; the badge reads corpus/badge.json from the orphan
|
|
50
|
+
# branch "badges" (gh-pages is not served: Pages is deployed from Actions by pages.yml).
|
|
50
51
|
needs: corpus
|
|
51
52
|
if: always() && needs.corpus.result != 'skipped' && github.ref == 'refs/heads/main'
|
|
52
53
|
runs-on: ubuntu-latest
|
|
53
54
|
permissions:
|
|
54
55
|
contents: write
|
|
55
56
|
steps:
|
|
56
|
-
- uses: actions/download-artifact@v4
|
|
57
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
57
58
|
with:
|
|
58
59
|
name: corpus-results
|
|
59
60
|
path: artifact
|
|
60
|
-
- name: Publish to
|
|
61
|
+
- name: Publish to the badges branch
|
|
61
62
|
env:
|
|
62
63
|
GH_TOKEN: ${{ github.token }}
|
|
63
64
|
REPO: ${{ github.repository }}
|
|
64
65
|
run: |
|
|
65
66
|
set -euo pipefail
|
|
66
67
|
test -f artifact/corpus-public/badge.json
|
|
67
|
-
git clone --quiet --depth 1 --branch
|
|
68
|
+
git clone --quiet --depth 1 --branch badges \
|
|
68
69
|
"https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git" site \
|
|
69
|
-
|| { mkdir site && git -C site init --quiet -b
|
|
70
|
+
|| { mkdir site && git -C site init --quiet -b badges \
|
|
70
71
|
&& git -C site remote add origin \
|
|
71
72
|
"https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git"; }
|
|
72
73
|
mkdir -p site/corpus
|
|
@@ -76,4 +77,4 @@ jobs:
|
|
|
76
77
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
77
78
|
git add corpus
|
|
78
79
|
git diff --cached --quiet || git commit --quiet -m "corpus: weekly summary"
|
|
79
|
-
git push origin
|
|
80
|
+
git push origin badges
|
|
@@ -14,9 +14,9 @@ jobs:
|
|
|
14
14
|
build:
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
18
18
|
|
|
19
|
-
- uses: actions/setup-python@v5
|
|
19
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
20
20
|
with:
|
|
21
21
|
python-version: "3.12"
|
|
22
22
|
|
|
@@ -41,7 +41,7 @@ jobs:
|
|
|
41
41
|
python -m pip install --disable-pip-version-check --quiet build
|
|
42
42
|
python -m build
|
|
43
43
|
|
|
44
|
-
- uses: actions/upload-artifact@v4
|
|
44
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
45
45
|
with:
|
|
46
46
|
name: dist
|
|
47
47
|
path: |
|
|
@@ -55,10 +55,10 @@ jobs:
|
|
|
55
55
|
permissions:
|
|
56
56
|
id-token: write # Trusted Publishing
|
|
57
57
|
steps:
|
|
58
|
-
- uses: actions/download-artifact@v4
|
|
58
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
59
59
|
with:
|
|
60
60
|
name: dist
|
|
61
|
-
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
61
|
+
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # release/v1
|
|
62
62
|
|
|
63
63
|
github-release:
|
|
64
64
|
needs: publish-pypi
|
|
@@ -66,7 +66,7 @@ jobs:
|
|
|
66
66
|
permissions:
|
|
67
67
|
contents: write
|
|
68
68
|
steps:
|
|
69
|
-
- uses: actions/download-artifact@v4
|
|
69
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
70
70
|
with:
|
|
71
71
|
name: dist
|
|
72
72
|
- name: Create the GitHub release
|
|
@@ -75,3 +75,24 @@ jobs:
|
|
|
75
75
|
TAG: ${{ github.ref_name }}
|
|
76
76
|
REPO: ${{ github.repository }}
|
|
77
77
|
run: gh release create "$TAG" dist/* --repo "$REPO" --title "$TAG" --notes-file release-notes.md
|
|
78
|
+
|
|
79
|
+
move-major-tag:
|
|
80
|
+
# Users pin BardLaudian/ghostrules@v0 (or an exact tag). Only a stable x.y.z release moves it.
|
|
81
|
+
needs: github-release
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
permissions:
|
|
84
|
+
contents: write
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
87
|
+
- name: Move the v0 tag to this release
|
|
88
|
+
env:
|
|
89
|
+
TAG: ${{ github.ref_name }}
|
|
90
|
+
run: |
|
|
91
|
+
set -euo pipefail
|
|
92
|
+
if ! [[ "$TAG" =~ ^v0\.[0-9]+\.[0-9]+$ ]]; then
|
|
93
|
+
echo "$TAG is not a stable v0.x.y release: the v0 tag stays where it is"
|
|
94
|
+
exit 0
|
|
95
|
+
fi
|
|
96
|
+
git tag -f v0 "$TAG"
|
|
97
|
+
git push --force origin v0
|
|
98
|
+
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
action:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
17
17
|
|
|
18
18
|
- name: Poisoned demo with fail-on none (must pass, must report findings)
|
|
19
19
|
id: poisoned
|
|
@@ -75,7 +75,7 @@ jobs:
|
|
|
75
75
|
import-chain:
|
|
76
76
|
runs-on: ubuntu-latest
|
|
77
77
|
steps:
|
|
78
|
-
- uses: actions/checkout@v4
|
|
78
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
79
79
|
|
|
80
80
|
- name: Import-chain demo, following imports (default)
|
|
81
81
|
uses: ./
|
|
@@ -125,6 +125,9 @@ jobs:
|
|
|
125
125
|
uses: ./
|
|
126
126
|
with:
|
|
127
127
|
path: ${{ runner.temp }}/acme/base
|
|
128
|
+
# not a git repository: scanned with its own settings
|
|
129
|
+
trust-repo-config: "true"
|
|
130
|
+
inline-suppressions: all
|
|
128
131
|
fail-on: none
|
|
129
132
|
lock-check: "true"
|
|
130
133
|
upload-sarif: "false"
|
|
@@ -140,6 +143,9 @@ jobs:
|
|
|
140
143
|
continue-on-error: true
|
|
141
144
|
with:
|
|
142
145
|
path: ${{ runner.temp }}/acme/pr
|
|
146
|
+
# not a git repository: scanned with its own settings
|
|
147
|
+
trust-repo-config: "true"
|
|
148
|
+
inline-suppressions: all
|
|
143
149
|
fail-on: none
|
|
144
150
|
lock-check: "true"
|
|
145
151
|
upload-sarif: "false"
|
|
@@ -5,6 +5,147 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.4.1] - 2026-10-29
|
|
9
|
+
|
|
10
|
+
Follow-up to 0.4.0: fewer false positives on scripts, fewer misses on visible attacks, and a
|
|
11
|
+
benchmark headline that no longer counts cases the author wrote. No breaking changes.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **Visible prose that reads as an attack stays `high`.** Inside one paragraph, exfiltration plus
|
|
16
|
+
concealment (GR013 + GR011), override plus exfiltration (GR010 + GR013) and a sensitive file plus
|
|
17
|
+
a URL outside `allow_domains` (GR014 + URL) keep the rule's severity even though the text is
|
|
18
|
+
visible. Examples, quotations, code blocks and descriptions stay `low`. On the held-out set no
|
|
19
|
+
repository starts to fail the check, but two GR014 findings already labelled false positive now
|
|
20
|
+
report `high`.
|
|
21
|
+
- **GR110 no longer reports a token sent to the service it belongs to**: `GITHUB_*`/`GH_*` to
|
|
22
|
+
`api.github.com`, `github.com` or `uploads.github.com`; `AZURE_*`, `ADO_*` and `SYSTEM_ACCESSTOKEN`
|
|
23
|
+
to `dev.azure.com` or `*.visualstudio.com`; `<X>_API_KEY` to a host that contains `x`. It still
|
|
24
|
+
reports any other host, a collector domain, the whole environment, a credentials file, a variable
|
|
25
|
+
with no known service and a destination it cannot read. The held-out GR110 finding is still
|
|
26
|
+
reported: its shape is not covered.
|
|
27
|
+
- **The benchmark headline counts only the independent cases.** Recall is split by origin (`author`,
|
|
28
|
+
`review`, `public`; `ours` is now `author`) and the README states the independent figure first,
|
|
29
|
+
with its sample size. Every number in `README.md` and `bench/README.md` is generated from the data
|
|
30
|
+
(`python bench/run.py report --write`) and a test fails when one is stale.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **Inline hook code is read as a script**: the code in `python3 -c`, `node -e`, `bash -c` and `sh -c`
|
|
35
|
+
(and the other interpreters in the hook table) is checked by GR110–GR113. The finding points at the
|
|
36
|
+
command in the settings file.
|
|
37
|
+
|
|
38
|
+
## [0.4.0] - 2026-10-29
|
|
39
|
+
|
|
40
|
+
Remediation release, from an independent review of 0.3.0 ([plan](docs/design/v0.4/REMEDIATION.md)).
|
|
41
|
+
The scanned change can no longer switch the scanner off, the lock cannot be re-blessed by a pull
|
|
42
|
+
request, visible prose no longer drowns the check, claims are corrected and measured, and the scanner
|
|
43
|
+
reads more of what agents run. On a new benchmark (101 attack cases and a 100-repository held-out
|
|
44
|
+
set, `bench/`): 87 of 101 attacks reported, 50 fail the default check; precision for a user on the
|
|
45
|
+
held-out set is 19% (4 of 21 findings, labels provisional). Both numbers come with their misses.
|
|
46
|
+
|
|
47
|
+
**Upgrading: breaking changes.** (1) On pull requests the Action reads the config, the ignore comments,
|
|
48
|
+
the baseline and the lock from the base commit; a path outside a git repository needs
|
|
49
|
+
`trust-repo-config: "true"` and `inline-suppressions: all`. (2) Plain visible prose is `low` or `info`
|
|
50
|
+
by default, so a visible `curl | sh` no longer fails `--fail-on high`; `--strict` (config
|
|
51
|
+
`profile = "strict"`, Action `profile: strict`) restores the 0.3 severities. (3) The label `wontfix` is
|
|
52
|
+
now `benign-context` in the corpus tools. (4) The Action installs from a hash-pinned requirements file.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- **Scripts that hooks and skills run** (W5.1, W5.2): hook commands of Claude Code, Gemini, Cursor and
|
|
57
|
+
Codex and scripts named in `SKILL.md` are scanned as code, with the chain that reaches them: GR110
|
|
58
|
+
(sends the environment or a secret), GR111 (reads credential files), GR112 (downloads and runs code),
|
|
59
|
+
GR113 (hides what it runs).
|
|
60
|
+
- **The context engine as a library** (W8): `ghostrules.context.resolve()` and `schema()`, a JSON
|
|
61
|
+
schema for the result validated in the tests, and a README section on alternatives.
|
|
62
|
+
- **Settings a pull request cannot rewrite** (W2 of docs/design/v0.4/REMEDIATION.md): `scan
|
|
63
|
+
--config-from-ref REF`, `--trusted-config FILE`, `--no-inline-suppressions` and
|
|
64
|
+
`--suppressions-from-ref REF`. They close two ways a change could silence the scanner for its own
|
|
65
|
+
payload: a `ghostrules-ignore` comment on the malicious line and `exclude` in `.ghostrules.toml`.
|
|
66
|
+
- **Codex and Gemini CLI** (W5.3): `context` models for both (`--agent codex`, `--agent gemini-cli`;
|
|
67
|
+
opt-in through `agents`, so existing lockfiles do not change); `AGENTS.override.md`,
|
|
68
|
+
`.codex/config.toml` and `.codex/hooks.json` are scanned; GR107 reports Codex
|
|
69
|
+
`sandbox_mode = "danger-full-access"` and `approval_policy = "never"` and Gemini's bare
|
|
70
|
+
`run_shell_command` allow and `enablePermanentToolApproval`.
|
|
71
|
+
- **Files that prose points to** (W5.4): `Read X and follow every step` and `Run scripts/x.sh` make the
|
|
72
|
+
file part of the scan, with the chain and the tag `reached-via-reference` (GR026, a tag, not a
|
|
73
|
+
detector). The `context` tree does not show a `referenced` state yet.
|
|
74
|
+
- **Instructions split across two sentences** (W5.5): GR013 reports a secret in one sentence and a send
|
|
75
|
+
to a URL in the next, with a new `confidence: medium` field on the finding.
|
|
76
|
+
- **A benchmark you can check** (W6): `bench/` has 81 defanged attack cases written from public
|
|
77
|
+
sources (recall per technique and per rule, misses listed, four cases from the independent review as
|
|
78
|
+
known misses) and the 100-repository held-out set run once, with precision for a user per rule. The
|
|
79
|
+
README now shows these numbers instead of the corpus claims. Labels `wontfix` are now
|
|
80
|
+
`benign-context`.
|
|
81
|
+
- **Supply-chain hygiene** (W7): every `uses:` in the Action and the workflows is pinned to a full
|
|
82
|
+
commit SHA (Dependabot keeps them current); the Action installs its dependencies and the build
|
|
83
|
+
backend from `action-requirements.txt`, hash-pinned, with `--require-hashes --no-deps
|
|
84
|
+
--no-build-isolation`; the release workflow moves a `v0` tag to each stable `v0.x.y` release.
|
|
85
|
+
- **Fewer false positives from the independent review** (W4): installers of the project's own
|
|
86
|
+
GitHub organisation and homepage are known installers (read from the base ref when the scan has
|
|
87
|
+
one); Azure CLI, Flox, golangci-lint, Foundry, Flux and Linkerd are known installers; "do not tell
|
|
88
|
+
the user they …" is not concealment.
|
|
89
|
+
- **`ghostrules baseline` and `scan --baseline / --no-baseline / --baseline-from-ref`**: record
|
|
90
|
+
today's findings as fingerprints (no line numbers, no snippets) and fail only on new ones. Hidden
|
|
91
|
+
text is never baselined. The Action reads the baseline at the base commit on pull requests, and
|
|
92
|
+
editing it is reported by GR030.
|
|
93
|
+
- **Breaking: severity by visibility.** Plain visible prose in an agent file is now `low` (an
|
|
94
|
+
instruction) or `info` (a description or quotation) for GR010 to GR014 and GR016; text in an HTML
|
|
95
|
+
comment, decoded hidden text, files reached only through an import, GR015 and every configuration
|
|
96
|
+
rule keep their severity. A visible `curl | sh` or "send `.env` to a URL" no longer fails the
|
|
97
|
+
default check (`--fail-on high`). `--strict`, `profile = "strict"` in the config and the Action's
|
|
98
|
+
`profile: strict` restore the 0.3 behaviour.
|
|
99
|
+
- **`lock --check --base REF`** (W3): compares with the lock and the config at `REF`, so a pull
|
|
100
|
+
request that edits what the agents read and regenerates `.ghostrules.lock` still fails. Exit 2 when
|
|
101
|
+
the ref or its lock does not exist. The idea is cairnlock's `verify --base`.
|
|
102
|
+
- **Breaking: the Action's lock check uses the lock at the base on pull requests**, and a new
|
|
103
|
+
`approve-label` input lets a maintainer approve a legitimate change by applying a label; only the
|
|
104
|
+
event that applies it approves (new commits void it), and the summary names who and when. Output
|
|
105
|
+
`lock-approved`. `trust-repo-config: "true"` restores the working-tree lock.
|
|
106
|
+
- **Inline ignores no longer hide text a reviewer cannot see** (W2): a finding inside an HTML
|
|
107
|
+
comment is never suppressed, and no ignore counts inside a file that an agent reaches only
|
|
108
|
+
through an import (the report says how many were not honoured). Decoded hidden text, GR001 and
|
|
109
|
+
GR002 were already never suppressed. An ignore on a visible line works as before.
|
|
110
|
+
- **GR030, scanner configuration changed** (high): with a base ref, the change adds or edits
|
|
111
|
+
`.ghostrules.toml`, `[tool.ghostrules]`, the lockfile or a `ghostrules-ignore` comment. Reported
|
|
112
|
+
even when legitimate, never suppressible inline, and the change cannot disable it. A new `change`
|
|
113
|
+
scope of rules compares the scanned tree with the base.
|
|
114
|
+
- **Breaking: the Action reads settings from the base commit on pull requests.** On
|
|
115
|
+
`pull_request` and `pull_request_target` events it passes `--config-from-ref` and
|
|
116
|
+
`--suppressions-from-ref` with the base SHA, fetching that commit if the checkout is shallow, and
|
|
117
|
+
fails with exit code 2 when it cannot read it. New inputs: `config`, `trust-repo-config` (default
|
|
118
|
+
`false`) and `inline-suppressions` (`base`, `off` or `all`; default `base`). Workflows that scan a
|
|
119
|
+
path outside a git repository on a pull request need `trust-repo-config: "true"` and
|
|
120
|
+
`inline-suppressions: all`. Other events behave as before.
|
|
121
|
+
|
|
122
|
+
### Changed
|
|
123
|
+
|
|
124
|
+
- **Corrected claims** (W1 of [docs/design/v0.4/REMEDIATION.md](docs/design/v0.4/REMEDIATION.md)).
|
|
125
|
+
The 0.2 statement that no other tool follows `@path` imports was wrong (cfgaudit does, since
|
|
126
|
+
2026-10-24): the prior-art notes are corrected and dated, and ghostrules no longer claims to be
|
|
127
|
+
the first or the only one. The README says "scheduled weekly; last run" instead of "every week".
|
|
128
|
+
The study report is now "A snapshot (2026-10-28)", puts hygiene statistics apart from security
|
|
129
|
+
indicators, shows a numerator and denominator next to each percentage, explains the MCP
|
|
130
|
+
denominator (a lower bound, so the share is an upper bound), counts benign-context matches as noise
|
|
131
|
+
in a per-rule precision table, and has a "Threats to validity" section. The 0.3.0 notes below are
|
|
132
|
+
left as published.
|
|
133
|
+
- README restructured as a landing page; option tables, configuration, supported files, FAQ and limits
|
|
134
|
+
moved to `docs/USAGE.md`, and the rules table to `docs/RULES.md`.
|
|
135
|
+
- SARIF `helpUri` points to `docs/RULES.md#grNNN` (the rules table moved there).
|
|
136
|
+
|
|
137
|
+
### Known limits
|
|
138
|
+
|
|
139
|
+
- Paraphrases, languages other than English and Spanish (GR013 has no Spanish patterns), a few read
|
|
140
|
+
verbs (`Open ~/.npmrc`), `exec(urlopen())`, endpoint overrides in settings and the descriptions an MCP
|
|
141
|
+
server serves are not caught: 14 of the 101 attack cases (`bench/RESULTS.md`).
|
|
142
|
+
- Only 4 of the review's 18 evasion cases are in the attack set; the rest were not available.
|
|
143
|
+
- The held-out labels are provisional (given by the assistant that ran the benchmark, not reviewed by a
|
|
144
|
+
person) and 21 findings are too few for a per-rule precision estimate.
|
|
145
|
+
- The `context` tree does not show files reached by a prose reference, and the lock does not record
|
|
146
|
+
them; Codex and Gemini CLI models are opt-in.
|
|
147
|
+
- The MCP share in the study is an upper bound (its denominator is a lower bound).
|
|
148
|
+
|
|
8
149
|
## [0.3.0] - 2026-10-28
|
|
9
150
|
|
|
10
151
|
Measurement release. ghostrules now runs against real repositories every week, a browser playground
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
machine) for **hidden instructions and risky configuration in the files AI coding agents read and
|
|
6
6
|
obey**: `AGENTS.md`, `CLAUDE.md`, `.cursorrules`, Copilot instructions, skills, MCP configs, etc.
|
|
7
7
|
|
|
8
|
-
Tagline: *"
|
|
8
|
+
Tagline: *"Know exactly what your AI agent reads — and approve every change to it."*
|
|
9
9
|
|
|
10
10
|
It is a **defensive** security tool. It only reads files and reports findings. It never executes
|
|
11
11
|
anything it finds, never makes network requests, and never phones home.
|
|
@@ -55,7 +55,7 @@ pull request.
|
|
|
55
55
|
|
|
56
56
|
3. **Document it.** Describe the rule in `docs/RULES.md` (ID, severity, logic, true and false
|
|
57
57
|
positive examples), note any judgement call in `docs/DECISIONS.md`, add a line to
|
|
58
|
-
`CHANGELOG.md`, then refresh the
|
|
58
|
+
`CHANGELOG.md`, then refresh the rule index in `docs/RULES.md`:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
python scripts/make_demo.py
|
|
@@ -88,6 +88,20 @@ model is one module, one section of `docs/AGENT_MODELS.md` and its tests:
|
|
|
88
88
|
in `ContextGraph.notes`, and add a line to `CHANGELOG.md` and a decision to
|
|
89
89
|
`docs/DECISIONS.md` for every judgement call.
|
|
90
90
|
|
|
91
|
+
## Supply chain
|
|
92
|
+
|
|
93
|
+
- Every `uses:` in `action.yml` and `.github/workflows/` is pinned to a full commit SHA with the
|
|
94
|
+
version in a comment (`actions/checkout@<sha> # v4`). Dependabot opens the update pull requests;
|
|
95
|
+
a test fails on a tag or branch reference.
|
|
96
|
+
- The Action installs from `action-requirements.txt`, which has a hash for every package, the build
|
|
97
|
+
backend included. Regenerate it when `pyproject.toml` dependencies change:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install pip-tools
|
|
101
|
+
pip-compile --generate-hashes --strip-extras --allow-unsafe \
|
|
102
|
+
-o action-requirements.txt action-requirements.in
|
|
103
|
+
```
|
|
104
|
+
|
|
91
105
|
## Rules of the repository
|
|
92
106
|
|
|
93
107
|
- **Defanged only.** Fixtures, demos and tests use `attacker.example`, `exfil.example`,
|