feat(plugin): split compliance-posture into 3 plugins (#256)

Closes #256. Per CEO direction, shipping three separate opt-in plugins
instead of one bundled "compliance-posture" — keeps installs granular
so a workspace that only wants CVE scanning doesn't carry OWASP policy
or append-only audit retention.

- plugins/molecule-compliance/        — wraps compliance.py (OWASP OA-01
  prompt injection + OA-03 excessive agency). Skill: owasp-agentic.
- plugins/molecule-audit/              — wraps audit.py (EU AI Act Art.
  12/13/17 append-only JSONL log, SIEM-friendly). Skill: ai-act-audit-log.
- plugins/molecule-security-scan/      — wraps security_scan.py (Snyk or
  pip-audit CVE gate on skill requirements.txt). Skill: skill-cve-gate.

Each plugin ships a manifest + one SKILL.md with:
- When to install / when to skip
- Configuration shape (config.yaml blocks)
- Anti-patterns to avoid
- Cross-references to the other two plugins so an operator can reason
  about the full compliance surface

All three wrap code that already exists in workspace-template/builtin_tools/
— no Python changes. Install per workspace via
POST /workspaces/:id/plugins {"source":"builtin://molecule-<name>"}.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-15 14:15:25 -07:00
parent 9edc576ac9
commit 720e92e426
6 changed files with 401 additions and 0 deletions

View File

@ -0,0 +1,16 @@
name: molecule-audit
version: 1.0.0
description: >
Immutable append-only audit log for EU AI Act compliance (Articles 12/13/17).
Wraps builtin_tools/audit.py — JSON Lines format, SIEM-friendly, write-only.
Opt-in per workspace; usually paired with molecule-compliance.
author: Molecule AI
tags: [audit, compliance, eu-ai-act, logging, siem]
runtimes:
- langgraph
- claude_code
- deepagents
skills:
- ai-act-audit-log

View File

@ -0,0 +1,133 @@
---
name: ai-act-audit-log
description: "Emit immutable audit events for EU AI Act compliance. Use when a workspace performs any action that needs to be legally reconstructable: delegations, approvals, RBAC decisions, memory read/write. JSON Lines, append-only, SIEM-friendly."
---
# EU AI Act Audit Log
Opt-in plugin that activates `builtin_tools/audit.py` — an append-only
JSON Lines log satisfying the record-keeping and transparency obligations
of the EU AI Act (Articles 12, 13, 17) for high-risk AI systems.
## When to install
Install on any workspace that:
- Must satisfy EU AI Act conformity assessment
- Needs a tamper-evident trail of agent decisions for a legal discovery
- Pairs with `molecule-compliance` to record OWASP OA-01 detections and
OA-03 terminations
Skip on disposable dev workspaces — the log fills disk over time and
isn't useful for throwaway agents.
## Event schema
Every line is one JSON object:
```json
{
"timestamp": "2026-04-15T21:30:00.123Z",
"event_type": "delegation",
"workspace_id": "ws-acme-pm-a1b2c3d4",
"actor": "ws-acme-pm-a1b2c3d4",
"action": "delegate",
"resource": "ws-acme-dev-lead-e5f6g7h8",
"outcome": "allowed",
"trace_id": "5e8b2f3c-9a1d-4e7b-8c6f-1234567890ab"
}
```
Required fields:
| Field | Meaning |
|---|---|
| `timestamp` | ISO-8601 UTC with offset — sort key + freshness indicator |
| `event_type` | `delegation` / `approval` / `memory` / `rbac` |
| `workspace_id` | Who generated the event |
| `actor` | Who triggered the action (defaults to workspace_id for automated events; human identity for approval decisions) |
| `action` | Verb: `delegate`, `approve`, `memory.read`, `memory.write`, `rbac.deny` |
| `resource` | Target of the action: another workspace id, memory scope, approval action string |
| `outcome` | `allowed` / `denied` / `success` / `failure` / `timeout` / `requested` / `granted` |
| `trace_id` | UUID v4 correlating related events across workspaces |
## Usage
Call `audit.log_event` from any tool or handler:
```python
from builtin_tools.audit import log_event
log_event(
event_type="delegation",
workspace_id=self.workspace_id,
actor=self.workspace_id,
action="delegate",
resource=target_workspace_id,
outcome="allowed",
trace_id=ctx.trace_id,
)
```
The function is synchronous and fire-and-forget — it opens the log file
in append mode, writes one line, closes. No buffering, no retry. If the
disk is full the call raises `IOError`; the caller decides whether to
surface that (usually yes — an audit gap is a compliance event itself).
## Configuration
Add to `config.yaml`:
```yaml
audit:
enabled: true
log_path: /var/log/molecule/audit.jsonl
max_size_mb: 100 # informational only; rotation is EXTERNAL
retention_days: 365 # informational only; the module never deletes
```
## Rotation (external)
This module is **write-only by design**. It does not rotate, compress,
or delete log lines. Use the host's `logrotate` (Linux) or equivalent:
```
/var/log/molecule/audit.jsonl {
daily
rotate 365
compress
copytruncate # NOT truncate — copytruncate leaves the file open
missingok
notifempty
}
```
`copytruncate` is load-bearing — the Python side holds the file
descriptor open for append, so a rename-based rotation would orphan the
new file and writes would continue to the rotated-away path.
## SIEM ingestion
The JSON Lines format is directly consumable by:
- Splunk (ingest via Universal Forwarder)
- Elastic (Filebeat + JSON decoder)
- Datadog (Agent in JSON mode)
- Self-hosted Loki
One ingestion pipeline per workspace volume. No post-processing needed.
## Anti-patterns
- **Don't** write to the same log path from multiple workspaces on the
same host — races corrupt the JSONL newlines. Use per-workspace paths.
- **Don't** truncate or edit the log. Tamper-evidence is the whole point.
- **Don't** log raw PII or secrets in the `resource` or `outcome` fields.
Use IDs or hashes; the audit story and the GDPR story have to coexist.
- **Don't** skip this on OA-01/OA-03 detections — they're exactly the
events an auditor wants to see.
## Related
- `builtin_tools/audit.py` — the implementation
- `molecule-compliance` — emits OWASP OA-01 / OA-03 events into this log
- `molecule-security-scan` — emits CVE-scan results into this log
- Issue #256 — the proposal that led to this plugin split

View File

@ -0,0 +1,17 @@
name: molecule-compliance
version: 1.0.0
description: >
OWASP Top 10 for Agentic Applications (Dec 2025) compliance enforcement.
Wraps builtin_tools/compliance.py — prompt-injection detection/blocking,
excessive-agency limits (max tool calls + task duration). Opt-in per
workspace via config.yaml compliance block.
author: Molecule AI
tags: [compliance, owasp, security, prompt-injection]
runtimes:
- langgraph
- claude_code
- deepagents
skills:
- owasp-agentic

View File

@ -0,0 +1,91 @@
---
name: owasp-agentic
description: "Enforce OWASP Top 10 for Agentic Applications. Use when a workspace handles untrusted input (user messages, scraped web content, file uploads) or when it would be catastrophic if the agent ran away with unlimited tool calls. Gates prompt injection + excessive agency."
---
# OWASP Agentic Compliance
Opt-in compliance layer that wraps `builtin_tools/compliance.py`. The
Python primitives exist in every runtime image — installing this plugin
activates them via config and documents the policy.
## Coverage
| OWASP ID | Name | Primitive | Default mode |
|---|---|---|---|
| **OA-01** | Prompt Injection | `sanitize_input(text)` | `detect` |
| **OA-03** | Excessive Agency | `check_agency_limits(task_ctx)` | 50 calls / 300s |
## When to install
Install this plugin on any workspace that:
- Accepts free-form user input (chat interfaces, A2A message bodies)
- Scrapes or ingests untrusted web content
- Runs long-horizon tasks where a stuck loop could burn LLM budget
- Must satisfy compliance reviews that cite OWASP Top 10 for AI
## Configuration
Add to `config.yaml`:
```yaml
compliance:
mode: owasp_agentic
prompt_injection: detect # detect → log+pass, block → raise PromptInjectionError
max_tool_calls_per_task: 50 # OA-03 ceiling
max_task_duration_seconds: 300 # OA-03 wall-clock ceiling
```
Modes explained:
- **`detect`** (default) — logs an audit event via `audit.log_event` when a
trigger pattern is found, returns the original text. The agent still
processes the input. Good for rollout: you see what triggers before
committing to blocking.
- **`block`** — raises `PromptInjectionError` before the agent sees the
text. The caller (typically `a2a_executor.py`) catches it and returns a
400-shaped error to the sender.
## Trigger patterns (OA-01)
`sanitize_input` scans for:
- Instruction-override phrases ("ignore previous instructions", "new system prompt")
- Role-hijacking attempts ("you are now", "act as")
- System-prompt delimiter injection (`</s>`, `<|im_start|>`)
- Known jailbreak keywords (rotating list; update via compliance.py)
False positives on legitimate content are expected in `detect` mode —
that's why it's the default. Only flip to `block` after you've reviewed
audit logs for a week and confirmed the hit rate is low.
## Agency limits (OA-03)
Tracks per-task:
- Number of tool calls (`tool_call_count`)
- Elapsed wall-clock time (`started_at → now`)
When either exceeds the configured ceiling, `check_agency_limits` raises
`ExcessiveAgencyError`. The task terminates gracefully — the caller sees
a final message + `status=failed`.
## Anti-patterns
- **Don't** install on workspaces that only process trusted internal
input — the overhead isn't worth it.
- **Don't** set `max_tool_calls_per_task` below 20. Many legitimate
multi-step tasks need 15-30 tool calls; ceilings that low cause false
terminations.
- **Don't** flip `prompt_injection` to `block` without a rollout period.
- **Don't** rely on this as your only defense — it's a cheap policy
layer, not a substitute for proper sandboxing of the agent's
filesystem + network access.
## Related
- `builtin_tools/compliance.py` — the implementation
- `molecule-audit` — audit-log retention for the events this plugin
generates (OA-01 detections, OA-03 terminations). Install both to get
a coherent compliance story.
- `molecule-security-scan` — pre-load CVE gate for skill dependencies
(complements this runtime policy with supply-chain policy).
- Issue #256 — the proposal that led to this plugin split

View File

@ -0,0 +1,16 @@
name: molecule-security-scan
version: 1.0.0
description: >
Supply-chain CVE gate for skill dependencies. Wraps builtin_tools/security_scan.py —
runs Snyk or pip-audit against a skill's requirements.txt before the skill
loads, blocking or warning on critical/high CVEs. Opt-in per workspace.
author: Molecule AI
tags: [security, cve, supply-chain, snyk, pip-audit]
runtimes:
- langgraph
- claude_code
- deepagents
skills:
- skill-cve-gate

View File

@ -0,0 +1,128 @@
---
name: skill-cve-gate
description: "Block or warn on CVE-vulnerable dependencies before a skill loads into the workspace. Use when a workspace installs skills from third-party sources (user-uploaded, marketplace, agentskills.io). Prevents known-bad transitive deps from running in the agent's process."
---
# Skill CVE Gate
Supply-chain risk management for skill dependencies. Wraps
`builtin_tools/security_scan.py`. When a skill is about to load, the
gate runs a CVE scanner against its `requirements.txt` and either
blocks, warns, or skips depending on mode.
## Scanners (auto-selected)
| Scanner | Requires | When selected |
|---|---|---|
| **Snyk CLI** | `snyk` binary in PATH + `SNYK_TOKEN` env | Available — preferred (richer DB + license coverage) |
| **pip-audit** | `pip-audit` binary in PATH | Fallback when Snyk isn't installed |
| **(none)** | — | Neither available → skip with log line |
Selection happens at scan time, per skill. No config flag needed.
## Modes
Configure in `config.yaml`:
```yaml
security_scan:
mode: warn # off | warn | block
```
- **`off`** — skip scanning entirely. Useful in air-gapped CI that has
no network access to CVE databases, or dev loops where you know the
deps are vetted.
- **`warn`** (default) — run the scanner, log a WARNING + audit event
on any critical/high finding, but load the skill anyway. Good for
rollout phase: you see the risk surface without breaking users.
- **`block`** — raise `SkillSecurityError` when critical/high CVEs are
found. Skill does not load; agent falls back to built-in tools only.
Use once warn-phase is clean.
## When to install
Install on any workspace that:
- Installs skills from third-party sources (marketplace, agentskills.io,
user uploads)
- Runs in a production tenant context where agent compromise is
meaningful
- Must satisfy a supply-chain audit (SOC 2, ISO 27001 control A.8.28)
Skip on workspaces that only use first-party plugins from
`plugins/molecule-*` — those are vetted at commit time in monorepo CI.
## Audit trail
Every scan writes to the audit log via `audit.log_event`:
```json
{
"event_type": "supply_chain",
"action": "cve_scan",
"resource": "skill-name:version",
"outcome": "pass",
"detail": {
"scanner": "snyk",
"critical": 0,
"high": 0,
"medium": 2,
"low": 5
}
}
```
Failures (mode=block) log `outcome: "denied"` + the blocking CVE id.
Pair with `molecule-audit` to get the full JSONL trail.
## SNYK_TOKEN
Set via workspace secret (not config.yaml):
```bash
curl -X POST http://localhost:8080/workspaces/$WS_ID/secrets \
-H "Content-Type: application/json" \
-d '{"key":"SNYK_TOKEN","value":"..."}'
```
Snyk authenticates via env var; the token is injected at container
start. Without it Snyk runs in unauthenticated mode (fewer CVE sources
available) and the fallback to pip-audit is more attractive.
## Configuration — full
```yaml
security_scan:
mode: warn
# Override the auto-selected scanner:
# scanner: pip-audit # force pip-audit even when snyk is available
severity_threshold: high # critical | high | medium | low
fail_open_if_no_scanner: true # skip silently when neither tool present
```
`severity_threshold` — only findings at or above this severity trigger
the mode behavior (warn or block). Medium and low are always logged at
INFO but never block.
## Anti-patterns
- **Don't** set `mode: block` during initial rollout — you'll strand
legitimate skills that have medium-severity transitive deps. Start
in `warn`, measure, then block.
- **Don't** install without also installing `molecule-audit` — the
compliance value of scanning disappears if the events aren't in a
durable log.
- **Don't** scan the monorepo's first-party plugins. They're vetted at
PR-review time. Repeat scanning wastes time + may trip on false
positives.
- **Don't** rely on this as your only supply-chain defense. It catches
known CVEs; it does NOT catch typosquatting, malicious package
updates, or signed-but-compromised releases. Complement with
deterministic lockfiles + registry allowlists.
## Related
- `builtin_tools/security_scan.py` — the implementation
- `molecule-compliance` — runtime OWASP policy; this is its supply-
chain counterpart
- `molecule-audit` — event retention for scan results
- Issue #256 — the proposal that led to this plugin split