ghostrules 0.4.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.4.0 → ghostrules-0.4.1}/.ghostrules.lock +1 -1
- {ghostrules-0.4.0 → ghostrules-0.4.1}/CHANGELOG.md +30 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/PKG-INFO +19 -7
- {ghostrules-0.4.0 → ghostrules-0.4.1}/README.md +18 -6
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/README.md +3 -3
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/RESULTS.md +15 -9
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/attacks/context-and-config.toml +4 -4
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/attacks/prose.toml +13 -13
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/attacks/unicode.toml +5 -5
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/attacks/w5.toml +6 -6
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/heldout-results.json +5 -5
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/lib.py +18 -1
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/run.py +102 -20
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/USAGE.md +3 -3
- ghostrules-0.4.1/release-notes.md +27 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/__init__.py +1 -1
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reach.py +41 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/scripts.py +84 -22
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/scanner.py +48 -4
- ghostrules-0.4.1/src/ghostrules/visibility.py +85 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_bench.py +29 -5
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_ci_files.py +3 -1
- ghostrules-0.4.1/tests/test_script_own_service.py +211 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_visibility.py +62 -6
- ghostrules-0.4.0/release-notes.md +0 -108
- ghostrules-0.4.0/src/ghostrules/visibility.py +0 -40
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.ghostrules.toml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.gitattributes +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/ISSUE_TEMPLATE/false-positive.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/ISSUE_TEMPLATE/new-rule.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/dependabot.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/workflows/ci.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/workflows/corpus.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/workflows/pages.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/workflows/release.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.github/workflows/self-test.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.gitignore +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/.pre-commit-hooks.yaml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/CONTRIBUTING.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/LICENSE +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/SECURITY.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/action-requirements.in +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/action-requirements.txt +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/action.yml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/assets/banner-dark.png +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/assets/banner-light.png +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/assets/logo.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/assets/social-preview.png +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/.cache-run.log +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/attacks/sources.toml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/heldout-labels.toml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/bench/heldout.toml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/demo/README.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/demo/demo.gif +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/demo/demo.tape +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/demo/imports.gif +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/demo/imports.tape +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/demo/lock.tape +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/AGENT_MODELS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/DECISIONS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/RULES.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/PLAN.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/SPEC.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.1.1/GR012-FIX.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.2/PLAN.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.2/RULES.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.2/SPEC.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.3/CORPUS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.3/PLAN.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.3/PLAYGROUND.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.3/STUDY.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/design/v0.4/REMEDIATION.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/docs/v0.2.1/FIXES.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/clean-repo/.claude/settings.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/clean-repo/.mcp.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/clean-repo/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/clean-repo/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/clean-repo/README.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/.claude/rules/api.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/.env +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/README.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/docs/setup.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/docs/style.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/shared/team-rules.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/src/api/users.ts +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/import-chain-repo/src/legacy/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/.claude/settings.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/.claude/skills/setup/SKILL.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/.cursorrules +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/.github/copilot-instructions.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/.mcp.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/examples/poisoned-repo/README.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/pyproject.toml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/scripts/build_site.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/scripts/changelog_section.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/scripts/gen_rules_table.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/scripts/make_demo.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/scripts/make_fixtures.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/scripts/set_owner.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/_compat.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/__init__.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/base.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/claude_code.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/codex.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/copilot_cli.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/cursor.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/gemini_cli.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/generic.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/agents/repo.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/baseline.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/changeset.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/clean.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/cli.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/config.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/context.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/context_command.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/contextscan.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/firstparty.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/gitref.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/globs.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/ignores.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/imports.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/jsonloc.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/lock.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/markdown_utils.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/mcp_config.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/models.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/playground.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/py.typed +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/__init__.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/common.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/context.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/context_data.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/json_.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/lock.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/markdown.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/sarif.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reporters/table.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reveal.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/reveal_data.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/__init__.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/agent_settings.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/context_rules.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/directive.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/helpers.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/injection.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/known_installers.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/mcp.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/patterns.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/registry.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/scanner_config.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/rules/unicode.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/scancontext.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/schemas/context.schema.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/secrets.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/sensitive.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/targets.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/src/ghostrules/unicode_utils.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/analyze.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/01-adoption-dark.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/01-adoption-light.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/02-tools-per-repo-dark.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/02-tools-per-repo-light.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/03-claude-agents-dark.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/03-claude-agents-light.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/04-what-agents-read-dark.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/04-what-agents-read-light.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/05-risk-dark.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/05-risk-light.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/06-precision-dark.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts/06-precision-light.svg +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/charts.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/adoption_families.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/claude_agents_relation.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/conditional_rules.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/findings_by_rule.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/headline.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/imports.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/instruction_file_lines.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/numbers.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/precision_by_rule.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/precision_runs.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/risk_indicators.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/sample.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/skills_concentration.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/data/tools_per_repo.csv +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/features.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/label_classes.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/labels.toml +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/report.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/study/report.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/conftest.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/corpus_fakes.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/data/sarif-schema-2.1.0.json +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr001_flag_forged/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr001_flag_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr001_tags/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr002_bidi/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr002_rtl_medium/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_bom_midfile/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_bom_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_emoji_zwj_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_indic_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_persian_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_soft_hyphen/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_zw_file_high/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_zw_line_high/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr003_zw_medium/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr004_vs_bytes/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr004_vs_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_benign_ok/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_comment_imperative/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_comment_injection/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_comment_multiline/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_in_code_block_ok/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_short_ok/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr005_unterminated/CLAUDE.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr006_ok/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr006_url/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/fixtures/unicode/gr006_word/AGENTS.md +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/pr_helpers.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/secret_samples.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_agents_claude.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_agents_others.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_agents_repo.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_baseline.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_bench_heldout.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_cli.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_codex_gemini.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_config.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_context_api.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_context_command.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_core_without_cli_deps.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_corpus_packaging.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_corpus_tools.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_crlf.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_cross_sentence.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_demo.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_directive.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_docs.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_globs.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_gr030.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_hidden_ignores.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_hook_scripts.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_imports.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_lock.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_lock_base.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_markdown_utils.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_mcp_config.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_models.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_noise_fixes.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_patterns.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_playground.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_pr_mode.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_precision_study.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_precision_v021.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_precision_v030.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_referenced_files.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_reporters.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_rules_config.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_rules_injection.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_rules_unicode.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_scan_imports.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_scanner.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_secret_masking.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_secrets.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_study.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_symlink_alias.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_targets.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/test_unicode_utils.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/tests/web/test_playground_smoke.py +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/web/app.js +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/web/index.html +0 -0
- {ghostrules-0.4.0 → ghostrules-0.4.1}/web/style.css +0 -0
|
@@ -5,6 +5,36 @@ 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
|
+
|
|
8
38
|
## [0.4.0] - 2026-10-29
|
|
9
39
|
|
|
10
40
|
Remediation release, from an independent review of 0.3.0 ([plan](docs/design/v0.4/REMEDIATION.md)).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ghostrules
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Find the instructions your AI agent sees but you don't.
|
|
5
5
|
Project-URL: Homepage, https://github.com/BardLaudian/ghostrules
|
|
6
6
|
Project-URL: Documentation, https://github.com/BardLaudian/ghostrules#readme
|
|
@@ -149,7 +149,7 @@ ghostrules lock --check .ghostrules.lock
|
|
|
149
149
|
|
|
150
150
|
```yaml
|
|
151
151
|
- uses: actions/checkout@v4
|
|
152
|
-
- uses: BardLaudian/ghostrules@v0.4.
|
|
152
|
+
- uses: BardLaudian/ghostrules@v0.4.1
|
|
153
153
|
with:
|
|
154
154
|
fail-on: high # critical | high | medium | low | info | none
|
|
155
155
|
lock-check: "true" # fail if what the agents read changed
|
|
@@ -158,7 +158,7 @@ ghostrules lock --check .ghostrules.lock
|
|
|
158
158
|
```yaml
|
|
159
159
|
repos:
|
|
160
160
|
- repo: https://github.com/BardLaudian/ghostrules
|
|
161
|
-
rev: v0.4.
|
|
161
|
+
rev: v0.4.1
|
|
162
162
|
hooks:
|
|
163
163
|
- id: ghostrules
|
|
164
164
|
```
|
|
@@ -171,8 +171,16 @@ A scanner that cries wolf gets switched off, and one that misses gets trusted by
|
|
|
171
171
|
measured and the misses are listed ([bench/](https://github.com/BardLaudian/ghostrules/blob/main/bench/README.md),
|
|
172
172
|
[full results](https://github.com/BardLaudian/ghostrules/blob/main/bench/RESULTS.md)). Nothing below was tuned on the data it is measured on.
|
|
173
173
|
|
|
174
|
-
<!-- bench:start -->
|
|
175
|
-
**Recall
|
|
174
|
+
<!-- bench:recall:start -->
|
|
175
|
+
**Recall on independent cases: 1 of 4 reported (25%); 1 fail the default `--fail-on high` check.** They are 4 of the 18 cases an independent review wrote; the other 14 are not in the repository yet, so this is a small sample. The 97 cases written by the author or taken from public sources are shown separately because the author knew what the rules match: of all 101 cases 87 are reported (86%). Visible prose is `low` by default, so the check fails less often than the rules report (hidden text and configuration are not lowered). The misses include paraphrases, other languages, scripts and files that nothing points the scanner to, tool descriptions served by a running MCP server, two scanner evasions that only the pull-request mode closes, and gaps of ours; each is listed in the results.
|
|
176
|
+
|
|
177
|
+
| Origin | Cases | Reported | Fails the default check |
|
|
178
|
+
|---|---:|---:|---:|
|
|
179
|
+
| author | 28 | 20 (71%) | 9 (32%) |
|
|
180
|
+
| review (independent) | 4 | 1 (25%) | 1 (25%) |
|
|
181
|
+
| public | 69 | 66 (96%) | 40 (58%) |
|
|
182
|
+
|
|
183
|
+
All origins, by technique:
|
|
176
184
|
|
|
177
185
|
| Technique | Cases | Reported | Fails the default check |
|
|
178
186
|
|---|---:|---:|---:|
|
|
@@ -197,9 +205,13 @@ measured and the misses are listed ([bench/](https://github.com/BardLaudian/ghos
|
|
|
197
205
|
| unicode-tags | 7 | 7 | 7 |
|
|
198
206
|
| variation-selectors | 3 | 3 | 3 |
|
|
199
207
|
| zero-width | 3 | 3 | 1 |
|
|
200
|
-
<!-- bench:end -->
|
|
208
|
+
<!-- bench:recall:end -->
|
|
209
|
+
|
|
210
|
+
<!-- bench:precision:start -->
|
|
211
|
+
**Precision: 100 repositories** frozen before any v0.4 tuning (851 agent files, default settings). They gave 21 findings, and 2 repositories would fail the default check. Precision for a user is true positives over everything reported, because a right pattern in a harmless place is still something to read: **4 of 21 (19 %)**, from labels given by the assistant that ran the benchmark and **not reviewed by a human** (0 findings unlabelled). Per rule: GR014 1 of 5; GR022 1 of 6; GR102 1 of 1; GR107 1 of 1; GR003 0 of 2; GR011 0 of 1; GR012 0 of 4; GR110 0 of 1. 21 findings are too few for a percentage per rule to mean much.
|
|
212
|
+
<!-- bench:precision:end -->
|
|
201
213
|
|
|
202
|
-
|
|
214
|
+
The new GR110 finding is a script that sends its own API key to the API it documents.
|
|
203
215
|
|
|
204
216
|
A workflow scheduled weekly scans 300 popular public repositories (`corpus/`, the only part of the project that uses the network) and fails if a critical or high finding has not been reviewed; last run: 2026-10-28. Those rules were tuned on that data, so its numbers are not an estimate of precision on new repositories. On it, 0.2.1 reported 84 critical and 156 high findings and 0.3.0 reports none critical and 53 high. The [study](https://github.com/BardLaudian/ghostrules/blob/main/study/report.md) used the same tooling; `ghostrules` is a set of heuristics, not a proof: see the [limits](https://github.com/BardLaudian/ghostrules/blob/main/docs/USAGE.md#limits).
|
|
205
217
|
|
|
@@ -89,7 +89,7 @@ ghostrules lock --check .ghostrules.lock
|
|
|
89
89
|
|
|
90
90
|
```yaml
|
|
91
91
|
- uses: actions/checkout@v4
|
|
92
|
-
- uses: BardLaudian/ghostrules@v0.4.
|
|
92
|
+
- uses: BardLaudian/ghostrules@v0.4.1
|
|
93
93
|
with:
|
|
94
94
|
fail-on: high # critical | high | medium | low | info | none
|
|
95
95
|
lock-check: "true" # fail if what the agents read changed
|
|
@@ -98,7 +98,7 @@ ghostrules lock --check .ghostrules.lock
|
|
|
98
98
|
```yaml
|
|
99
99
|
repos:
|
|
100
100
|
- repo: https://github.com/BardLaudian/ghostrules
|
|
101
|
-
rev: v0.4.
|
|
101
|
+
rev: v0.4.1
|
|
102
102
|
hooks:
|
|
103
103
|
- id: ghostrules
|
|
104
104
|
```
|
|
@@ -111,8 +111,16 @@ A scanner that cries wolf gets switched off, and one that misses gets trusted by
|
|
|
111
111
|
measured and the misses are listed ([bench/](https://github.com/BardLaudian/ghostrules/blob/main/bench/README.md),
|
|
112
112
|
[full results](https://github.com/BardLaudian/ghostrules/blob/main/bench/RESULTS.md)). Nothing below was tuned on the data it is measured on.
|
|
113
113
|
|
|
114
|
-
<!-- bench:start -->
|
|
115
|
-
**Recall
|
|
114
|
+
<!-- bench:recall:start -->
|
|
115
|
+
**Recall on independent cases: 1 of 4 reported (25%); 1 fail the default `--fail-on high` check.** They are 4 of the 18 cases an independent review wrote; the other 14 are not in the repository yet, so this is a small sample. The 97 cases written by the author or taken from public sources are shown separately because the author knew what the rules match: of all 101 cases 87 are reported (86%). Visible prose is `low` by default, so the check fails less often than the rules report (hidden text and configuration are not lowered). The misses include paraphrases, other languages, scripts and files that nothing points the scanner to, tool descriptions served by a running MCP server, two scanner evasions that only the pull-request mode closes, and gaps of ours; each is listed in the results.
|
|
116
|
+
|
|
117
|
+
| Origin | Cases | Reported | Fails the default check |
|
|
118
|
+
|---|---:|---:|---:|
|
|
119
|
+
| author | 28 | 20 (71%) | 9 (32%) |
|
|
120
|
+
| review (independent) | 4 | 1 (25%) | 1 (25%) |
|
|
121
|
+
| public | 69 | 66 (96%) | 40 (58%) |
|
|
122
|
+
|
|
123
|
+
All origins, by technique:
|
|
116
124
|
|
|
117
125
|
| Technique | Cases | Reported | Fails the default check |
|
|
118
126
|
|---|---:|---:|---:|
|
|
@@ -137,9 +145,13 @@ measured and the misses are listed ([bench/](https://github.com/BardLaudian/ghos
|
|
|
137
145
|
| unicode-tags | 7 | 7 | 7 |
|
|
138
146
|
| variation-selectors | 3 | 3 | 3 |
|
|
139
147
|
| zero-width | 3 | 3 | 1 |
|
|
140
|
-
<!-- bench:end -->
|
|
148
|
+
<!-- bench:recall:end -->
|
|
149
|
+
|
|
150
|
+
<!-- bench:precision:start -->
|
|
151
|
+
**Precision: 100 repositories** frozen before any v0.4 tuning (851 agent files, default settings). They gave 21 findings, and 2 repositories would fail the default check. Precision for a user is true positives over everything reported, because a right pattern in a harmless place is still something to read: **4 of 21 (19 %)**, from labels given by the assistant that ran the benchmark and **not reviewed by a human** (0 findings unlabelled). Per rule: GR014 1 of 5; GR022 1 of 6; GR102 1 of 1; GR107 1 of 1; GR003 0 of 2; GR011 0 of 1; GR012 0 of 4; GR110 0 of 1. 21 findings are too few for a percentage per rule to mean much.
|
|
152
|
+
<!-- bench:precision:end -->
|
|
141
153
|
|
|
142
|
-
|
|
154
|
+
The new GR110 finding is a script that sends its own API key to the API it documents.
|
|
143
155
|
|
|
144
156
|
A workflow scheduled weekly scans 300 popular public repositories (`corpus/`, the only part of the project that uses the network) and fails if a critical or high finding has not been reviewed; last run: 2026-10-28. Those rules were tuned on that data, so its numbers are not an estimate of precision on new repositories. On it, 0.2.1 reported 84 critical and 156 high findings and 0.3.0 reports none critical and 53 high. The [study](https://github.com/BardLaudian/ghostrules/blob/main/study/report.md) used the same tooling; `ghostrules` is a set of heuristics, not a proof: see the [limits](https://github.com/BardLaudian/ghostrules/blob/main/docs/USAGE.md#limits).
|
|
145
157
|
|
|
@@ -6,7 +6,7 @@ Two measurements, both reported with their misses and without tuning on them (W6
|
|
|
6
6
|
|
|
7
7
|
## Recall: the attack set (`attacks/`)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<!-- bench:cases:start -->101 cases (28 by the author, 4 from the independent review, 69 from public sources), each a technique written from a public description<!-- bench:cases:end --> (sources in `attacks/sources.toml`:
|
|
10
10
|
Rehberger and Butler on Unicode smuggling, Trojan Source, Pillar's Rules File Backdoor, Invariant
|
|
11
11
|
Labs on MCP tool poisoning, Check Point's Claude Code CVEs, OWASP's malicious-skills entry), with
|
|
12
12
|
the rule that should catch it and whether it does today (`status = "detected"` or `"known-miss"`).
|
|
@@ -21,7 +21,7 @@ the rule that should catch it and whether it does today (`status = "detected"` o
|
|
|
21
21
|
- **Misses stay in the base and are listed.** A test fails when a declared status stops matching what
|
|
22
22
|
the scanner does, so a miss that gets fixed must be updated, and a detection that regresses shows.
|
|
23
23
|
- **Provenance.** `origin` is `public` (a public technique), `review` (a case from the independent
|
|
24
|
-
review) or `
|
|
24
|
+
review) or `author` (written by the maintainer, who knew what the rules match: not independent). <!-- bench:provenance:start -->Only **4 of the review's 18 cases**<!-- bench:provenance:end --> are included:
|
|
25
25
|
the ones `REMEDIATION.md` describes. The other 14 are not in the repository and are not
|
|
26
26
|
invented here; add them as `origin = "review"` cases when the list is available. Ideally someone
|
|
27
27
|
who has not seen the rules writes another 20.
|
|
@@ -38,7 +38,7 @@ right pattern in a harmless place is still something someone has to read.
|
|
|
38
38
|
- **The labels are provisional.** They were given by the assistant that ran the benchmark, from the
|
|
39
39
|
finding and the lines around it, and no human has reviewed them. Borderline calls are noted in
|
|
40
40
|
[RESULTS.md](RESULTS.md).
|
|
41
|
-
- **The set is small.** 100 repositories gave
|
|
41
|
+
- **The set is small.** <!-- bench:heldout:start -->100 repositories gave 21 findings<!-- bench:heldout:end -->: a per-rule percentage on 1 to 6 findings
|
|
42
42
|
is an anecdote, not an estimate.
|
|
43
43
|
- **Never tune on it.** A rule changed after reading these findings would no longer be measured by
|
|
44
44
|
this set. Fixing what it shows needs a new held-out set, frozen first.
|
|
@@ -5,6 +5,12 @@ measured and how. Nothing here was tuned on the held-out set.
|
|
|
5
5
|
|
|
6
6
|
## Recall on the attack set
|
|
7
7
|
|
|
8
|
+
| Origin | Cases | Reported | Fails the default check |
|
|
9
|
+
|---|---:|---:|---:|
|
|
10
|
+
| author | 28 | 20 (71%) | 9 (32%) |
|
|
11
|
+
| review (independent) | 4 | 1 (25%) | 1 (25%) |
|
|
12
|
+
| public | 69 | 66 (96%) | 40 (58%) |
|
|
13
|
+
|
|
8
14
|
| Technique | Cases | Reported | Fails the default check |
|
|
9
15
|
|---|---:|---:|---:|
|
|
10
16
|
| agent-settings | 9 | 8 | 7 |
|
|
@@ -69,20 +75,20 @@ Known misses:
|
|
|
69
75
|
- `mcp-tool-description-poisoning` (mcp-server-content, public): Tool descriptions come from a running server and are never read (docs/USAGE.md, Limits).
|
|
70
76
|
- `evasion-ignore-end-of-line` (scanner-evasion, review): In the default scan; with --suppressions-from-ref or the Action on a pull request it is reported.
|
|
71
77
|
- `evasion-exclude-in-config` (scanner-evasion, review): In the default scan; --config-from-ref or the Action on a pull request ignores the added config.
|
|
72
|
-
- `override-french` (instruction-override,
|
|
73
|
-
- `override-leetspeak` (instruction-override,
|
|
74
|
-
- `override-paraphrase` (instruction-override,
|
|
75
|
-
- `conceal-paraphrase` (concealment,
|
|
76
|
-
- `exfil-spanish` (exfiltration,
|
|
78
|
+
- `override-french` (instruction-override, author): Patterns exist for English and Spanish only.
|
|
79
|
+
- `override-leetspeak` (instruction-override, author): Patterns match words, not look-alike spellings.
|
|
80
|
+
- `override-paraphrase` (instruction-override, author): Paraphrases cannot be caught by patterns (REMEDIATION W5.6).
|
|
81
|
+
- `conceal-paraphrase` (concealment, author): A paraphrase; patterns cannot catch intent (REMEDIATION W5.6).
|
|
82
|
+
- `exfil-spanish` (exfiltration, author): Found when the bench ran: GR013 has no Spanish patterns, although GR010, GR011, GR014 and GR016 do.
|
|
77
83
|
- `exfil-upload-config-file` (exfiltration, review): From REMEDIATION.md; a paraphrase, out of reach of patterns.
|
|
78
84
|
- `sensitive-npmrc` (sensitive-file, public): Found when the bench ran: 'Open ~/.npmrc' is not matched, even at info; the read-verb list lacks 'open'.
|
|
79
|
-
- `sensitive-paraphrase` (sensitive-file,
|
|
80
|
-
- `rce-python-urlopen-exec` (remote-code-execution,
|
|
81
|
-
- `skill-script-not-mentioned` (skill-scripts,
|
|
85
|
+
- `sensitive-paraphrase` (sensitive-file, author): No path or file name to match: a description of the file.
|
|
86
|
+
- `rce-python-urlopen-exec` (remote-code-execution, author): GR012 knows shell pipes, PowerShell and eval of curl; not exec(urlopen(...)).
|
|
87
|
+
- `skill-script-not-mentioned` (skill-scripts, author): Only scripts that SKILL.md references are scanned (REMEDIATION W5.2), by design.
|
|
82
88
|
|
|
83
89
|
## Precision on the held-out set
|
|
84
90
|
|
|
85
|
-
100 of 100 repositories scanned (
|
|
91
|
+
100 of 100 repositories scanned (851 agent files), ghostrules 0.4.1 at commit bef33a6, 2026-10-29, default settings and an empty configuration. 21 findings shown by default; 2 repositories would fail `--fail-on high`; 0 findings unlabelled.
|
|
86
92
|
|
|
87
93
|
| Rule | Findings | True positive | Benign context | False positive | Unlabelled | Precision for a user |
|
|
88
94
|
|---|---:|---:|---:|---:|---:|---:|
|
|
@@ -26,7 +26,7 @@ status = "detected"
|
|
|
26
26
|
[[case]]
|
|
27
27
|
id = "import-chain-payload"
|
|
28
28
|
technique = "imports"
|
|
29
|
-
origin = "
|
|
29
|
+
origin = "author"
|
|
30
30
|
source = "ours"
|
|
31
31
|
description = "A clean CLAUDE.md imports a file that imports a file with the instruction."
|
|
32
32
|
expected = ["GR013"]
|
|
@@ -39,7 +39,7 @@ status = "detected"
|
|
|
39
39
|
[[case]]
|
|
40
40
|
id = "import-payload-in-comment-of-imported-file"
|
|
41
41
|
technique = "imports"
|
|
42
|
-
origin = "
|
|
42
|
+
origin = "author"
|
|
43
43
|
source = "ours"
|
|
44
44
|
description = "The instruction sits in an HTML comment of an imported file."
|
|
45
45
|
expected = ["GR005"]
|
|
@@ -51,7 +51,7 @@ status = "detected"
|
|
|
51
51
|
[[case]]
|
|
52
52
|
id = "symlink-out-of-repo"
|
|
53
53
|
technique = "imports"
|
|
54
|
-
origin = "
|
|
54
|
+
origin = "author"
|
|
55
55
|
source = "ours"
|
|
56
56
|
description = "An instruction file that is a symlink pointing outside the repository."
|
|
57
57
|
expected = ["GR025"]
|
|
@@ -76,7 +76,7 @@ status = "detected"
|
|
|
76
76
|
[[case]]
|
|
77
77
|
id = "referenced-script-by-name"
|
|
78
78
|
technique = "referenced-files"
|
|
79
|
-
origin = "
|
|
79
|
+
origin = "author"
|
|
80
80
|
source = "ours"
|
|
81
81
|
description = "'Run scripts/setup.sh first' where the script exfiltrates."
|
|
82
82
|
expected = ["GR013", "GR110"]
|
|
@@ -37,7 +37,7 @@ status = "detected"
|
|
|
37
37
|
[[case]]
|
|
38
38
|
id = "html-comment-short"
|
|
39
39
|
technique = "html-comment"
|
|
40
|
-
origin = "
|
|
40
|
+
origin = "author"
|
|
41
41
|
source = "ours"
|
|
42
42
|
description = "A short comment (under 40 characters) that conceals."
|
|
43
43
|
expected = ["GR011"]
|
|
@@ -60,7 +60,7 @@ status = "detected"
|
|
|
60
60
|
[[case]]
|
|
61
61
|
id = "override-spanish"
|
|
62
62
|
technique = "instruction-override"
|
|
63
|
-
origin = "
|
|
63
|
+
origin = "author"
|
|
64
64
|
source = "ours"
|
|
65
65
|
description = "The same phrase in Spanish."
|
|
66
66
|
expected = ["GR010"]
|
|
@@ -104,7 +104,7 @@ status = "detected"
|
|
|
104
104
|
[[case]]
|
|
105
105
|
id = "override-across-lines"
|
|
106
106
|
technique = "instruction-override"
|
|
107
|
-
origin = "
|
|
107
|
+
origin = "author"
|
|
108
108
|
source = "ours"
|
|
109
109
|
description = "The phrase broken over several lines of a list."
|
|
110
110
|
expected = ["GR010"]
|
|
@@ -115,7 +115,7 @@ status = "detected"
|
|
|
115
115
|
[[case]]
|
|
116
116
|
id = "override-french"
|
|
117
117
|
technique = "instruction-override"
|
|
118
|
-
origin = "
|
|
118
|
+
origin = "author"
|
|
119
119
|
source = "ours"
|
|
120
120
|
description = "The override in French: only English and Spanish are covered."
|
|
121
121
|
expected = ["GR010"]
|
|
@@ -127,7 +127,7 @@ note = "Patterns exist for English and Spanish only."
|
|
|
127
127
|
[[case]]
|
|
128
128
|
id = "override-leetspeak"
|
|
129
129
|
technique = "instruction-override"
|
|
130
|
-
origin = "
|
|
130
|
+
origin = "author"
|
|
131
131
|
source = "ours"
|
|
132
132
|
description = "Digits for letters."
|
|
133
133
|
expected = ["GR010"]
|
|
@@ -139,7 +139,7 @@ note = "Patterns match words, not look-alike spellings."
|
|
|
139
139
|
[[case]]
|
|
140
140
|
id = "override-paraphrase"
|
|
141
141
|
technique = "instruction-override"
|
|
142
|
-
origin = "
|
|
142
|
+
origin = "author"
|
|
143
143
|
source = "ours"
|
|
144
144
|
description = "The same intent without any listed phrase."
|
|
145
145
|
expected = ["GR010"]
|
|
@@ -173,7 +173,7 @@ status = "detected"
|
|
|
173
173
|
[[case]]
|
|
174
174
|
id = "conceal-spanish"
|
|
175
175
|
technique = "concealment"
|
|
176
|
-
origin = "
|
|
176
|
+
origin = "author"
|
|
177
177
|
source = "ours"
|
|
178
178
|
description = "Concealment in Spanish."
|
|
179
179
|
expected = ["GR011"]
|
|
@@ -184,7 +184,7 @@ status = "detected"
|
|
|
184
184
|
[[case]]
|
|
185
185
|
id = "conceal-paraphrase"
|
|
186
186
|
technique = "concealment"
|
|
187
|
-
origin = "
|
|
187
|
+
origin = "author"
|
|
188
188
|
source = "ours"
|
|
189
189
|
description = "Concealment worded without a listed phrase."
|
|
190
190
|
expected = ["GR011"]
|
|
@@ -229,7 +229,7 @@ status = "detected"
|
|
|
229
229
|
[[case]]
|
|
230
230
|
id = "exfil-spanish"
|
|
231
231
|
technique = "exfiltration"
|
|
232
|
-
origin = "
|
|
232
|
+
origin = "author"
|
|
233
233
|
source = "ours"
|
|
234
234
|
description = "The same instruction in Spanish."
|
|
235
235
|
expected = ["GR013"]
|
|
@@ -253,7 +253,7 @@ note = "From REMEDIATION.md; a paraphrase, out of reach of patterns."
|
|
|
253
253
|
[[case]]
|
|
254
254
|
id = "exfil-two-sentences"
|
|
255
255
|
technique = "exfiltration"
|
|
256
|
-
origin = "
|
|
256
|
+
origin = "author"
|
|
257
257
|
source = "ours"
|
|
258
258
|
description = "The secret and the destination are in different sentences."
|
|
259
259
|
expected = ["GR013"]
|
|
@@ -309,7 +309,7 @@ note = "Found when the bench ran: 'Open ~/.npmrc' is not matched, even at info;
|
|
|
309
309
|
[[case]]
|
|
310
310
|
id = "sensitive-paraphrase"
|
|
311
311
|
technique = "sensitive-file"
|
|
312
|
-
origin = "
|
|
312
|
+
origin = "author"
|
|
313
313
|
source = "ours"
|
|
314
314
|
description = "The same target described without a path."
|
|
315
315
|
expected = ["GR014"]
|
|
@@ -376,7 +376,7 @@ status = "detected"
|
|
|
376
376
|
[[case]]
|
|
377
377
|
id = "rce-python-urlopen-exec"
|
|
378
378
|
technique = "remote-code-execution"
|
|
379
|
-
origin = "
|
|
379
|
+
origin = "author"
|
|
380
380
|
source = "ours"
|
|
381
381
|
description = "Download and exec from Python instead of a shell pipe."
|
|
382
382
|
expected = ["GR012"]
|
|
@@ -410,7 +410,7 @@ status = "detected"
|
|
|
410
410
|
[[case]]
|
|
411
411
|
id = "encoded-hex-blob"
|
|
412
412
|
technique = "encoded-payload"
|
|
413
|
-
origin = "
|
|
413
|
+
origin = "author"
|
|
414
414
|
source = "ours"
|
|
415
415
|
description = "A long hex string."
|
|
416
416
|
expected = ["GR015"]
|
|
@@ -69,7 +69,7 @@ status = "detected"
|
|
|
69
69
|
[[case]]
|
|
70
70
|
id = "tags-with-flag-prefix"
|
|
71
71
|
technique = "unicode-tags"
|
|
72
|
-
origin = "
|
|
72
|
+
origin = "author"
|
|
73
73
|
source = "ours"
|
|
74
74
|
description = "Tags that start like an emoji flag sequence (black flag, tags, cancel tag), which the rule treats as legitimate."
|
|
75
75
|
expected = ["GR001"]
|
|
@@ -115,7 +115,7 @@ status = "detected"
|
|
|
115
115
|
[[case]]
|
|
116
116
|
id = "zwsp-inside-keyword"
|
|
117
117
|
technique = "zero-width"
|
|
118
|
-
origin = "
|
|
118
|
+
origin = "author"
|
|
119
119
|
source = "ours"
|
|
120
120
|
description = "Zero-width spaces split a keyword so a naive text match misses it."
|
|
121
121
|
expected = ["GR003"]
|
|
@@ -137,7 +137,7 @@ status = "detected"
|
|
|
137
137
|
[[case]]
|
|
138
138
|
id = "soft-hyphen-split"
|
|
139
139
|
technique = "zero-width"
|
|
140
|
-
origin = "
|
|
140
|
+
origin = "author"
|
|
141
141
|
source = "ours"
|
|
142
142
|
description = "Soft hyphens inside a word, invisible when rendered."
|
|
143
143
|
expected = ["GR003"]
|
|
@@ -181,7 +181,7 @@ status = "detected"
|
|
|
181
181
|
[[case]]
|
|
182
182
|
id = "homoglyph-keyword"
|
|
183
183
|
technique = "homoglyph"
|
|
184
|
-
origin = "
|
|
184
|
+
origin = "author"
|
|
185
185
|
source = "ours"
|
|
186
186
|
description = "A Cyrillic letter inside a Latin word that is meant to look like an override phrase."
|
|
187
187
|
expected = ["GR006"]
|
|
@@ -203,7 +203,7 @@ status = "detected"
|
|
|
203
203
|
[[case]]
|
|
204
204
|
id = "homoglyph-greek"
|
|
205
205
|
technique = "homoglyph"
|
|
206
|
-
origin = "
|
|
206
|
+
origin = "author"
|
|
207
207
|
source = "ours"
|
|
208
208
|
description = "A Greek omicron in a command name."
|
|
209
209
|
expected = ["GR006"]
|
|
@@ -38,7 +38,7 @@ status = "detected"
|
|
|
38
38
|
[[case]]
|
|
39
39
|
id = "codex-override-payload"
|
|
40
40
|
technique = "exfiltration"
|
|
41
|
-
origin = "
|
|
41
|
+
origin = "author"
|
|
42
42
|
source = "ours"
|
|
43
43
|
description = "The instruction sits in AGENTS.override.md, which Codex reads before AGENTS.md."
|
|
44
44
|
expected = ["GR013"]
|
|
@@ -180,7 +180,7 @@ status = "detected"
|
|
|
180
180
|
[[case]]
|
|
181
181
|
id = "skill-script-not-mentioned"
|
|
182
182
|
technique = "skill-scripts"
|
|
183
|
-
origin = "
|
|
183
|
+
origin = "author"
|
|
184
184
|
source = "ours"
|
|
185
185
|
description = "A script in a skill directory that SKILL.md never mentions."
|
|
186
186
|
expected = ["GR110"]
|
|
@@ -193,7 +193,7 @@ note = "Only scripts that SKILL.md references are scanned (REMEDIATION W5.2), by
|
|
|
193
193
|
[[case]]
|
|
194
194
|
id = "referenced-see-and-do"
|
|
195
195
|
technique = "referenced-files"
|
|
196
|
-
origin = "
|
|
196
|
+
origin = "author"
|
|
197
197
|
source = "ours"
|
|
198
198
|
description = "'See docs/setup.txt and do what it says', with the payload in the .txt."
|
|
199
199
|
expected = ["GR013"]
|
|
@@ -205,7 +205,7 @@ status = "detected"
|
|
|
205
205
|
[[case]]
|
|
206
206
|
id = "referenced-follow-instructions-in"
|
|
207
207
|
technique = "referenced-files"
|
|
208
|
-
origin = "
|
|
208
|
+
origin = "author"
|
|
209
209
|
source = "ours"
|
|
210
210
|
description = "'Follow the instructions in notes/steps.md', a markdown file that is not imported."
|
|
211
211
|
expected = ["GR013"]
|
|
@@ -217,7 +217,7 @@ status = "detected"
|
|
|
217
217
|
[[case]]
|
|
218
218
|
id = "exfil-read-then-upload"
|
|
219
219
|
technique = "exfiltration"
|
|
220
|
-
origin = "
|
|
220
|
+
origin = "author"
|
|
221
221
|
source = "ours"
|
|
222
222
|
description = "A private key is read in one sentence and uploaded in the next."
|
|
223
223
|
expected = ["GR013"]
|
|
@@ -228,7 +228,7 @@ status = "detected"
|
|
|
228
228
|
[[case]]
|
|
229
229
|
id = "exfil-note-then-post"
|
|
230
230
|
technique = "exfiltration"
|
|
231
|
-
origin = "
|
|
231
|
+
origin = "author"
|
|
232
232
|
source = "ours"
|
|
233
233
|
description = "The env file is opened in one sentence and its values posted in the next."
|
|
234
234
|
expected = ["GR013"]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"date": "2026-10-29",
|
|
3
|
-
"ghostrules_version": "0.
|
|
4
|
-
"commit": "
|
|
3
|
+
"ghostrules_version": "0.4.1",
|
|
4
|
+
"commit": "bef33a6",
|
|
5
5
|
"repos_total": 100,
|
|
6
6
|
"repos_scanned": 100,
|
|
7
|
-
"files_scanned":
|
|
7
|
+
"files_scanned": 851,
|
|
8
8
|
"repos_failing_default_check": 2,
|
|
9
9
|
"findings": [
|
|
10
10
|
{
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
"rule_id": "GR014",
|
|
47
|
-
"severity": "
|
|
47
|
+
"severity": "high",
|
|
48
48
|
"fingerprint": "7f3c86ebf1cbcb23"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"rule_id": "GR014",
|
|
57
|
-
"severity": "
|
|
57
|
+
"severity": "high",
|
|
58
58
|
"fingerprint": "95602096758bd17b"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
@@ -29,7 +29,10 @@ from ghostrules.scanner import scan_paths
|
|
|
29
29
|
HERE = Path(__file__).resolve().parent
|
|
30
30
|
ATTACKS = HERE / "attacks"
|
|
31
31
|
STATUSES = ("detected", "known-miss")
|
|
32
|
-
ORIGINS = ("
|
|
32
|
+
ORIGINS = ("author", "review", "public")
|
|
33
|
+
# The headline recall counts only the cases the project's author did not write or choose.
|
|
34
|
+
INDEPENDENT = "review"
|
|
35
|
+
REVIEW_TOTAL = 18 # cases the independent review reported; the rest are not in the repository yet
|
|
33
36
|
|
|
34
37
|
_TOKEN = re.compile(r"\{(tags|vs|b64|hex):([^{}]*)\}|\{u\+([0-9A-Fa-f]{4,6})\}")
|
|
35
38
|
|
|
@@ -161,6 +164,20 @@ def recall_table(outcomes: list[Outcome]) -> list[dict[str, Any]]:
|
|
|
161
164
|
return sorted(rows.values(), key=lambda r: r["technique"])
|
|
162
165
|
|
|
163
166
|
|
|
167
|
+
def origin_table(outcomes: list[Outcome]) -> list[dict[str, Any]]:
|
|
168
|
+
"""Per origin (every origin, even with no case): cases, reported, failing the default check."""
|
|
169
|
+
return [
|
|
170
|
+
{
|
|
171
|
+
"origin": origin,
|
|
172
|
+
"cases": len(mine),
|
|
173
|
+
"reported": sum(o.reported for o in mine),
|
|
174
|
+
"fails_ci": sum(o.fails_ci for o in mine),
|
|
175
|
+
}
|
|
176
|
+
for origin in ORIGINS
|
|
177
|
+
for mine in [[o for o in outcomes if o.case.origin == origin]]
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
|
|
164
181
|
# --- precision on the held-out set --------------------------------------------------------------
|
|
165
182
|
|
|
166
183
|
LABELS = ("tp", "benign-context", "fp")
|