Skip to content

security(actions): detect transport-only bounds on verdict polling loops #1087

Description

@seonghobae

Verified organization security-control incident

ContextualWisdomLab/.github required-review polling had a real availability/governance failure mode: a shell loop in .github/workflows/opencode-review.yml bounded consecutive gh api transport failures but did not bound total wall-clock wait when every API call succeeded and no current-head verdict ever appeared. The loop could therefore retain a GitHub Actions runner until the platform job timeout, contributing to organization-wide shared-runner saturation and delaying required review/security workflows.

Causal owner evidence

  • vulnerable protected predecessor: ContextualWisdomLab/.github@5c561a65cca3b925d533e4b40c5c3ac00f16524e;
  • causal protected repair: e29302c05eade7da7b0bdbb453e53980bc9d577b (PR #1707), which adds a 10,800-second total polling deadline checked on every iteration and fails closed when no verdict arrives;
  • current protected .github/main descendant: 6f70174e338013fec9a000311bc72312f5d4dbf9;
  • related PR #1706 carries additional regression-test deltas and must not be retired unless those valid deltas are independently shown redundant or completely carried forward.

The protected repair documented live Required OpenCode Review/Strix Security Scan runs stuck for 7–20 hours and organization-wide Actions queue starvation. This issue turns that fixed incident into an AppGuardrail executable detection obligation.

Root cause / reusable pattern

A long-running GitHub Actions shell polling loop has:

  1. an unbounded while : / while true success-without-result path;
  2. remote/control-plane polling such as gh api;
  3. sleep/retry behavior;
  4. a counter that only limits transport failures (for example max_poll_transport_failures) rather than the successful-no-verdict path; and
  5. no total wall-clock deadline or other finite total-attempt bound for that loop.

The defect is uncontrolled CI resource consumption / security-control availability degradation, not the presence of a particular issue title or workflow name.

Detection contract

Add a packaged GitHub Actions detector, regression corpus, traceability, and changelog entry for this causal pattern.

Positive signal

A workflow shell step contains a transport-failure-limited remote polling loop whose all-success/no-verdict path can repeat without a total deadline/attempt bound.

False-positive boundaries

Do not report when the same polling loop has an explicit total wall-clock deadline checked per iteration, a finite total-attempt bound, or an enclosing explicit job/runtime bound that provably terminates the reviewed loop within policy. Documentation/comment text must not count as executable polling evidence.

False-negative boundaries

Cross-file/composite-action polling, non-shell control flow, dynamically generated workflow code, or substantially different retry frameworks are separate obligations unless the lightweight detector can model them safely.

Acceptance criteria

  • executable AppGuardrail rule/analyzer, not issue-string matching;
  • pinned vulnerable .github incident fixture and protected fixed fixture or equivalent answer-free regression evidence;
  • positive, negative, path-scope, comment/prose, deadline and finite-attempt tests through production _scan_file;
  • CWE-400 traceability and explicit FP/FN boundaries;
  • exact-head repository checks and independent review before ordinary merge;
  • no suppression, timeout-gate weakening, self-approval, or protection bypass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity vulnerability or security-governance work

    Type

    No type

    Projects

    • Status
      Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions