Skip to content

fix(bridge): close GLOB blocked-pattern fail-open via RE2-compatible translation#3166

Open
Haruka-Kaya wants to merge 1 commit into
microsoft:mainfrom
Haruka-Kaya:fix/3137-bridge-robustness
Open

fix(bridge): close GLOB blocked-pattern fail-open via RE2-compatible translation#3166
Haruka-Kaya wants to merge 1 commit into
microsoft:mainfrom
Haruka-Kaya:fix/3137-bridge-robustness

Conversation

@Haruka-Kaya

Copy link
Copy Markdown

Addresses the policies/bridge.py findings from the robustness review #3137 (per @imran-siddique's request to track fixes as separate per-file PRs).

Fail-open: GLOB blocked patterns never matched

_pattern_to_regex translated a v4 GLOB blocked pattern with
fnmatch.translate, whose output ((?s:.*\.exe)\Z) is Python-only regex
syntax. Go RE2 — the engine behind OPA's agt.patterns — rejects the \Z
anchor and the inline (?s:...) flag group, so regex.match went undefined
and the deny rule silently failed open. This contradicts the _pattern_to_regex
docstring ("emits a Go RE2 regex literal in every case") and fails open in a
policy-enforcement layer (ADR-0013).

Empirically confirmed under OPA 0.70.0 / Go RE2:

OLD fnmatch.translate('*.exe') = '(?s:.*\.exe)\Z'  -> regex.match: UNDEFINED (rule fails open)
NEW _glob_to_re2('*.exe')      = '(?s)^.*\.exe$'    -> regex.match: matches correctly

Fix

  • Add _glob_to_re2: translates * / ? / [seq] / [!seq] to RE2-safe
    syntax, anchored whole-string with (?s)^...$. Whole-string anchoring
    preserves v4 glob semantics under agt.patterns' unanchored regex.match.

Two smaller robustness fixes in the same file

  • Non-finite confidence_threshold. inf slipped past the > 0 guard and
    rendered deny_if_low_confidence(Infinity) — invalid Rego/JSON that fails to
    compile; nan was silently dropped (a threshold the caller set but got no
    deny for). Now rejected up front in governance_to_acs_manifest, with
    json.dumps(..., allow_nan=False) as a backstop in _render_rego.
  • Orphaned temp bundle dir. Pattern translation / render / write are now
    wrapped so a failure removes a self-created agt_bridge_* temp dir instead
    of leaving a half-built bundle (stock libs copied, generated module missing).
    A caller-supplied bundle_dir is left untouched.

Why the existing suite missed this

test_bridge.py includes a ("*.exe", PatternType.GLOB) entry but only
string-checks the generated Rego body; the OPA-running test used a SUBSTRING
pattern. No test ever compiled a GLOB pattern under OPA, so the fail-open went
undetected.

Tests

Adds tests/test_bridge_pattern.py (no agent_os dependency): RE2-safety of the
translation, an OPA-backed regression test that compiles the GLOB regex under
real Go RE2 (skips if opa is absent), non-finite-threshold rejection, and
temp-dir cleanup on failure.

Verified locally against the real ACS native runtime (built from
policy-engine/sdk/python) + OPA 0.70.0:

platform linux -- Python 3.13.14, pytest-9.0.3
tests/test_bridge_pattern.py  (15 passed)
tests/test_bridge.py          (21 passed)
tests/test_runtime.py         (23 passed)

Refs #3137

🤖 Generated with Claude Code

@github-actions github-actions Bot added tests size/L Large PR (< 500 lines) labels Jun 24, 2026
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
🤖 AI Agent: docs-sync-checker — Docs Sync

AI-generated review output. Treat it as untrusted analysis and verify before acting.

Docs Sync

  • _glob_to_re2() in bridge.py -- missing docstring in the CHANGELOG for the new public API addition.
  • README.md -- no updates found reflecting the changes to pattern translation or robustness fixes.
  • CHANGELOG -- missing entry for the behavioral changes introduced in this PR.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
🤖 AI Agent: breaking-change-detector — API Compatibility

AI-generated review output. Treat it as untrusted analysis and verify before acting.

API Compatibility

Severity Change Impact
High Changed _pattern_to_regex to use _glob_to_re2 instead of fnmatch.translate. Existing code relying on the previous behavior of _pattern_to_regex may break if it expected Python-specific regex constructs.
High Added validation to reject non-finite confidence_threshold values in governance_to_acs_manifest. Code that previously passed non-finite values (e.g., inf or nan) to confidence_threshold will now raise a ValueError.
Medium Changed behavior of governance_to_acs_manifest to delete self-created temporary directories on failure. May affect workflows relying on the existence of partially created temporary directories after a failure.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
🤖 AI Agent: code-reviewer — Action Items:

AI-generated review output. Treat it as untrusted analysis and verify before acting.

TL;DR: 0 blockers, 1 warning. The PR improves robustness and fixes critical fail-open issues but introduces a minor test dependency concern.

# Sev Issue Where
1 Warn Test relies on external opa binary, which may cause CI instability. tests/test_bridge_pattern.py

Action Items:

  • None (no blockers found).

Warnings (fine as follow-up PRs):

  • Ensure the opa binary dependency in tests/test_bridge_pattern.py is managed or mocked to avoid CI failures due to missing external dependencies.

@github-actions

Copy link
Copy Markdown
🤖 AI Agent: contributor-guide — Actionable Items:

AI-generated review output. Treat it as untrusted analysis and verify before acting.

Welcome, and thank you for your detailed contribution! Great job on the comprehensive explanation and adding robust test coverage.

Actionable Items:

  1. The _glob_to_re2 function and its usage look solid, but ensure that all edge cases for GLOB patterns (e.g., unmatched brackets) are covered in the new tests.
  2. Verify that the added tests in test_bridge_pattern.py are integrated into the CI pipeline to ensure they run automatically.

For more guidance, see CONTRIBUTING.md. Let us know if you need help!

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
🤖 AI Agent: security-scanner — View details

AI-generated review output. Treat it as untrusted analysis and verify before acting.

No security issues found.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
🤖 AI Agent: test-generator — `agent-governance-python/agt-policies/src/agt/policies/bridge.py`

AI-generated review output. Treat it as untrusted analysis and verify before acting.

agent-governance-python/agt-policies/src/agt/policies/bridge.py

  • test_glob_to_re2_invalid_patterns -- Validate that _glob_to_re2 handles invalid or malformed glob patterns correctly.
  • test_governance_to_acs_manifest_temp_dir_cleanup -- Ensure temporary directories created by governance_to_acs_manifest are cleaned up on failure.
  • test_governance_to_acs_manifest_confidence_threshold -- Test that non-finite confidence_threshold values (e.g., inf, nan) are rejected.

Test coverage for other changes appears sufficient.

@github-actions

Copy link
Copy Markdown

PR Review Summary

Check Status Details
🔍 Code Review ⚠️ Missing No current-run comment
🛡️ Security Scan ⚠️ Missing No current-run comment
🔄 Breaking Changes ⚠️ Missing No current-run comment
📝 Docs Sync ⚠️ Missing No current-run comment
🧪 Test Coverage ⚠️ Missing No current-run comment

Verdict: ⚠️ AI review incomplete; ready for human review

AI review comments are untrusted advisory output. The summary reports workflow-generated completion status only, not model-authored pass/fail claims.

Haruka-Kaya added a commit to Haruka-Kaya/agent-governance-toolkit that referenced this pull request Jun 25, 2026
AI レビュー警告(テストカバレッジ)対応。_glob_to_re2 の出力を Python の
参照実装 fnmatch.fnmatchcase と突き合わせる cross-check を追加:
否定文字クラス [!ab]、メタ文字のリテラル扱い(. + のエスケープ)、未閉じ
'[' のリテラル化、複数 * / ? を含むケース。挙動が glob セマンティクスを
保ったまま RE2 安全であることを保証。

Refs microsoft#3137

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VT2CfGX3tmPsQaofuuWwED
imran-siddique
imran-siddique previously approved these changes Jun 25, 2026

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR fixes three real bugs in bridge.py: a security-relevant fail-open in GLOB pattern matching, invalid Rego/JSON emission for non-finite confidence thresholds, and orphaned temp directories on failure. The fixes are correct and the new tests are thorough.\n\nCore fix - _glob_to_re2: The implementation is correct. I traced every branch manually and ran all the edge cases (unclosed brackets, negated classes, ranges, escaped chars, [^...]). All results agree with fnmatch.fnmatchcase semantics, which is the right reference. The RE2-safe output ((?s)^...$) is confirmed to avoid \Z and (?s:...).\n\nNon-finite threshold guard: math.isfinite correctly rejects inf, -inf, and nan before they reach _render_rego. The allow_nan=False backstop in _render_rego is correctly described as a defensive measure for direct callers, not redundant.\n\nTemp dir cleanup: The created_bundle_dir flag logic is sound. The mkdir call sits outside the try block, but that is safe because mkdtemp already created the directory, so mkdir(exist_ok=True) cannot fail on it.\n\nOne minor observation (no action needed): Python's re module treats $ as matching end-of-string OR before a trailing \n, while Go RE2's $ without (?m) matches end-of-text only. This means the Python-only unit tests using re.match have a slight semantic gap vs. OPA for inputs with trailing newlines. In practice this is not exploitable (policy text does not end with \n) and the OPA-backed regression test covers the real runtime. Worth knowing about but not blocking.\n\nAll required CI gates pass. The OPA regression test correctly skips when the opa binary is absent rather than failing. The test coverage for the cleanup path (test_created_bundle_dir_cleaned_up_on_failure) properly exercises the monkeypatched mkdtemp path.

@MohammadHaroonAbuomar

Copy link
Copy Markdown
Collaborator

@Haruka-Kaya thanks a lot for making this change. One request is to translate the commit message to English so others can read it easily. Otherwise the change itself looks good!

@Haruka-Kaya Haruka-Kaya force-pushed the fix/3137-bridge-robustness branch 2 times, most recently from ec04889 to 5bd8042 Compare June 25, 2026 19:36
@Haruka-Kaya

Copy link
Copy Markdown
Author

@MohammadHaroonAbuomar done — the commit message is now in English (squashed to a single commit). Heads-up: while doing that I force-pushed and my push raced with your Merge branch 'main' commit on this branch, so I rebased my change onto the current origin/main (d3c2a29) instead — the branch is up to date with main and the diff is unchanged (only bridge.py + tests/test_bridge_pattern.py). Sorry for the churn; I've stopped force-pushing now. Happy to re-request review when you're ready.

@MohammadHaroonAbuomar MohammadHaroonAbuomar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is green and the new test_glob_to_re2_agrees_with_fnmatchcase is a good cross-check. The PR title still says "(fail-open)", which reads as if the change introduces fail-open behaviour; it eliminates one. Please retitle to e.g. fix(bridge): close GLOB blocked-pattern fail-open via RE2-compatible translation. The optional [[:posix:]] hardening (inner.replace("[", r"\[")) is still open if you want to take it; non-blocking.

…dle robustness

Addresses the policies/bridge.py findings from review microsoft#3137.

- GLOB fail-open: _pattern_to_regex translated GLOB patterns with
  fnmatch.translate, whose output (e.g. "(?s:.*\.exe)\Z") uses Python-only
  syntax that Go RE2 (OPA) rejects, leaving regex.match undefined so the GLOB
  deny silently failed open. Add _glob_to_re2 (translates * / ? / [seq] /
  [!seq] to RE2-safe syntax, anchored whole-string with (?s)^...$).
- Reject non-finite confidence_threshold up front (inf rendered invalid Rego;
  nan was silently dropped); json.dumps uses allow_nan=False.
- Clean up a self-created temp bundle dir if materialization fails.

Adds tests/test_bridge_pattern.py (no agent_os dependency) including an
OPA-backed regression test that compiles the GLOB regex under real RE2 and a
fnmatchcase cross-check across edge cases. Verified with OPA 0.70.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VT2CfGX3tmPsQaofuuWwED

Signed-off-by: Himanaraba <kayaharuka@hotmail.com>
@Haruka-Kaya Haruka-Kaya force-pushed the fix/3137-bridge-robustness branch from 5834c6e to 4a70053 Compare June 25, 2026 20:57
@Haruka-Kaya Haruka-Kaya changed the title fix(bridge): translate GLOB blocked patterns to RE2 (fail-open) + bundle robustness fix(bridge): close GLOB blocked-pattern fail-open via RE2-compatible translation Jun 25, 2026
@Haruka-Kaya

Copy link
Copy Markdown
Author

@MohammadHaroonAbuomar good point — retitled to "fix(bridge): close GLOB blocked-pattern fail-open via RE2-compatible translation" so it reads as eliminating the fail-open, not introducing one. I'll leave the optional [[-literal hardening as a follow-up since glob char-classes already round-trip correctly through _glob_to_re2 (the fnmatchcase cross-check covers [ab]/[!ab]).

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

Labels

size/L Large PR (< 500 lines) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants