Skill Permissions, Evals and Supply Chain
Review skill actions before they run, isolate them, gate each release by eval layer and scan bundles.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Comparisons
- Defaults and numbers
- Troubleshooting
- Questions and tips
Symptoms, causes and fixes#
| Symptom | Cause | Fix |
|---|---|---|
deny with rule workspace-jail | The target resolves outside the root | Keep targets under workspace_root |
command-review deny with approved=True | rm is in DESTRUCTIVE_EXECUTABLES | Approval cannot override it. Use a narrow tool |
network-allowlist deny on port 8443 | Port 8443 is a different origin | Add that origin with its port |
approval-gate on an allowlisted URL | influenced_by_untrusted_content is true | Show the target and consequence. Get approval |
secret-review deny | The payload matches SECRET_PATTERNS | Inject the credential into one narrow tool |
Lint issue orphan-file | SKILL.md does not reference the file | Reference the file or remove it |
fixture_passed true, passed false | No trusted external attestation | Pass --attestation and --trusted-attestation-sha256 |
Audit usable is false | Same content or group in both partitions | Union by content and group before the split |
From Lesson 13.26, 13.26 code/main.py, Lesson 13.27, 13.27 code/main.py, self-improving-skill-loop/README.md, solution/dataset.py
review_action check order#
From 13.26 code/main.py
Review one action#
policy = SandboxPolicy( workspace_root=workspace,resolved with strict=True allowed_kinds=("read", "write", "delete", "command", "network"), command_allowlist=(("python3", "-m", "unittest"),),argv prefixes network_allowlist=("https://docs.example.test",),origins only)request = ActionRequest("write", target="summary.json")write is in approval_kindsreview_action(policy, request).to_dict()["verdict"]"require-approval"request = ActionRequest("write", target="summary.json", approved=True)review_action(policy, request).rule"policy-allow"normalize_https_origin("https://api.example.test")adds ":443"Run the isolation drill#
docker build -f code/sandbox/Containerfile -t aiefs-skill-sandbox code/sandboxdocker run --rm --network none --read-only --cap-drop ALL \ --security-opt no-new-privileges --pids-limit 64 --memory 128m --cpus 0.5 \ --tmpfs /tmp:rw,noexec,nosuid,size=16m \ --mount type=bind,src="${PWD}/code/sandbox/input",dst=/input,readonly \ --env DEMO_VALUE=bounded aiefs-skill-sandboxexpect: /input readable, image read-only, network fails
From Lesson 13.26
Six eval layers#
- Structure
- lint without a model. CI blocks
E_*errors and allows reviewedW_*warnings. - Routing
- precision and recall over positives, clear negatives and near misses. Report raw counts.
- Behavior
- baseline and treatment use the same model, tools, task and budget.
- Scripts
- test outside model runs with empty, malformed and repeated input.
- Safety
- record the control: instruction only, tool policy, approval, sandbox or verification.
- Portability
- install into a clean destination. Validate the installed copy.
- Verdicts
fixturePassed,localEvidenceReady,productionReady.passedfollowsproductionReady.
From Lesson 13.27
Run the labs and tools#
| Command | What it does |
|---|---|
python3 code/ | Prints the demo decisions. It starts no process |
python3 -m unittest discover -s code/ | Runs the lesson tests |
python3 scripts/ | Returns the verdict and the rule for one request |
evaluate_skill.py --fixture-demo <bundle> | Fixture run. fixturePassed true, productionReady false |
scanner examples/ | Scans a bundle with threshold 3. Prints JSON |
cli.py <cases> --out experiment.json | Proposes rules and runs the gate. It does not promote |
python3 scripts/ | Checks every stage of the reference solution |
From Lesson 13.26, 13.26 outputs/skill-safety-reviewer/SKILL.md, Lesson 13.27, skill-scanner/README.md, self-improving-skill-loop/README.md
Defaults and limits#
| Setting | Default | Note |
|---|---|---|
allowed_kinds | ("read", | SandboxPolicy allows reads only |
approval_kinds | ("write", | These kinds need approved |
MAX_SKILL_BODY_CHARS | 10_000 | Lint issue body-size above it |
MAX_COMPANION_FILE_BYTES | 1_000_000 | Lint issue file-size above it |
min_precision, min_recall | 0.8 | ReleaseThresholds in the lab |
min_repeat_rate | 0.9 | Lowest per-case pass rate |
min_gain | 0.05 | The loop gate also needs zero regressions |
| Scanner threshold | 3 | A threshold of 0 is an error |
From 13.26 code/main.py, 13.27 code/main.py, solution/promotion.py, solution/cli.rs
Isolation boundaries#
| Factor | Restricted subprocess | Container | MicroVM |
|---|---|---|---|
| Isolates | Environment, cwd, timeout, output | Filesystem and process namespaces | Separate guest kernel |
| Does not isolate | Kernel, host filesystem, network | Shared kernel, host mounts | Bad mounts, credentials, egress |
| Typical use | Reviewed local utilities | Repository builds and tests | Untrusted code, high impact |
From Lesson 13.26
Project pipelines#
From skill-scanner/solution, self-improving-skill-loop/solution
Tasks in code#
| Task | Code |
|---|---|
| Lint a bundle | lint_package(root) |
| Score routing | classification_metrics(expected, |
| Check an installed copy | verify_manifest(root, |
| Host support matrix | portability_matrix(requirements, |
| Split without leakage | split_cases(cases, |
| Propose rules | propose(development, |
| Gate a candidate | gate(holdout, |
| Refine a library skill | register(skill, dedup=True) bumps the version |
From 13.27 code/main.py, 14.10 code/main.py, self-improving-skill-loop/solution
Questions#
- Does
allowed-toolssandbox a skill? - No. It affects capability or permission prompts. It is not OS isolation.
- Does a hash prove a package is safe?
- No. It proves that the bytes match a manifest.
- Is a below-threshold scan safe?
- No. Only these rules did not cross the threshold.
- Why version skills?
- A parent pinned to v1 does not pick up a refined v3.
- Why rotate the holdout?
- Repeated tuning on the same holdout leaks information.
From Lesson 13.26, skill-scanner/README.md, Lesson 14.10, docs/en.md