# editor-extensions attestor

Source: https://www.testifysec.com/docs/cilock/attestors/editor-extensions/

The cilock editor-extensions attestor signs an inventory of the VS Code, Cursor, Windsurf and VSCodium extensions installed for a user, with a sha256 per file, so a verifier can prove an install matches upstream or flag a tampered one.

Inventories the extensions installed for VS Code, VS Code Insiders, Cursor, Windsurf and VSCodium, plus the dirs their remote servers install into, and signs a sha256 for every file. Pure Go: no tracing, no privileges, same behaviour on macOS, Windows and Linux.

 

| Name | `editor-extensions` |
| --- | --- |
| Predicate type | `https://aflock.ai/attestations/editor-extensions/v0.1` |
| Lifecycle | `prematerial` |
| Default binary? | No |
| Category | asset-inventory (primary) |
| Recommended trace | off — no syscall tracing needed |
| Auto-attaches when | - preenv\_set: CILOCK\_EDITOR\_EXTENSIONS\_HOME<br>- preargv\_contains: --install-extension<br>- preargv\_contains: --list-extensions |

The facts in this box are generated from the CI/lock binary's own catalog (`cilock tools list`). Do not hand-edit — run `npm run gen:catalog`.

 

## Validated invocation

 

```bash
cilock attest --step editor-extensions --attestations editor-extensions \
  --signer-file-key-path key.pem --outfile editor-extensions.json
```

 

Point it at another home directory (a fixture, a mounted image) with `CILOCK_EDITOR_EXTENSIONS_HOME`; a relative value is resolved against `--workingdir`.

 

## What gets captured

 

- `roots[]`: one entry per editor dir it looks in, recorded home-relative (`~/.cursor/extensions`), whether it is `present`, and the sha256 of each top-level file (`extensions.json`, `.obsolete`). Dirs read, under the home directory (`%USERPROFILE%` on Windows): `.vscode`, `.vscode-insiders`, `.cursor`, `.windsurf`, `.vscode-oss` (VSCodium), and `.vscode-server`, `.vscode-server-insiders`, `.cursor-server`, `.windsurf-server`, `.vscodium-server`, each `/extensions`.
 - `extensions[]`, one per directory under a root: 
  
  - `id` (lowercased `publisher.name`) and `version` from the extension's `package.json`;
   - `target_platform` from `extensions.json` metadata, else `package.json` `__metadata`, else the dir-name suffix (`-darwin-arm64`); universal installs record `""`;
   - `source` (`gallery`, `vsix`, ...) and `listed` from the root's `extensions.json`;
   - `files[]`: slash-separated path, `sha256`, `size`; a symlink is recorded with its `symlink` target and never followed;
   - `tree_sha256`: sha256 over `files` in path order, one compact JSON array per line: `["f","<sha256>","<path>"]` for a file, `["l","<target>","<path>"]` for a symlink. JSON escaping keeps it unambiguous whatever a path contains;
   - `complete`: every file under the dir was hashed.
 - `gaps[]`: every cap hit, unreadable path, symlinked extension dir, unparseable `extensions.json` and extension without a usable `package.json`. Nothing is skipped without a gap.
 - `limits`: the caps in force (files and bytes hashed, directory entries visited). Every byte read counts against the byte cap, and every entry, directories included, against the entry cap. Directories are read in batches, so a huge one is never listed whole first.

 

Subjects: `editor-extension:<editor>:<id>@<version>[+<platform>]` (or `editor-extension:<editor>:dir:<name>` without an identity), digest = `tree_sha256`.

 

## Why this shape

 

A file digest is what proves tampering: the Cursor 2.1.312 build differed from upstream 2.1.312 only in `extension.js`. The per-file list lets a verifier name the file; the tree digest lets the fleet search for one bad tree by subject digest. Identity comes from `package.json` because that is what the editor loads, and it is parsed from the same bytes that were hashed.

 

## Validate it locally

 

```bash
make test MODULES="plugins/attestors/editor-extensions"
```

 

## How a verifier consumes this

 

A policy reads `extensions[]` and joins on `id`, `version`, `target_platform` and each file's `sha256` against an upstream reference set; `complete` and `gaps` say whether the inventory may back a "not affected" claim at all. An extension with `complete: false`, or any gap, cannot. The subject digest (`tree_sha256`) finds every machine carrying one known-bad tree.

 

## Gotchas

 

- Opt-in. Its gate fires on `CILOCK_EDITOR_EXTENSIONS_HOME` or a wrapped `--install-extension` / `--list-extensions`; otherwise name it with `--attestations editor-extensions`.
 - The editor writes `.vsixmanifest` into the dir at install; a verifier comparing against the VSIX must ignore it. Older VS Code builds also wrote `__metadata` into `package.json`, which makes that file differ from the VSIX.
 - Not read: portable installs (`data/extensions` beside the binary), `--extensions-dir`, `VSCODE_EXTENSIONS`, `--extensionDevelopmentPath` dev extensions, and builtin extensions inside the app bundle. Run it on each side of WSL or a remote host: it reads the running OS's home only.
 - It proves what is on disk when it runs, not what the editor process loaded.

 

## See also

 

- [lockfiles](https://www.testifysec.com/docs/cilock/attestors/lockfiles)
 - [instruction-file](https://www.testifysec.com/docs/cilock/attestors/instruction-file)

 

---

**This page is generated from the CI/lock tool catalog.** Don't edit it here — the source is [`attestation/detection/docs/editor-extensions.doc.md`](https://github.com/aflock-ai/rookery/edit/main/attestation/detection/docs/editor-extensions.doc.md) in [aflock-ai/rookery](https://github.com/aflock-ai/rookery). The same catalog powers `cilock tools show editor-extensions` in the CLI, so a single edit keeps the terminal and the website in sync.

[Edit this page →](https://github.com/aflock-ai/rookery/edit/main/attestation/detection/docs/editor-extensions.doc.md) ·  [Add a new tool →](https://github.com/aflock-ai/rookery/new/main/attestation/detection/docs)

When you open the pull request: if you are an **AI agent**, add the `ai-agent` label. If you are a **human**, add the `human` label.

Reference generated from the product documentation. Match commands and support details to your installed release.
