Skip to content

feat(sandbox): add agt-sandbox AppArmor profile for command-denylist enforcement#3114

Open
qubeena07 wants to merge 5 commits into
microsoft:mainfrom
qubeena07:feat/3068-apparmor-sandbox-profile
Open

feat(sandbox): add agt-sandbox AppArmor profile for command-denylist enforcement#3114
qubeena07 wants to merge 5 commits into
microsoft:mainfrom
qubeena07:feat/3068-apparmor-sandbox-profile

Conversation

@qubeena07

@qubeena07 qubeena07 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Closes #3068.

Summary

  • Adds docker/apparmor/agt-sandbox — a custom AppArmor profile that denies exec of dangerous network, cloud, and infra CLIs at the kernel level (option 3 of the command-denylist stack from feat: command denylist enforcement inside sandbox execution #2662)
  • DockerSandboxProvider probes /sys/kernel/security/apparmor/profiles at container creation and applies agt-sandbox when loaded, falling back to docker-default on hosts where AppArmor is unavailable (macOS / Docker Desktop)
  • Adds _apparmor_profile_loaded() helper and _APPARMOR_PROFILE_NAME class constant

Relationship to other layers

Layer Mechanism Issue
Option 1 Minimal-PATH hardened image — CLIs unreachable by name #2713
Option 2 Logging shim — by-name and absolute-path attempts logged #3019
Option 3 AppArmor profile — kernel-level exec deny, fires even on bind-mounted binaries this PR

Test plan

  • TestAppArmorProfileLoaded — 4 tests: profile present/absent/OSError/no-partial-match
  • TestAppArmorProfileSelection — 3 tests: custom profile used when loaded, fallback to docker-default, constant value
  • Existing TestContainerCreationHardening::test_hardening_flags and TestHardeningAdditions::test_security_opt_includes_seccomp_and_apparmor still pass (fallback path, no AppArmor on macOS CI)
  • Full test_docker_sandbox.py suite: 226 passed

Install the profile (one-time, requires root on the Docker host)

sudo apparmor_parser -r -W agent-governance-python/agent-sandbox/docker/apparmor/agt-sandbox

@github-actions

github-actions Bot commented Jun 18, 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 Added _APPARMOR_PROFILE_NAME constant to DockerSandboxProvider. Any code relying on the absence of this constant may break if it conflicts with existing code or assumptions.
High Added _apparmor_profile_loaded function to agent_sandbox.docker_provider.provider. Any code that defines a function with the same name in the same module may encounter conflicts.
High Modified _create_container method in DockerSandboxProvider to conditionally apply the agt-sandbox AppArmor profile. Changes the behavior of container creation, potentially impacting existing workflows that rely on the previous behavior.

@github-actions github-actions Bot added the size/L Large PR (< 500 lines) label Jun 18, 2026
@github-actions

github-actions Bot commented Jun 18, 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

  • README.md -- section on AppArmor profiles needs update to include agt-sandbox.
  • CHANGELOG.md -- missing entry for the addition of the agt-sandbox AppArmor profile and related functionality.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
🤖 AI Agent: test-generator — `agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py`

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

agent-governance-python/agent-sandbox/src/agent_sandbox/docker_provider/provider.py

  • test_apparmor_profile_not_applied_on_error -- Validate that _create_container does not apply the AppArmor profile if _apparmor_profile_loaded raises an unexpected exception.
  • test_apparmor_profile_name_constant -- Ensure _APPARMOR_PROFILE_NAME is correctly set to "agt-sandbox" and used consistently.

agent-governance-python/agent-sandbox/docker/apparmor/agt-sandbox

  • test_apparmor_profile_denylist_completeness -- Verify that all binaries in DENIED_LOGGED_BIN_NAMES from Dockerfile.sandbox are included in the AppArmor profile's deny rules.
  • test_apparmor_profile_no_unexpected_allows -- Ensure no unintended allow rules override the denylist in the AppArmor profile.

@github-actions

github-actions Bot commented Jun 18, 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 introduces a robust AppArmor profile for kernel-level command-denylist enforcement, with proper fallback mechanisms and tests. One improvement is suggested for better test coverage.

# Sev Issue Where
1 Warn Test coverage for _apparmor_profile_loaded does not include a case for malformed AppArmor profile entries. test_docker_sandbox.py

Action items:

  1. Add a test case for _apparmor_profile_loaded to handle malformed AppArmor profile entries (e.g., lines with unexpected formats or missing fields). Fine as follow-up PR.

@github-actions

github-actions Bot commented Jun 18, 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

Copy link
Copy Markdown

🟡 Contributor Check: MEDIUM

Check Result
Profile MEDIUM
Credential LOW
Overall MEDIUM

Automated check by AGT Contributor Check.

@github-actions github-actions Bot added the needs-review:MEDIUM Contributor check flagged MEDIUM risk label Jun 18, 2026
@github-actions

github-actions Bot commented Jun 18, 2026

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.

Closes microsoft#3012.

The existing CONTEXT_RULES covered first-person social-engineering phrases
but had no coverage for indirect / content-channel injection — payloads
that arrive inside retrieved content (web pages, emails, RAG documents,
tool results) and address the model directly.

Four new rules added to CONTEXT_RULES in the `context:` family:

- `context:instructions_for_ai_reading` (High / 0.90) — "instructions
  for the AI/assistant/model/LLM reading/processing/analyzing this"
- `context:system_note_to_assistant` (High / 0.90) — "system note:" or
  "note to the assistant/AI/model/LLM:" impersonating a system channel
- `context:embedded_tool_directive` (Medium / 0.75) — HTML-comment-
  smuggled directive addressed to the AI (<!-- ai: … -->)
- `context:retrieved_doc_override` (High / 0.90) — "this document/page/
  email/file/result instructs you to ignore/override/reveal/…"

All patterns use the Rust `regex` crate (no lookarounds, no backrefs)
and match after normalize_for_detection(), consistent with the existing
corpus. All four IDs are registered as known built-in rule IDs so they
can be disabled via BuiltInRuleOverrides::disable.

Integration tests added for each rule: positive matches across wording
variants, benign inputs that must not fire, and a round-trip test
verifying each ID is accepted by validate_disable_list.
…enforcement (microsoft#3068)

Implements option 3 of the command-denylist stack from microsoft#2662. Adds a custom
AppArmor profile (docker/apparmor/agt-sandbox) that denies exec of dangerous
network, cloud, and infra CLIs at the kernel level — complementing the
minimal-PATH image (option 1) and logging shim (option 2) already in place.

DockerSandboxProvider probes /sys/kernel/security/apparmor/profiles at
container creation time and applies the custom profile when loaded, falling
back to docker-default transparently on hosts where AppArmor is unavailable
(e.g. macOS / Docker Desktop).
@qubeena07 qubeena07 force-pushed the feat/3068-apparmor-sandbox-profile branch from 0c6747c to d3174a6 Compare June 18, 2026 18:30
imran-siddique
imran-siddique previously approved these changes Jun 19, 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.

LGTM. AppArmor profile structure is correct (deny rules override the broad file allow), DockerSandboxProvider fallback is clean and tested, and the Rust prompt-injection improvements are well-covered. Minor: Python-based CLI entry points and local bin paths aren't covered by the profile, but that's expected for a defense-in-depth layer and is noted in the PR. Closing #3068.

@imran-siddique

Copy link
Copy Markdown
Collaborator

Reviewed and approved -- LGTM. The CI suite hasn't run yet for this PR (fork contributor first-run protection). Could you push a trivial update (e.g. add a blank line to the AppArmor profile comment block) so CI triggers? Once the test suite is green we can merge.

@qubeena07

Copy link
Copy Markdown
Contributor Author

Done, pushed a trivial doc note to the AppArmor profile comment block to unblock CI. Will update once the test suite passes.

@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.

Re-approving after qubeena07's latest push. Profile correctly denies exec of network fetch tools, SSH, git, cloud CLIs, and k8s/terraform/helm. Fallback to docker-default is safe. Approved.

@imran-siddique

Copy link
Copy Markdown
Collaborator

@MohammadHaroonAbuomar -- AppArmor profile for sandbox command denylist. Re-approved on my end after qubeena07's latest push. Waiting on your approval to merge.

@imran-siddique

Copy link
Copy Markdown
Collaborator

@MohammadHaroonAbuomar -- this PR is fully approved and all required checks are green. Could you merge it when you get a chance?

Side note: my merge access stopped working -- looks like I was removed from the microsoft/agent-governance-toolkit maintainer team when my Microsoft account was offboarded, so my permissions dropped from maintain to push. If you can add me back to the team (or re-add as a direct collaborator with maintain role), that would unblock me from merging on my own going forward. Otherwise I'll need to route these through you for now.

@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.

Solid implementation of option 3 in the command-denylist stack. The _apparmor_profile_loaded helper is correct: reads the kernel sysfs file, splits each line safely, returns False on any OSError so macOS / Docker Desktop hosts fall through cleanly.

The profile mirrors docker-default for base rules and adds targeted deny ... x, blocks covering the same binary set as DENIED_LOGGED_BIN_NAMES in Dockerfile.sandbox. The three-layer stack (minimal PATH + shim + AppArmor) is well thought out and the in-profile comments explain the relationship clearly.

The Rust side (indirect injection rules) tightens the patterns in a compatible, non-breaking way: requiring this at the end of instructions_for_ai_reading, adding llm to system_note_to_assistant, adding model to the HTML-comment variant, and expanding retrieved_doc_override to cover article/text/result as document nouns and the ai/assistant/model as addressees. Test coverage is comprehensive and the benign-not-flagged tests prevent over-trigger regressions.

Minor nit: line.split() is called twice per line in _apparmor_profile_loaded. Not a bug, but splitting once into a local variable would be marginally cleaner. Not a blocker.

LGTM.

imran-siddique
imran-siddique previously approved these changes Jun 22, 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.

Two additions that complement each other: AppArmor kernel-level enforcement + new prompt injection detection rules.

AppArmor profile:

  • The deny rules correctly target both named and absolute-path invocations. AppArmor deny rules fire before allow rules, so the broad file, allow above doesn't bypass the denies -- this is correct behavior.
  • The _apparmor_profile_loaded() probe approach (reading from /sys/kernel/security/apparmor/profiles) and the graceful fallback to docker-default are the right design for a multi-platform tool.
  • Profile structure mirrors docker-default (moby/moby), which means security updates to docker-default can be ported here systematically.

Prompt injection Rust additions:

  • The four new indirect injection rule IDs (context:instructions_for_ai_reading, context:system_note_to_assistant, context:embedded_tool_directive, context:retrieved_doc_override) cover important attack vectors in RAG/agentic contexts.
  • The benign test cases (ordinary HTML comments, documents describing their own purpose) prevent false positives.
  • new_indirect_rules_are_known_built_in_ids test pins each rule ID in the registry, which catches typos or registration gaps.

Only the maintainer gate is failing.

Split the line once into a local variable instead of calling split() twice on the same string.
@qubeena07

Copy link
Copy Markdown
Contributor Author

Fixed the nit from the review. Split the line once into a local variable instead of calling line.split() twice. Pushed to the branch.

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

Labels

needs-review:MEDIUM Contributor check flagged MEDIUM risk size/L Large PR (< 500 lines) tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Track AppArmor/SELinux denylist enforcement for sandbox execution (option 3)

2 participants