4.3 KiB
4.3 KiB
Skill/Plugin Safety Scanner — Implementation Checklist
Date: 2026-02-15
Parent roadmap: docs/plans/2026-02-15-openclaw-gap-roadmap.md
Goal: Close the gap item "Skill/plugin code safety scanner" by adding static analysis gates for skills so unsafe skill packages are rejected (or marked unavailable) before they can be injected into prompts or used in routing.
Scope
In scope
- Add a static scanner for skill directories.
- Run the scanner during skill load (covers daemon startup, watcher reloads, and CLI skill operations).
- Optionally run the scanner during install/upgrade (pre-copy) to avoid persisting unsafe content.
- Emit audit events (pass/fail + reason) without leaking sensitive content.
Out of scope (for this milestone)
- Full SAST for arbitrary languages.
- Code signing / provenance chains.
- Remote registries.
Baseline
Current skill load/install entry points:
- Load/validate:
src/skills/loader.ts(loadSkill()) - Install/upgrade:
src/skills/installer.ts - CLI workflows:
src/cli/skills.ts - Prompt injection:
src/skills/registry.ts->src/daemon/services.ts(# Available Skillssection)
Scanner Policy (MVP)
File system safety
- Deny any symlinks inside a skill directory.
- Deny files above a size threshold (default: 1MB) unless allowlisted.
- Deny binary blobs (heuristic: NUL bytes or high non-text ratio) for
SKILL.mdandmanifest.json.
Manifest safety
manifest.jsonmust parse as JSON if present.- If a skill is intended for routing (i.e. referenced by an intent target), require
manifest.json.permissions.- This aligns with the deny-by-default runtime enforcement: skills without permissions should not be routable.
Prompt content safety (lightweight)
- Scan
SKILL.mdfor obvious prompt-injection patterns:- "ignore previous" / "system prompt" / "exfiltrate" / "send secrets" etc.
- Treat these as warnings or failures (recommend: failure for now).
Implementation Strategy
Scanner API
Create a small scanner module:
src/skills/scanner.tsscanSkillDirectory(dir): { ok: boolean; issues: SkillScanIssue[] }SkillScanIssue = { severity: 'error' | 'warn'; code: string; message: string; path?: string }
Config knobs (optional in MVP):
skills.scan.enabled(default true)skills.scan.max_file_size_bytes(default 1_000_000)skills.scan.fail_on_warnings(default false)
Enforce during load
- In
src/skills/loader.tsinsideloadSkill():- run scanner before returning a
Skill - on failure:
- either return
null(hard fail), OR - return Skill with
available=falseand add reasons
- either return
- run scanner before returning a
Recommendation: mark skill unavailable (not null) so the user can see it in flynn skills list with reasons.
Enforce during install (optional but recommended)
- In
src/skills/installer.ts:- scan
sourceDirbefore copying - fail install if scanner errors
- scan
Audit events
- Add audit event types for skill scans:
skills.scan.passskills.scan.fail
- Ensure messages do not include raw secret content; include issue codes and counts.
PR Breakdown
PR 1 — Scanner module + loader integration
Checklist:
- Add
src/skills/scanner.tswith MVP rules. - Integrate into
src/skills/loader.ts. - Update
src/skills/loader.test.tswith fixtures:- symlink skill rejected
- oversized file rejected
- injection marker in SKILL.md rejected
Acceptance:
flynn doctor/ skill load does not crash on bad skills.- Bad skills become unavailable with clear reasons.
Tests:
pnpm test:run src/skills/loader.test.ts
PR 2 — Installer integration
Checklist:
- Add scan preflight to
src/skills/installer.ts install(). - Add tests for install failure on scan errors.
Tests:
pnpm test:run src/skills/installer.test.ts(add if missing)
PR 3 — Audit events
Checklist:
- Extend
src/audit/types.tsto include skill scan events. - Extend
src/audit/logger.tshelpers. - Emit events from loader/install paths.
- Add targeted tests validating event shape (if audit logger has test coverage).
Acceptance:
- Audit logs show scan pass/fail with counts and stable issue codes.
Final Checks
pnpm typecheckpnpm test:run- Update
docs/plans/state.jsonentry tocompletedonce implemented (include test status).