Skip to content

[AAASM-3107] 🔒 (adapters): Fail closed on unknown/None decision under enforce#139

Merged
Chisanan232 merged 7 commits into
masterfrom
v0.0.1/AAASM-3107/normalize_fail_closed
Jun 17, 2026
Merged

[AAASM-3107] 🔒 (adapters): Fail closed on unknown/None decision under enforce#139
Chisanan232 merged 7 commits into
masterfrom
v0.0.1/AAASM-3107/normalize_fail_closed

Conversation

@Chisanan232

Copy link
Copy Markdown
Contributor

Description

_normalize_decision in the LangChain, CrewAI, and MCP adapters mapped any
unknown / None / malformed governance verdict to allow, so a denied
action could slip through the SDK interception layer. This PR makes those
helpers fail closed when the agent is in enforce posture: an unrecognized,
None, or malformed verdict now maps to deny. Under observe / disabled
(and when no native runtime authority is engaged) the helpers still fail open,
preserving the dry-run / hermetic behavior.

The fix reuses the enforcement_mode plumbing landed in Wave-1 (AAASM-3106 /
#138): the governance interceptor (RuntimeQueryInterceptor /
_FailClosedInterceptor) already carries an _enforce bool derived from
enforcement_mode == "enforce". Each adapter reads that flag (strictly, via
is True) and threads it into _normalize_decision.

Type of Change

  • 🔧 Bug fix

Breaking Changes

  • No

Behavior only tightens under enforce for verdicts that were never valid
(unknown / None / malformed). Valid allow / deny / pending verdicts and
all observe / disabled behavior are unchanged.

Related Issues

Testing

  • Unit tests added/updated

Added regression tests for each adapter asserting unknown/None/malformed
verdicts deny under enforce and pass through otherwise (LangChain sync +
async, CrewAI, MCP). Full suite: 513 passed, 13 skipped. mypy clean.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Comments added for complex logic
  • All tests passing

Closes AAASM-3107

🤖 Generated with Claude Code

_normalize_decision mapped unknown/None/malformed verdicts to allow,
so a denied action could slip through the SDK layer. Under enforce
(enforcement_mode == "enforce", threaded in by AAASM-3106) such verdicts
now deny; observe/disabled still fail open. Shared by the MCP adapter.

Refs AAASM-3107
AssemblyCallbackHandler._normalize_decision defaulted unknown/None/
malformed verdicts to allow. Under enforce it now denies (reusing the
interceptor's _enforce flag from AAASM-3106); observe/disabled fail open.

Refs AAASM-3107
Thread the enforce posture into the MCP call_tool patch so an unknown/
None/malformed verdict denies under enforce instead of allowing,
forwarding it to the shared crewai _normalize_decision.

Refs AAASM-3107
Sync and async on_tool_start deny unknown/None/malformed verdicts under
enforce and pass them through otherwise.

Refs AAASM-3107
Compare _enforce strictly against True so an interceptor whose __getattr__
synthesizes truthy values for missing attributes is not mistaken for the
enforce posture. The real flag is always a bool on RuntimeQueryInterceptor /
_FailClosedInterceptor.

Refs AAASM-3107
@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud

Copy link
Copy Markdown

@Chisanan232

Copy link
Copy Markdown
Contributor Author

🔎 Claude Code review — Wave 2 security remediation

CI: All checks green (codecov/Sonar advisory, out of scope).
Scope vs AAASM-3107: Fully covers the ticket. All three adapters (crewai/langchain/mcp) now map unknown/None/malformed verdicts → DENY under enforce, reusing the Wave-1 _enforce flag with a strict is True check; observe/disabled still fail open. The ticket named langchain+mcp explicitly; crewai is correctly included as the consistent superset. Per-adapter regression tests present (crewai, langchain sync + async, mcp).
Readiness: Ready to merge — green CI, mypy clean (513 passed / 13 skipped), no code change needed.
Caveats: None.
— Claude Code (automated PR review, 2026-06-17)

@Chisanan232 Chisanan232 merged commit 0886d44 into master Jun 17, 2026
21 checks passed
@Chisanan232 Chisanan232 deleted the v0.0.1/AAASM-3107/normalize_fail_closed branch June 17, 2026 07:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant