Skip to content

fix(catalog): project installability into skill index - #311

Draft
davida-ps wants to merge 1 commit into
davida-ps/nanoclaw-v2-truthfulnessfrom
davida-ps/catalog-installability-projection-v1
Draft

fix(catalog): project installability into skill index#311
davida-ps wants to merge 1 commit into
davida-ps/nanoclaw-v2-truthfulnessfrom
davida-ps/catalog-installability-projection-v1

Conversation

@davida-ps

@davida-ps davida-ps commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

User description

Summary

  • project an explicit installable boolean into the Pages and local skill indexes
  • resolve effective installability as immutable release metadata AND current repository metadata
  • fail closed when the current repository record is missing, while preserving legacy true for an absent field on an existing record
  • bind release and repository metadata to the tag/directory-derived skill identity
  • preserve mirrored and copied skill.json bytes unchanged

Scope

This is the projection-only PR from the multi-harness design. It does not yet make the website or clawsec-suite consume the projected denial; those remain separate follow-up PRs.

Changed paths:

  • .github/workflows/deploy-pages.yml
  • scripts/populate-local-skills.sh
  • scripts/ci/project_skill_catalog_installability.mjs
  • scripts/test-skill-catalog-installability-projection.mjs

This PR is stacked on davida-ps/nanoclaw-v2-truthfulness, which contains the shared installability contract and NanoClaw tombstone used by the projection.

Security behavior

  • a withdrawn release cannot be re-enabled by repository metadata
  • current repository metadata can withdraw a previously installable legacy release
  • an orphaned release with no current repository record projects false
  • mismatched release, repository, and expected identities are rejected
  • malformed, missing, unreadable, or non-boolean metadata fails without a permissive stdout token
  • lifecycle preflight completes before the local catalog is truncated or files are copied

Remote validation

All executable validation ran only on davida@20.14.133.241 in preserved quarantine:

/tmp/clawsec-catalog-projection.hAf0TD/source

The remote checkout was pinned to base ffe94d3b1e5f596d8dc1ce8c72ce49ebdca70775. Only the four reviewed files were transferred. Their remote SHA-256 values matched the reviewed local files.

  • npm ci — 422 packages, 0 vulnerabilities
  • node scripts/test-skill-catalog-installability-projection.mjs — passed
  • every scripts/test-skill-*.mjs — passed
  • release-bundle verifier — all 19 hostile archive cases passed
  • bash scripts/populate-local-skills.sh — passed with 15 public skills
  • real generated index — clawsec-nanoclaw=false, field-absent clawsec-suite=true
  • source and copied NanoClaw/Suite skill.json hashes — byte-identical
  • bash -n scripts/populate-local-skills.sh — passed
  • npx eslint . --ext .ts,.tsx,.js,.jsx,.mjs --max-warnings 0 — passed
  • npx tsc --noEmit — passed
  • npm run build — passed; 115 wiki exports generated

The remote host's system zip is incompatible with the tag-release simulation, so the full-suite rerun used the previously reviewed narrow zip -qr <archive> . compatibility wrapper with SHA-256 472456191504d886c5871da3e900797512ab9266b8426d736c6a0f48248046b0.

Release impact

No tags, releases, publications, or store writes are performed by this PR.


Generated description

Below is a concise technical summary of the changes proposed in this PR:
Project effective skill installability into the Pages deploy flow and the local catalog population flow by wiring both through project_skill_catalog_installability.mjs. Keep release skill.json bytes immutable while deploy-pages and populate-local-skills.sh bind repository metadata to the tag/directory skill identity and fail closed on invalid lifecycle state.

TopicDetails
Installability Project the catalog installable flag from release and repository metadata, and publish it into the Pages and local skill indexes without mutating copied skill.json artifacts.
Modified files (3)
  • .github/workflows/deploy-pages.yml
  • scripts/ci/project_skill_catalog_installability.mjs
  • scripts/populate-local-skills.sh
Latest Contributors(2)
UserCommitDate
David.a@prompt.securityfix(catalog): project ...July 22, 2026
david.a@prompt.securityfix(advisories): publi...July 12, 2026
Projection tests Cover the projection rules, CLI behavior, and preflight ordering with end-to-end tests for valid, withdrawn, missing, and malformed metadata cases.
Modified files (1)
  • scripts/test-skill-catalog-installability-projection.mjs
Latest Contributors(1)
UserCommitDate
David.a@prompt.securityfix(catalog): project ...July 22, 2026
Review this PR on Baz | Customize your next review

Comment on lines +20 to +24
async function readSkillJson(skillJsonPath, label) {
let source;
try {
source = await readFile(skillJsonPath, "utf8");
} catch (error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unvalidated CLI paths enable file read

--release-skill-json / --repository-skill-json flow from argv into readSkillJson(), which passes them straight to await readFile(skillJsonPath, "utf8"), so the CLI can read any local JSON file the invoker names — should we confine these paths to the repo root or an allow-list?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
scripts/ci/project_skill_catalog_installability.mjs around lines 20-36 in
`readSkillJson()` (and around lines 119-158 in `readOptionValue()`/`parseArgs()`),
don’t accept the CLI-provided path verbatim and pass it to `readFile`. Refactor by
adding a dedicated path-sanitization helper that resolves the given path against an
allowed base directory (e.g., the repo root), rejects absolute paths and any traversal
(like `..`), restricts to `.json` files, and verifies the resolved (real) path stays
within the base directory to prevent symlink escape. Then call `readSkillJson()` only
with the validated/constrained path and update the thrown errors to clearly indicate
invalid/unallowed paths.

Comment on lines +202 to 205
SKILL_DATA=$(jq -c \
--arg tag "$TAG" \
--argjson installable "$EFFECTIVE_INSTALLABLE" '
. as $skill |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated catalog projection

The jq program that builds each catalog entry is duplicated verbatim in scripts/populate-local-skills.sh and .github/workflows/deploy-pages.yml, so schema or defaulting changes have to be updated in two places and the Pages/local catalog paths can drift — should we move the JSON projection into a shared helper, like a small Node script both call?

Severity

Want Baz to fix this for you? Activate Fixer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant