Skip to content

fix(cache): validate git version/ref values before rev-parse - #317

Merged
Minitour merged 6 commits into
fix/stale-hook-entries-306from
fix/validate-git-refs-308
Oct 15, 2026
Merged

Minitour merged 6 commits into
fix/stale-hook-entries-306from
fix/validate-git-refs-308

Conversation

@Minitour

@Minitour Minitour commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Summary

def.version, def.ref, repo suffixes and lockfile pins reached git rev-parse --verify <ref>^{commit} unchecked, so rev-parse's revision syntax and option-like values could get through. Closes #308.

Stack 3/5: base #316, next #318.

What changed

  • resolveRef in mirror.ts is the only place these values reach git (skills, plugins and hooks all go through it). It now rejects what git check-ref-format rejects:
    • control characters and spaces;
    • ~ ^ : ? * [ \;
    • .. and @{;
    • a bare @;
    • a leading -, / or .;
    • a trailing / or .;
    • // and .lock components;
    • more than 255 characters.
  • That rules out rev-parse revision syntax (HEAD~1, main@{1}, :/text, v1..v2) and option-like values (--output=...).
  • Tags git accepts keep working, including monorepo tags such as @scope/pkg@1.2.3 (an allowlist in the first version of this PR rejected those).
  • The offline snapshot fast path (snapshot.ts) now requires a lockfile pin to be 40 hex characters before using it as a path segment. A tampered resolvedRef like ../../... could otherwise point at another local directory. Anything else falls through to resolveRef and is rejected.
  • Empty values still mean "unset".

Test plan

  • New test:
    • bad values are rejected for version, ref and the pinned commit;
    • v1.0.0 still resolves;
    • @scope/pkg@1.2.3, pkg@1.2.3, release/1.0, feature#12, café and v1.0.0+build.1 pass validation.
  • Existing cache/mirror tests.
  • bunx tsc --noEmit

Checklist

  • Tests added or updated
  • bunx tsc --noEmit passes
  • Docs updated (if user-facing): not needed

🤖 Generated with Claude Code

Skill, plugin and hook entries pass def.version, def.ref (and repo
suffixes) to `git rev-parse --verify <ref>^{commit}`. Nothing checked
them first, so rev-parse's revision syntax (HEAD~2, main@{1}, :/text,
a..b) and option-like values reached git.

resolveRef, the only place these values reach git, now accepts only
plain tag/branch names and SHAs (letters, digits, . _ / + -, no leading
"-", no "..") and fails with a clear error otherwise. Lockfile pins
are checked the same way.

Closes #308
The capabilities-manager reference still described ${PLUGIN_DATA} as
~/.capa/plugin-data/<projectId>/<pluginId>/ that survives plugin updates.
Since #286 the folder is <slug>-<hash>, keyed by the declared source, and
it is removed when the plugin is no longer declared and by capa clean.
Match docs/README.md.

Closes #309
The allowlist rejected tags git accepts, such as changesets/lerna
monorepo tags (@scope/pkg@1.2.3), "feature#12" or unicode names, which
installed fine before. Reject what git check-ref-format rejects instead
(control characters, space, ~ ^ : ? * [ \, "..", "@{", a leading
"-", "/" or ".", and similar), which still rules out rev-parse's
revision syntax and option-like values.

The offline snapshot fast path only checked that a lockfile pin was 40
characters long before using it as a path segment, so a tampered
resolvedRef could point at another local directory. It now requires 40
hex characters; anything else falls through to resolveRef, which
rejects it.
docs(skills): correct the PLUGIN_DATA path and lifetime
@Minitour
Minitour merged commit e25a430 into fix/stale-hook-entries-306 Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant