feat(rules): ResearchExtensions (RE001-RE010) — 10 rules from Snyk/StepSecurity/Endor/academic literature#325
Merged
Conversation
…epSecurity/Endor/academic literature New Hypatia.Rules.ResearchExtensions module captures GitHub Actions workflow defects sourced from commercial CI/CD security tools (Snyk, StepSecurity, Endor Labs, Datadog CI Visibility) and recent academic literature. Complementary to WorkflowHardening (OSS actionlint/zizmor cluster, PR #321) and BaselineHealth (drift, PR #320). Rule inventory: | ID | Detects | Severity | Provenance | |---|---|---|---| | RE001 | Workflow with secrets access lacks step-security/harden-runner | warn | StepSecurity | | RE002 | Harden-Runner in egress-policy: audit on protected-branch workflow | high | StepSecurity | | RE003 | actions/cache key interpolates github.head_ref / PR title | high | Snyk dataflow | | RE004 | Container action pinned by tag, not digest | warn | Snyk container scan | | RE005 | Test step swallows non-zero exit | warn | Datadog CI | | RE006 | Composite action calls third-party uses: it doesn't SHA-pin | warn | Endor R-END-05 | | RE007 | Top-level env: contains secrets reference | warn | Endor R-END-01 | | RE008 | Bot identity check via spoofable github.actor | critical | zizmor + Koishybayev 2022 | | RE009 | fromJSON(secrets.X) bypasses runner-side redaction | critical | zizmor unredacted-secrets | | RE010 | workflow_run consumes artifacts without provenance verification | warn | EuroS&P 2023 | Module + tests mirror WorkflowHardening shape: scan/2 facade returning %{findings, total, by_severity, dispatch}; helpers workflow_files/1, composite_action_files/1, line_number_for_offset/2, group_by_severity/1, dispatch_recommendations/1. All findings routed to :sustainabot at confidence calibrated by severity (critical -> 0.92, high -> 0.85, warn -> 0.75, info -> 0.60). Pure-local file scans; no GitHub API. Test file: test/research_extensions_test.exs -- 27 tests covering positive + negative cases per rule plus three scan/1 facade tests. Module size: ~900 lines. No refactor to existing modules.
🔍 Hypatia Security ScanFindings: 103 issues detected
View findings[
{
"reason": "Docker reference in Nickel config -- Podman/Containerfile highly preferred (Docker permitted) (1 occurrences, CWE-1104)",
"type": "ncl_docker_not_podman",
"file": "/home/runner/work/hypatia/hypatia/.machine_readable/svc/k9/hypatia-metadata.k9.ncl",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/batch.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
"type": "unwrap_dangerous_default",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/batch.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (1 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
"type": "unwrap_dangerous_default",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/fleet.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/output.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/build.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (3 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/fixer/src/main.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (5 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/fixer/src/scanner.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
🔍 Hypatia Security ScanFindings: 104 issues detected
View findings[
{
"reason": "Docker reference in Nickel config -- Podman/Containerfile highly preferred (Docker permitted) (1 occurrences, CWE-1104)",
"type": "ncl_docker_not_podman",
"file": "/home/runner/work/hypatia/hypatia/.machine_readable/svc/k9/hypatia-metadata.k9.ncl",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/batch.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
"type": "unwrap_dangerous_default",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/batch.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (1 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
"type": "unwrap_dangerous_default",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/fleet.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/src/output.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (2 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/cli/build.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap() without prior check -- DoS via panic (3 occurrences, CWE-754)",
"type": "unwrap_without_check",
"file": "/home/runner/work/hypatia/hypatia/fixer/src/main.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (5 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/fixer/src/scanner.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
🔍 Hypatia Security ScanFindings: 105 issues detected
View findings[
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/batch.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
"type": "unwrap_dangerous_default",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (1 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/scan.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/commands/fleet.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/src/output.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/cli/build.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (2 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/fixer/src/main.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "expect() in hot path (1 occurrences, CWE-754)",
"type": "expect_in_hot_path",
"file": "/home/runner/work/hypatia/hypatia/integration/src/lib.rs",
"action": "flag",
"rule_module": "code_safety",
"severity": "low"
},
{
"reason": "1 untracked file(s) -- review and add or .gitignore",
"type": "GS001",
"file": ".",
"action": "review",
"rule_module": "git_state",
"severity": "low"
},
{
"reason": "Repository has 3 non-main remote branch(es). Policy: single main branch only.",
"type": "GS007",
"file": ".",
"action": "delete_remote_branches",
"rule_module": "git_state",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
hyperpolymath
added a commit
that referenced
this pull request
May 26, 2026
## Summary Adds delegates on \`Hypatia.Rules\` for the rule families landed in PRs #320, #321, #322, #323. The new modules were independently usable but invisible from the top-level facade; this PR closes that gap. ## New facade surface | Function | Delegates to | Rules | |---|---|---| | \`scan_baseline_health/2\` | \`BaselineHealth.scan\` (existing; docstring updated for BH007) | BH001-BH007 | | \`scan_workflow_hardening/2\` | \`WorkflowHardening.scan\` | WH001-WH012 | | \`scan_supply_chain/2\` | \`SupplyChain.scan\` | SC001-SC011 | | \`scan_branch_protection/2\` | \`BranchProtection.scan\` (API-backed; takes \`owner, repo\`) | BP001-BP007 | | \`scan_all_estate_policies/2\` | — runs all four families and merges results | 34 rules total | ## Why combined-scan helper Callers (\`pattern_analyzer\`, \`triangle_router\`, \`fleet_dispatcher\`) already treat \`Hypatia.Rules\` as the entrypoint. Without these delegates the new modules are reachable only via fully-qualified names, which works but inverts the facade pattern adopted in #316. ## RE001-RE010 (PR #325) not included PR #325 (ResearchExtensions) is still OPEN at the time of this PR. Adding its alias here would break compilation on \`main\` for the window between this PR landing and #325 landing. A follow-up commit will wire \`ResearchExtensions\` once #325 merges — tracked inline at the alias block as a \`# wired in follow-up\` comment. ## Test plan - Existing 18-test \`BaselineHealth\` suite still passes (untouched). - Module compile-checks pass for the four families + facade. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New module
Hypatia.Rules.ResearchExtensionscaptures GitHub Actions workflow defects sourced from commercial CI/CD security tools (Snyk, StepSecurity, Endor Labs, Datadog CI Visibility) and recent academic literature. Complementary toWorkflowHardening(open-source actionlint/zizmor cluster, PR #321) andBaselineHealth(drift, PR #320).10 rules, all pure-local YAML scans on
<repo>/.github/workflows/*.ymlplus repo-walk for compositeaction.ymlfiles.Rule inventory
secrets.*but does not installstep-security/harden-runner:warnegress-policy: auditon a protected-branch trigger (no actual containment):highactions/cachekey interpolatesgithub.head_ref/ PR title / PR body — cache poisoning vector:highdocker://image:tagorcontainer: image:tagpinned by tag, not by@sha256:<digest>:warncontinue-on-error: trueor|| true:warnaction.yml(using: composite) calls a third-partyuses:that isn't SHA-pinned (nested-pinning gap):warnenv:block contains\${{ secrets.* }}— exposes secret to every step:warnif: github.actor == 'dependabot[bot]'— spoofable bot identity onpull_request_targetfrom a fork:criticalbot-conditions+ Koishybayev et al. (USENIX Security 2022)\${{ fromJSON(secrets.X) }}— parsed fields bypass runner-side log redaction:criticalunredacted-secretsworkflow_runtrigger downloads artifacts withoutrun-idconstraint or provenance verification:warnTests
mix test test/research_extensions_test.exscovers 27 tests:continue-on-error: trueand\|\| true; RE010 has two clean-state paths:run-idconstraint andgh attestation verify).scan/1facade exercised on a clean workflow, an empty repo, and a multi-finding repo (asserting all dispatch routes to:sustainabotat severity-calibrated confidence).Design
.github/workflows/*.yml/*.yamlonly (mirrorsWorkflowHardening/BaselineHealthBH004).action.ymlfiles — composite actions can live anywhere.WorkflowHardening.:sustainabot(advisory — fixes vary by defect class). Confidence calibrated by severity: critical 0.92, high 0.85, warn 0.75, info 0.60.Module size
lib/rules/research_extensions.ex— ~900 lines.test/research_extensions_test.exs— ~400 lines.No refactor to existing modules.
🤖 Generated with Claude Code