security(actions): detect transport-only verdict poll bounds - #1088
security(actions): detect transport-only verdict poll bounds#1088seonghobae wants to merge 93 commits into
Conversation
📝 WalkthroughWalkthroughGitHub Actions 폴링 탐지기가 조건부 종료, 전체 시도 제한, 상태 재설정, 도달 불가능한 종료와 셸 명령 경계를 구분하도록 확장되었다. 새 규칙, 회귀 테스트, 보안 코퍼스와 추적성 문서가 추가되었다. ChangesGitHub Actions 폴링 경계 탐지
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: 🟡 Moderate · up to The PR adds static HIGH findings for unsafe GitHub Actions polling patterns, but the current rules can still flag safe workflows and overlook valid bounds in some cases, potentially blocking legitimate CI configurations. Required exact-head checks are also not yet complete, so merge should wait for the detector corrections or explicit owner acceptance. Sequence Diagram(s)sequenceDiagram
participant Workflow as GitHub Actions 워크플로
participant Scanner as _scan_file
participant PollRule as 폴링 경계 규칙
participant Findings as Findings
Workflow->>Scanner: 워크플로 파일 전달
Scanner->>PollRule: gh api·sleep·상태·종료 흐름 검사
PollRule->>Findings: 폴링 경계 또는 상태 변이 결과 반환
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 115 functions across 15 files. (2 skipped: 2 unsupported.) ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| return ''' if ! response="$(%s)"; then | ||
| api_error_streak=$((api_error_streak + 1)) | ||
| if [ "$api_error_streak" -ge "$transport_error_budget" ]; then | ||
| exit 1 | ||
| fi | ||
| continue | ||
| fi | ||
| api_error_streak=0''' % command | ||
|
|
||
|
|
||
| def _historical_transport_branch(command: str = "gh api repos/example/repo/pulls/7/reviews") -> str: | ||
| """Return the source-incident transport-failure branch.""" | ||
| return ''' if ! response="$(%s)"; then |
| def test_unreachable_exit_requires_executable_poll_command(tmp_path: Path) -> None: | ||
| """Quoted nested command text cannot witness an unreachable-bound polling defect.""" | ||
| shell = """ | ||
| overall_deadline=$(($(date +%s) + 600)) | ||
| while :; do | ||
| if [ "$(date +%s)" -ge "$overall_deadline" ]; then | ||
| continue | ||
| exit 1 | ||
| fi | ||
| if ! response="$(printf '%s\\n' '$(gh api repos/example/repo/pulls/7/reviews)')"; then | ||
| continue | ||
| fi | ||
| sleep 30 | ||
| done | ||
| """ | ||
| assert _UNREACHABLE not in _scan(tmp_path, shell) | ||
|
|
||
|
|
||
| def test_unreachable_exit_keeps_direct_poll_command_positive(tmp_path: Path) -> None: | ||
| """A directly executed gh api poll keeps the unreachable-exit finding positive.""" | ||
| shell = """ | ||
| overall_deadline=$(($(date +%s) + 600)) | ||
| while :; do | ||
| if [ "$(date +%s)" -ge "$overall_deadline" ]; then | ||
| continue | ||
| exit 1 | ||
| fi | ||
| if ! response="$(gh api repos/example/repo/pulls/7/reviews)"; then | ||
| continue | ||
| fi | ||
| sleep 30 | ||
| done | ||
| """ | ||
| assert _UNREACHABLE in _scan(tmp_path, shell) |
There was a problem hiding this comment.
| patterns: | ||
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ timeout-minutes[ \t]*:|^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ (?P<dline>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[^\n]*\n(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<ind_d>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=ind_d)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?=(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<ind_c>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n)(?=(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<ind_l>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n)(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=ind_c)[ \t]*=[ \t]*\$\(\([ \t]*(?P=ind_c)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=ind_c)\b[^\n]*-ge[^\n]*\$(?P=ind_l)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=dline)[ \t]*=[ \t]*(?:\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)|\$\(\([ \t]*(?P=dline)[ \t]*\+[ \t]*[1-9][0-9]*[ \t]*\)\))[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=dline)\b[^\n]*;[ \t]*then[^\n]*\n(?:^ {14,}(?!continue\b|break\b|return\b|exit\b)[^\n]*\n){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?:(?:if[ \t]+![ \t]+)?[A-Za-z_][A-Za-z0-9_]*[ \t]*=[^#\n]*\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:if[ \t]+![ \t]+)?(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit[ \t]+0)[ \t]*(?:#[^\n]*)?\n)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,180}?^ done\b' | ||
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ timeout-minutes[ \t]*:|^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ (?P<creset>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<ind_d2>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=ind_d2)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?!(?P=creset)\b)(?P<sclc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,40}?^ (?P<scll>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=sclc)[ \t]*=[ \t]*0\b)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=scll)[ \t]*=)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=sclc)[ \t]*=[ \t]*\$\(\([ \t]*(?P=sclc)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=sclc)\b[^\n]*-ge[^\n]*\$(?P=scll)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<slcl>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,40}?^ (?!(?P=creset)\b)(?P<slcc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=slcc)[ \t]*=[ \t]*0\b)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=slcl)[ \t]*=)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=slcc)[ \t]*=[ \t]*\$\(\([ \t]*(?P=slcc)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=slcc)\b[^\n]*-ge[^\n]*\$(?P=slcl)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,140}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=creset)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=creset)[ \t]*=[ \t]*\$\(\([ \t]*(?P=creset)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )if[^\n]*\$(?P=creset)\b[^\n]*-ge[^\n]*\$[A-Za-z_][A-Za-z0-9_]*\b[^\n]*;[ \t]*then[^\n]*\n(?:^ {14,}(?!continue\b|break\b|return\b|exit\b)[^\n]*\n){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?:(?:if[ \t]+![ \t]+)?[A-Za-z_][A-Za-z0-9_]*[ \t]*=[^#\n]*\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:if[ \t]+![ \t]+)?(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit[ \t]+0)[ \t]*(?:#[^\n]*)?\n)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,180}?^ done\b' | ||
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ timeout-minutes[ \t]*:|^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ (?P<gcounter>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<glimit>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<gindd_cl>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=gindd_cl)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?!(?P=gcounter)\b)(?P<gicl_clc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,40}?^ (?P<gicl_cll>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gicl_clc)[ \t]*=[ \t]*0\b)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gicl_cll)[ \t]*=)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=gicl_clc)[ \t]*=[ \t]*\$\(\([ \t]*(?P=gicl_clc)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=gicl_clc)\b[^\n]*-ge[^\n]*\$(?P=gicl_cll)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<gilc_cll>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,40}?^ (?!(?P=gcounter)\b)(?P<gilc_clc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gilc_clc)[ \t]*=[ \t]*0\b)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gilc_cll)[ \t]*=)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=gilc_clc)[ \t]*=[ \t]*\$\(\([ \t]*(?P=gilc_clc)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=gilc_clc)\b[^\n]*-ge[^\n]*\$(?P=gilc_cll)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=gcounter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=gcounter)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=glimit)[ \t]*=[ \t]*\$\(\([ \t]*(?P=glimit)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )if[^\n]*\$(?P=gcounter)\b[^\n]*-ge[^\n]*\$(?P=glimit)\b[^\n]*;[ \t]*then[^\n]*\n(?:^ {14,}(?!continue\b|break\b|return\b|exit\b)[^\n]*\n){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?:(?:if[ \t]+![ \t]+)?[A-Za-z_][A-Za-z0-9_]*[ \t]*=[^#\n]*\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:if[ \t]+![ \t]+)?(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit[ \t]+0)[ \t]*(?:#[^\n]*)?\n)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,180}?^ done\b' | ||
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ timeout-minutes[ \t]*:|^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ (?P<glimit>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<gcounter>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<gindd_lc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[^\n]*\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=gindd_lc)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?!(?P=gcounter)\b)(?P<gicl_lcc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,40}?^ (?P<gicl_lcl>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gicl_lcc)[ \t]*=[ \t]*0\b)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gicl_lcl)[ \t]*=)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=gicl_lcc)[ \t]*=[ \t]*\$\(\([ \t]*(?P=gicl_lcc)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=gicl_lcc)\b[^\n]*-ge[^\n]*\$(?P=gicl_lcl)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ (?P<gilc_lcl>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,40}?^ (?!(?P=gcounter)\b)(?P<gilc_lcc>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,80}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gilc_lcc)[ \t]*=[ \t]*0\b)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?P=gilc_lcl)[ \t]*=)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,80}?^ {12}(?! )(?P=gilc_lcc)[ \t]*=[ \t]*\$\(\([ \t]*(?P=gilc_lcc)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )if[^\n]*\$(?P=gilc_lcc)\b[^\n]*-ge[^\n]*\$(?P=gilc_lcl)\b[^\n]*;[ \t]*then[^\n]*\n^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?:(?!^ while[ \t])^ {10,}[^\n]*\n){0,120}?^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=gcounter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=gcounter)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )(?P=glimit)[ \t]*=[ \t]*\$\(\([ \t]*(?P=glimit)[ \t]*\+[ \t]*1[ \t]*\)\)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )if[^\n]*\$(?P=gcounter)\b[^\n]*-ge[^\n]*\$(?P=glimit)\b[^\n]*;[ \t]*then[^\n]*\n(?:^ {14,}(?!continue\b|break\b|return\b|exit\b)[^\n]*\n){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12,}(?:(?:if[ \t]+![ \t]+)?[A-Za-z_][A-Za-z0-9_]*[ \t]*=[^#\n]*\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:if[ \t]+![ \t]+)?(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*\n)(?=(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n)(?!(?:(?!^ done\b)^ {12,}[^\n]*\n){0,120}?^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?:(?!^ done\b)^ {12,}[^\n]*\n){0,40}?^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit[ \t]+0)[ \t]*(?:#[^\n]*)?\n)(?:(?!^ done\b)^ {12,}[^\n]*\n){0,180}?^ done\b' |
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?!(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,80}^ max_poll_transport_failures\b)(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,80}(?:^ (?P<helper_counter>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<helper_limit>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=helper_counter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=helper_counter)[ \t]*\+[ \t]*1[ \t]*\)\)|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=helper_counter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=helper_counter)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=helper_counter)[^\n]*-ge[^\n]*\$(?P=helper_limit)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=helper_counter)[^\n]*-ge[^\n]*\$(?P=helper_limit)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n(?>(?:(?!^ done\b)^ {12,}[^\n]*\n)){0,40}^ done\b[^\n]*\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,80})?^ (?P<retry_counter_alias>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<retry_limit_alias>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\))^ {10,}[^\n]*\n)){0,40}^ (?P<safe_deadline>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=safe_deadline)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=safe_deadline)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_counter>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_limit>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=safe_total_counter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter)[ \t]*\+[ \t]*1[ \t]*\)\)|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=safe_total_counter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=safe_total_counter)[^\n]*-ge[^\n]*\$(?P=safe_total_limit)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=safe_total_counter)[^\n]*-ge[^\n]*\$(?P=safe_total_limit)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_limit_first>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_counter_second>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=safe_total_counter_second)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter_second)[ \t]*\+[ \t]*1[ \t]*\)\)|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=safe_total_counter_second)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter_second)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=safe_total_counter_second)[^\n]*-ge[^\n]*\$(?P=safe_total_limit_first)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=safe_total_counter_second)[^\n]*-ge[^\n]*\$(?P=safe_total_limit_first)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12,}if[ \t]+![ \t]+(?:[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*["'']?\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,40}^ {12,}if[ \t]+![ \t]+(?:[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*["'']?\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14,}(?P=retry_counter_alias)[ \t]*=[ \t]*\$\(\([ \t]*(?P=retry_counter_alias)[ \t]*\+[ \t]*1[ \t]*\)\)|^ {12}fi\b)^ {14,}[^\n]*\n)){0,12}^ {14,}(?P=retry_counter_alias)[ \t]*=[ \t]*\$\(\([ \t]*(?P=retry_counter_alias)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {14,}if[^\n]*\$(?P=retry_counter_alias)[^\n]*-ge[^\n]*\$(?P=retry_limit_alias)\b[^\n]*;[ \t]*then|^ {12}fi\b)^ {14,}[^\n]*\n)){0,12}^ {14,}if[^\n]*\$(?P=retry_counter_alias)[^\n]*-ge[^\n]*\$(?P=retry_limit_alias)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {16,}exit[ \t]+[1-9][0-9]*\b)^ {16,}[^\n]*\n)){0,8}^ {16,}exit[ \t]+[1-9][0-9]*\b[^\n]*\n(?>(?:(?!^ {12}fi[ \t]*(?:#[^\n]*)?$)^ {12,}[^\n]*\n)){0,40}^ {12}fi[ \t]*(?:#[^\n]*)?\n(?!(?>(?:(?!^ {12}(?! )(?:if\b|fi\b|break(?:[ \t]+[1-9][0-9]*)?\b|exit(?:[ \t]+0)?\b))^ {12,}[^\n]*\n)){0,40}^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit(?:[ \t]+0)?)[ \t]*(?:#[^\n]*)?$\n)(?>(?:(?!^ {12}(?! )sleep(?:[ \t]+|$)|^ done\b)^ {12,}[^\n]*\n)){0,40}^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?!(?>(?:(?!^ {12}(?! )(?:if\b|fi\b|break(?:[ \t]+[1-9][0-9]*)?\b|exit(?:[ \t]+0)?\b)|^ done\b)^ {12,}[^\n]*\n)){0,40}^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit(?:[ \t]+0)?)[ \t]*(?:#[^\n]*)?$\n)(?>(?:(?!^ done\b)^ {12,}[^\n]*\n)){0,40}^ done\b' | ||
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?!(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,80}^ max_poll_transport_failures\b)(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,80}^ (?P<retry_limit_reverse>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<retry_counter_reverse>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\))^ {10,}[^\n]*\n)){0,40}^ (?P<safe_deadline_reverse>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=safe_deadline_reverse)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-ge[^\n]*\$(?P=safe_deadline_reverse)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_counter_reverse>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_limit_reverse>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=safe_total_counter_reverse)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter_reverse)[ \t]*\+[ \t]*1[ \t]*\)\)|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=safe_total_counter_reverse)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter_reverse)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=safe_total_counter_reverse)[^\n]*-ge[^\n]*\$(?P=safe_total_limit_reverse)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=safe_total_counter_reverse)[^\n]*-ge[^\n]*\$(?P=safe_total_limit_reverse)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_limit_reverse_first>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!(?:^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$))^ {10,}[^\n]*\n)){0,40}^ (?P<safe_total_counter_reverse_second>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=safe_total_counter_reverse_second)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter_reverse_second)[ \t]*\+[ \t]*1[ \t]*\)\)|^ done\b)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=safe_total_counter_reverse_second)[ \t]*=[ \t]*\$\(\([ \t]*(?P=safe_total_counter_reverse_second)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=safe_total_counter_reverse_second)[^\n]*-ge[^\n]*\$(?P=safe_total_limit_reverse_first)\b[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=safe_total_counter_reverse_second)[^\n]*-ge[^\n]*\$(?P=safe_total_limit_reverse_first)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12,}if[ \t]+![ \t]+(?:[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*["'']?\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*;[ \t]*then|^ done\b)^ {12,}[^\n]*\n)){0,40}^ {12,}if[ \t]+![ \t]+(?:[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*["'']?\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14,}(?P=retry_counter_reverse)[ \t]*=[ \t]*\$\(\([ \t]*(?P=retry_counter_reverse)[ \t]*\+[ \t]*1[ \t]*\)\)|^ {12}fi\b)^ {14,}[^\n]*\n)){0,12}^ {14,}(?P=retry_counter_reverse)[ \t]*=[ \t]*\$\(\([ \t]*(?P=retry_counter_reverse)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {14,}if[^\n]*\$(?P=retry_counter_reverse)[^\n]*-ge[^\n]*\$(?P=retry_limit_reverse)\b[^\n]*;[ \t]*then|^ {12}fi\b)^ {14,}[^\n]*\n)){0,12}^ {14,}if[^\n]*\$(?P=retry_counter_reverse)[^\n]*-ge[^\n]*\$(?P=retry_limit_reverse)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {16,}exit[ \t]+[1-9][0-9]*\b)^ {16,}[^\n]*\n)){0,8}^ {16,}exit[ \t]+[1-9][0-9]*\b[^\n]*\n(?>(?:(?!^ {12}fi[ \t]*(?:#[^\n]*)?$)^ {12,}[^\n]*\n)){0,40}^ {12}fi[ \t]*(?:#[^\n]*)?\n(?!(?>(?:(?!^ {12}(?! )(?:if\b|fi\b|break(?:[ \t]+[1-9][0-9]*)?\b|exit(?:[ \t]+0)?\b))^ {12,}[^\n]*\n)){0,40}^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit(?:[ \t]+0)?)[ \t]*(?:#[^\n]*)?$\n)(?>(?:(?!^ {12}(?! )sleep(?:[ \t]+|$)|^ done\b)^ {12,}[^\n]*\n)){0,40}^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?!(?>(?:(?!^ {12}(?! )(?:if\b|fi\b|break(?:[ \t]+[1-9][0-9]*)?\b|exit(?:[ \t]+0)?\b)|^ done\b)^ {12,}[^\n]*\n)){0,40}^ {12}(?! )(?:break(?:[ \t]+[1-9][0-9]*)?|exit(?:[ \t]+0)?)[ \t]*(?:#[^\n]*)?$\n)(?>(?:(?!^ done\b)^ {12,}[^\n]*\n)){0,40}^ done\b' |
There was a problem hiding this comment.
🟡 Strict polling bounds trigger false alarms
A finite deadline or attempt guard using -gt bypasses the detector's -ge-only exclusions. The scanner then reports a HIGH unbounded-loop finding.
Prompt for agents
Update both transport-only polling detectors in scanner/rules/github_actions_transport_budget.yml and scanner/rules/github_actions.yml to recognize strict greater-than guards as finite total bounds alongside greater-than-or-equal guards. Cover both wall-clock deadlines and total-attempt counters, both declaration orders, and add production _scan_file regressions for historical and renamed transport-budget forms. Preserve the existing direction check so reversed or non-expiring comparisons do not suppress findings.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
RED를 먼저 고정했습니다. d50f49ccea1cbf2aecc6da268850fddfc80db3b6의 production _scan_file 회귀가 historical/renamed 두 transport detector 모두에 대해 date ... -gt $overall_deadline과 total-attempt counter -gt limit를 finite negative로 요구하고, attempt counter/limit 선언 순서 두 가지를 모두 검증합니다. 현재 production matcher는 이 계약을 아직 만족하지 않으므로 thread는 unresolved로 유지합니다. GREEN은 같은 causally initialized state의 forward -gt/-ge를 모두 안전 경계로 인정하되 reversed/non-expiring 비교는 suppress하지 않고, 기존 vulnerable positives를 그대로 통과하는 exact-head test evidence입니다.
| # unconditionally breaks/exits before the back edge is finite. | ||
| - id: github-actions-transport-only-poll-bound | ||
| patterns: | ||
| - pattern-regex: '^ [A-Za-z0-9_.-]+[^\n]*\n(?!(?>(?:(?!^(?:[A-Za-z0-9_.-]+| [A-Za-z0-9_.-]+)[ \t]*:)[^\n]*\n))*^ timeout-minutes[ \t]*:[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)(?>(?:(?!^ timeout-minutes[ \t]*:|^ -[ \t]*run|^ run)^ {4,}[^\n]*\n)){0,80}^(?: -[ \t]*run| run)[ \t]*:[ \t]*\|[+-]?[^\n]*\n(?=(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,80}^ (?P<hist_transport_counter>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ max_poll_transport_failures)^ {10,}[^\n]*\n)){0,40}^ max_poll_transport_failures[ \t]*=[ \t]*[1-9][0-9]*[^\n]*\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )if[ \t]+![^\n]*\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|^ done\b)^ {12,}[^\n]*\n)){0,80}^ {12}(?! )if[ \t]+![^\n]*\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b[^\n]*\n(?>(?:(?!^ {14}(?! )(?P=hist_transport_counter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=hist_transport_counter)[ \t]*\+[ \t]*1[ \t]*\)\)|^ done\b)^ {14,}[^\n]*\n)){0,20}^ {14}(?! )(?P=hist_transport_counter)[ \t]*=[ \t]*\$\(\([ \t]*(?P=hist_transport_counter)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {14}(?! )if[^\n]*\$(?P=hist_transport_counter)[^\n]*-ge[^\n]*\$max_poll_transport_failures\b[^\n]*;[ \t]*then|^ done\b)^ {14,}[^\n]*\n)){0,12}^ {14}(?! )if[^\n]*\$(?P=hist_transport_counter)[^\n]*-ge[^\n]*\$max_poll_transport_failures\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {16}(?! )exit[ \t]+[1-9][0-9]*\b)^ {16,}[^\n]*\n)){0,8}^ {16}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n(?>(?:(?!^ {12}(?! )sleep(?:[ \t]+|$)|^ done\b)^ {12,}[^\n]*\n)){0,80}^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?>(?:(?!^ done\b)^ {12,}[^\n]*\n)){0,80}^ done\b)(?>(?:(?!^ max_poll_transport_failures)^ {10,}[^\n]*\n)){0,80}^ max_poll_transport_failures[ \t]*=[ \t]*[1-9][0-9]*[^\n]*\n(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\))^ {10,}[^\n]*\n)){0,80}^ (?P<hist_deadline_any>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*\$\(\([^\n]*\bdate\b[^\n]*\+%s[^\n]*\+[ \t]*[1-9][0-9]*[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-[gl]e[^\n]*\$(?P=hist_deadline_any)\b[^\n]*;[ \t]*then)^ {12,}[^\n]*\n)){0,20}^ {12}(?! )if[^\n]*\bdate\b[^\n]*\+%s[^\n]*-[gl]e[^\n]*\$(?P=hist_deadline_any)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,80}^ (?P<hist_counter_a>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<hist_limit_a>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=hist_counter_a)[ \t]*=[ \t]*\$\(\([ \t]*(?P=hist_counter_a)[ \t]*\+[ \t]*1[ \t]*\)\))^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=hist_counter_a)[ \t]*=[ \t]*\$\(\([ \t]*(?P=hist_counter_a)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=hist_counter_a)[^\n]*-ge[^\n]*\$(?P=hist_limit_a)\b[^\n]*;[ \t]*then)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=hist_counter_a)[^\n]*-ge[^\n]*\$(?P=hist_limit_a)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?!(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,80}^ (?P<hist_limit_b>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*[1-9][0-9]*[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ [A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?$)^ {10,}[^\n]*\n)){0,40}^ (?P<hist_counter_b>[A-Za-z_][A-Za-z0-9_]*)[ \t]*=[ \t]*0[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,40}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12}(?! )(?P=hist_counter_b)[ \t]*=[ \t]*\$\(\([ \t]*(?P=hist_counter_b)[ \t]*\+[ \t]*1[ \t]*\)\))^ {12,}[^\n]*\n)){0,20}^ {12}(?! )(?P=hist_counter_b)[ \t]*=[ \t]*\$\(\([ \t]*(?P=hist_counter_b)[ \t]*\+[ \t]*1[ \t]*\)\)[ \t]*(?:#[^\n]*)?\n(?>(?:(?!^ {12}(?! )if[^\n]*\$(?P=hist_counter_b)[^\n]*-ge[^\n]*\$(?P=hist_limit_b)\b[^\n]*;[ \t]*then)^ {12,}[^\n]*\n)){0,12}^ {12}(?! )if[^\n]*\$(?P=hist_counter_b)[^\n]*-ge[^\n]*\$(?P=hist_limit_b)\b[^\n]*;[ \t]*then[^\n]*\n(?>(?:(?!^ {14}(?! )exit[ \t]+[1-9][0-9]*\b)^ {14,}[^\n]*\n)){0,8}^ {14}(?! )exit[ \t]+[1-9][0-9]*\b[^\n]*\n)(?>(?:(?!^ while[ \t])^ {10,}[^\n]*\n)){0,80}^ while[ \t]+(?::|true)[ \t]*;[ \t]*do[^\n]*\n(?>(?:(?!^ {12,}(?:(?:if[ \t]+![ \t]+)?(?:[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*)?["'']?\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:if[ \t]+![ \t]+)?(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b))^ {12,}[^\n]*\n)){0,80}^ {12,}(?:(?:if[ \t]+![ \t]+)?(?:[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*)?["'']?\$\([ \t]*(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b|(?:if[ \t]+![ \t]+)?(?:timeout[ \t]+\S+[ \t]+)?gh[ \t]+api\b)[^\n]*\n(?!(?>^ {12}[^\n]*\n){0,80}^ {12}(?:break(?:[ \t]+[1-9][0-9]*)?|exit(?:[ \t]+0)?)[ \t]*(?:#[^\n]*)?$\n)(?>(?:(?!^ {12}(?! )sleep(?:[ \t]+|$))^ {12,}[^\n]*\n)){0,80}^ {12}(?! )sleep(?:[ \t]+|$)[^\n]*\n(?>(?:(?!^ done\b)^ {12,}[^\n]*\n)){0,80}^ done\b' |
There was a problem hiding this comment.
🟡 Constant timeout expressions trigger blockers
A positive timeout written as ${{ 20 }} does not match the literal-only timeout-minutes exclusion. Bounded jobs can receive HIGH polling findings.
Prompt for agents
Extend the owning-job timeout exclusion consistently across all four polling detector files to accept statically positive GitHub Actions constant expressions, while continuing to reject zero, negative, empty, or dynamically unprovable expressions. Add _scan_file regressions for each detector family member and ensure sibling-job timeouts still cannot suppress a finding.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
RED를 먼저 고정했습니다. d50f49ccea1cbf2aecc6da268850fddfc80db3b6에서 owning-job timeout-minutes: ${{ 20 }}를 네 polling family member 각각의 bounded negative로 실행하고, ${{ 0 }}, ${{ -1 }}, ${{ inputs.timeout }}는 unproved timeout이라 generic vulnerable loop를 suppress하지 못한다는 control도 추가했습니다. 현재 literal-only matcher는 아직 이 계약을 만족하지 않으므로 unresolved 유지합니다. GREEN은 네 detector 모두 statically positive integer constant expression만 owning-job hard bound로 인정하고 zero/negative/empty/dynamic 및 sibling-job timeout은 계속 거부하는 exact-head regression evidence입니다.
| review_poll_failures=0 | ||
| max_poll_transport_failures=3 | ||
| while :; do | ||
| if ! reviews="$(gh api repos/example/repo/pulls/1/reviews)"; then | ||
| review_poll_failures=$((review_poll_failures + 1)) | ||
| if [ "$review_poll_failures" -ge "$max_poll_transport_failures" ]; then | ||
| exit 1 | ||
| fi | ||
| continue | ||
| fi | ||
| review_poll_failures=0 | ||
| [ -n "$reviews" ] && break | ||
| sleep 30 |
There was a problem hiding this comment.
| review_poll_failures=0 | ||
| max_poll_transport_failures=3 | ||
| poll_deadline_epoch=$(( $(date -u +%s) + 60 )) | ||
| while :; do | ||
| if ! reviews="$(gh api repos/example/repo/pulls/1/reviews)"; then | ||
| review_poll_failures=$((review_poll_failures + 1)) | ||
| if [ "$review_poll_failures" -ge "$max_poll_transport_failures" ]; then | ||
| exit 1 | ||
| fi | ||
| continue | ||
| fi | ||
| review_poll_failures=0 | ||
| [ -n "$reviews" ] && break | ||
| sleep 30 |
There was a problem hiding this comment.
Goal
Close AppGuardrail issue #1087 by converting a verified
ContextualWisdomLab/.githubrequired-review runner-capacity defect into executable AppGuardrail detector evidence, while preserving the protected canonical-owner repair and explicit false-positive/false-negative boundaries.Current exact candidate:
b34670b8130f1857a8e53e6baf7cd5933826da29ondevelop@e71d37e7c58118e6764c96ab7c4492fe33eed6f8.Status: not merge-ready. Two current detector-precision obligations are pinned RED, and current-head required execution is not terminal GREEN.
Verified causal incident
Protected predecessor
ContextualWisdomLab/.github@5c561a65cca3b925d533e4b40c5c3ac00f16524ehad awhile :verdict poll whosemax_poll_transport_failuresbudget counted failedgh apitransports only. Healthy API/no-current-head-verdict iterations could therefore retain a runner indefinitely relative to repository control flow.The canonical owner wall-clock repair is protected as
ContextualWisdomLab/.github@e29302c05eade7da7b0bdbb453e53980bc9d577b..githubowns runner/review workflow behavior; AppGuardrail owns reusable detection evidence and does not copy owner source.Executable detector family
The #1087 family retains four packaged HIGH/CWE-400 identities:
github-actions-transport-only-poll-bound: historical transport-budget incident shape;github-actions-transport-failure-budget-poll-bound: renamed/identifier-agnostic transport-budget companion;github-actions-poll-bound-state-reset: apparent total bound neutralized by reviewed non-convergent state mutation;github-actions-poll-bound-unreachable-exit: apparent total bound whose fail-closed exit is unreachable because a directly reachable validcontinuetransfers control first.The historical detector now requires causal transport-budget data flow, not a setting-name hit: zeroed failure counter → failed executable
gh apibranch → counter increment → threshold comparison against the positive transport budget → nonzero exit. Test-firstdf4ff1c3f724764b1d047b2cb95ef491c096356c, productiona467678c7e3b4bedca8092eec28072fdd0aae90a, and fixture repair5d87c36251391e5f0254eb915ca7f3068a22b21cestablish that contract.Current RED obligations
Review exposed two additional blocker-class false-positive boundaries.
d50f49ccea1cbf2aecc6da268850fddfc80db3b6pins both as production_scan_fileregressions before production repair:-gtis finite just like-ge. Regressions cover historical and renamed transport forms and both total-attempt declaration orders. Reversed/non-expiring comparisons must remain positive findings.timeout-minutes: ${{ 20 }}is a statically positive hard bound. Regressions cover all four polling detector identities.${{ 0 }},${{ -1 }},${{ inputs.timeout }}, empty/unproved values, and sibling-job timeouts must not suppress findings.Production regex semantics have not yet been changed for these two RED contracts, so their review threads remain unresolved.
docs/product-technical-gap-baseline.mdwas created and updated throughb34670b8130f1857a8e53e6baf7cd5933826da29;docs/TRACEABILITY.mdwas updated through93f953a692bc5f93d8036d2f9f17aa1f196aafd9to keep the bounded detector grammar and owner evidence explicit.Product/architecture boundary
The regex family remains intentionally bounded to conventional GitHub Actions job syntax and literal shell blocks. Composite actions, generated/non-shell control flow, cross-file state, materially different loop frameworks, and general shell block ownership remain an explicit structural GitHub Actions + shell analyzer Gap. Current rule IDs and regression corpus are migration oracles; additional regex must not be presented as universal shell semantics.
Exact-head evidence state
Every source/doc commit invalidates predecessor evidence. On exact head
b34670b8130f1857a8e53e6baf7cd5933826da29at the last fresh read:CodeQL PRrun33682765699completedstartup_failure; its jobs endpoint returnedjobs=[], so no source CodeQL job materialized;33682763772, Security Process33682763817, Security Scan33682763902, SAST Semgrep33682763880, Pinned HTTPS33682763847, OpenSSF Evidence33682763869, Retention Audit33682763996, Scan path context33682763877, OSV33682764647, and Scorecard33682763970were queued;.github#712rather than patched around in this leaf repository.No queued/startup-failure run is source GREEN, and no predecessor success transfers.
Acceptance
Repair the two current RED precision contracts, then merge only through ordinary protection on an unchanged exact head after production regressions pass, all live current-head review findings are reconciled, required Tests/security/SAST/coverage/code-scanning lanes are terminal-success, a qualifying independent non-author review satisfies protection, and the protected base still accepts the candidate. Do not use local regex probes, author replies, startup failures, predecessor checks, self-approval, status coercion, force-push, or protection bypass as acceptance.
Refs #1087.