From c1d1824f862ef673829463e67254593cad362937 Mon Sep 17 00:00:00 2001 From: davida-ps Date: Wed, 10 Jun 2026 13:22:22 +0300 Subject: [PATCH] ci(skills): publish release trust packets + expand skill installer awareness (vercel) (#262) * ci(skills): publish release trust packets * ci(skills): simulate beta tag releases * ci(skills): match release version bump rules * chore(skills): group agent skills for installer * chore(skills): make clawtributor global * chore(skills): bump all skills for trust release * ci(skills): require npx install docs * fix(skills): simulate prerelease tag versions * fix(skills): aggregate trust artifact checksum failures * fix(frontend): advertise npx skills suite install * chore(frontend): drop ad hoc homepage copy test * fix(ci): run skill release tooling tests --- .claude-plugin/marketplace.json | 56 ++ .github/workflows/ci.yml | 8 +- .github/workflows/skill-release.yml | 280 ++++++++++ README.md | 2 +- index.html | 4 +- pages/Home.tsx | 16 +- .../generate_skill_release_trust_packet.mjs | 404 ++++++++++++++ scripts/ci/simulate_skill_tag_release.mjs | 520 ++++++++++++++++++ scripts/ci/validate_skill_install_docs.mjs | 316 +++++++++++ scripts/test-skill-install-docs.mjs | 137 +++++ scripts/test-skill-release-workflow.mjs | 98 ++++ scripts/test-skill-tag-release-simulation.mjs | 155 ++++++ scripts/test-skill-trust-packet.mjs | 79 +++ skills/claw-release/CHANGELOG.md | 7 + skills/claw-release/README.md | 11 + skills/claw-release/SKILL.md | 20 +- skills/claw-release/skill.json | 2 +- skills/clawsec-clawhub-checker/CHANGELOG.md | 6 + skills/clawsec-clawhub-checker/README.md | 8 + skills/clawsec-clawhub-checker/SKILL.md | 11 +- skills/clawsec-clawhub-checker/skill.json | 2 +- skills/clawsec-feed/CHANGELOG.md | 6 + skills/clawsec-feed/README.md | 8 + skills/clawsec-feed/SKILL.md | 14 +- skills/clawsec-feed/skill.json | 2 +- skills/clawsec-nanoclaw/CHANGELOG.md | 6 + skills/clawsec-nanoclaw/README.md | 8 + skills/clawsec-nanoclaw/SKILL.md | 11 +- skills/clawsec-nanoclaw/skill.json | 2 +- skills/clawsec-scanner/CHANGELOG.md | 6 + skills/clawsec-scanner/README.md | 11 + skills/clawsec-scanner/SKILL.md | 10 +- skills/clawsec-scanner/skill.json | 2 +- skills/clawsec-suite/CHANGELOG.md | 6 + skills/clawsec-suite/README.md | 11 + skills/clawsec-suite/SKILL.md | 10 +- skills/clawsec-suite/skill.json | 2 +- skills/clawtributor/CHANGELOG.md | 10 + skills/clawtributor/README.md | 22 + skills/clawtributor/SKILL.md | 50 +- skills/clawtributor/skill.json | 17 +- .../hermes-attestation-guardian/CHANGELOG.md | 6 + skills/hermes-attestation-guardian/README.md | 8 + skills/hermes-attestation-guardian/SKILL.md | 11 +- skills/hermes-attestation-guardian/skill.json | 2 +- skills/hermes-traffic-guardian/CHANGELOG.md | 6 + skills/hermes-traffic-guardian/README.md | 9 +- skills/hermes-traffic-guardian/SKILL.md | 12 +- skills/hermes-traffic-guardian/skill.json | 2 +- skills/nanoclaw-traffic-guardian/CHANGELOG.md | 6 + skills/nanoclaw-traffic-guardian/README.md | 9 +- skills/nanoclaw-traffic-guardian/SKILL.md | 12 +- skills/nanoclaw-traffic-guardian/skill.json | 2 +- skills/openclaw-audit-watchdog/CHANGELOG.md | 6 + skills/openclaw-audit-watchdog/README.md | 8 + skills/openclaw-audit-watchdog/SKILL.md | 13 +- skills/openclaw-audit-watchdog/skill.json | 2 +- skills/openclaw-traffic-guardian/CHANGELOG.md | 6 + skills/openclaw-traffic-guardian/README.md | 9 +- skills/openclaw-traffic-guardian/SKILL.md | 12 +- skills/openclaw-traffic-guardian/skill.json | 2 +- .../picoclaw-security-guardian/CHANGELOG.md | 6 + skills/picoclaw-security-guardian/README.md | 9 +- skills/picoclaw-security-guardian/SKILL.md | 11 +- skills/picoclaw-security-guardian/skill.json | 2 +- skills/picoclaw-self-pen-testing/CHANGELOG.md | 6 + skills/picoclaw-self-pen-testing/README.md | 8 + skills/picoclaw-self-pen-testing/SKILL.md | 11 +- skills/picoclaw-self-pen-testing/skill.json | 2 +- skills/picoclaw-traffic-guardian/CHANGELOG.md | 6 + skills/picoclaw-traffic-guardian/README.md | 9 +- skills/picoclaw-traffic-guardian/SKILL.md | 12 +- skills/picoclaw-traffic-guardian/skill.json | 2 +- skills/soul-guardian/CHANGELOG.md | 6 + skills/soul-guardian/README.md | 8 + skills/soul-guardian/SKILL.md | 13 +- skills/soul-guardian/skill.json | 2 +- 77 files changed, 2528 insertions(+), 84 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 scripts/ci/generate_skill_release_trust_packet.mjs create mode 100644 scripts/ci/simulate_skill_tag_release.mjs create mode 100644 scripts/ci/validate_skill_install_docs.mjs create mode 100644 scripts/test-skill-install-docs.mjs create mode 100644 scripts/test-skill-tag-release-simulation.mjs create mode 100644 scripts/test-skill-trust-packet.mjs create mode 100644 skills/claw-release/README.md create mode 100644 skills/clawsec-scanner/README.md create mode 100644 skills/clawsec-suite/README.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..2b8aa28 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,56 @@ +{ + "plugins": [ + { + "name": "global-skills", + "source": "./", + "skills": [ + "./skills/clawtributor" + ] + }, + { + "name": "hermes-skills", + "source": "./", + "skills": [ + "./skills/hermes-attestation-guardian", + "./skills/hermes-traffic-guardian" + ] + }, + { + "name": "nano-claw-skills", + "source": "./", + "skills": [ + "./skills/clawsec-nanoclaw", + "./skills/nanoclaw-traffic-guardian" + ] + }, + { + "name": "open-claw-skills", + "source": "./", + "skills": [ + "./skills/clawsec-clawhub-checker", + "./skills/clawsec-feed", + "./skills/clawsec-scanner", + "./skills/clawsec-suite", + "./skills/openclaw-audit-watchdog", + "./skills/openclaw-traffic-guardian", + "./skills/soul-guardian" + ] + }, + { + "name": "pico-claw-skills", + "source": "./", + "skills": [ + "./skills/picoclaw-security-guardian", + "./skills/picoclaw-self-pen-testing", + "./skills/picoclaw-traffic-guardian" + ] + }, + { + "name": "repo-internal-skills", + "source": "./", + "skills": [ + "./skills/claw-release" + ] + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5aa426e..a37b37c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,8 +111,12 @@ jobs: run: node scripts/test-nvd-ghsa-consolidation-workflow.mjs - name: NVD + GHSA Pipeline Dry Run run: node scripts/test-nvd-ghsa-pipeline-dry-run.mjs - - name: Skill Release Workflow Tests - run: node scripts/test-skill-release-workflow.mjs + - name: Skill Release Tooling Tests + run: | + set -euo pipefail + for test_file in scripts/test-skill-*.mjs; do + node "$test_file" + done - name: Deploy Pages Advisory Checksums Tests run: node scripts/test-deploy-pages-checksums.mjs - name: GitHub Traffic Archive Tests diff --git a/.github/workflows/skill-release.yml b/.github/workflows/skill-release.yml index 18ba701..cf65de2 100644 --- a/.github/workflows/skill-release.yml +++ b/.github/workflows/skill-release.yml @@ -7,6 +7,9 @@ on: pull_request: paths: - 'skills/**' + - '.github/workflows/skill-release.yml' + - 'scripts/ci/**' + - 'scripts/test-skill-*.mjs' workflow_dispatch: inputs: tag: @@ -35,6 +38,11 @@ jobs: with: fetch-depth: 0 + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 20 + - name: Verify signing key consistency (repo + docs) run: ./scripts/ci/verify_signing_key_consistency.sh @@ -229,6 +237,12 @@ jobs: echo "Validated ${checked_skills} bumped skill(s): version parity and changelog release notes are present." + - name: Validate npx skills install docs + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: node scripts/ci/validate_skill_install_docs.mjs --base "$BASE_SHA" --head "$HEAD_SHA" + release: if: github.event_name == 'pull_request' needs: validate-pr-version-sync @@ -241,6 +255,21 @@ jobs: with: fetch-depth: 0 + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 20 + + - name: Install SkillSpector + run: | + set -euo pipefail + python3 -m venv /tmp/skillspector-venv + . /tmp/skillspector-venv/bin/activate + git clone --depth 1 https://github.com/NVIDIA/SkillSpector.git /tmp/skillspector + make -C /tmp/skillspector install + echo "/tmp/skillspector-venv/bin" >> "$GITHUB_PATH" + skillspector --help >/dev/null + - name: Generate test signing key for dry-run run: | set -euo pipefail @@ -399,6 +428,50 @@ jobs: [[ "$lower" == test/* || "$lower" == tests/* || "$lower" == */test/* || "$lower" == */tests/* ]] } + generate_skillspector_report() { + local skill_dir="$1" + local report_path="$2" + + set +e + skillspector scan "${skill_dir}" --no-llm --format markdown --output "${report_path}" + local status=$? + set -e + + if [ ! -s "${report_path}" ]; then + echo "::error file=${skill_dir}::SkillSpector did not produce a report." + return 1 + fi + + if [ "${status}" -ne 0 ]; then + echo "::warning file=${report_path}::SkillSpector returned exit code ${status}; report is included for review." + fi + } + + add_release_asset_checksum() { + local out_assets="$1" + local asset="$2" + local file_path="${out_assets}/${asset}" + local sha256 + local size + local tmp_json + + if [ ! -s "${file_path}" ]; then + echo "::error file=${file_path}::Required release trust artifact is missing or empty." + return 1 + fi + + sha256="$(sha256sum "${file_path}" | awk '{print $1}')" + size="$(stat -c%s "${file_path}" 2>/dev/null || stat -f%z "${file_path}")" + tmp_json="$(mktemp)" + jq \ + --arg key "${asset}" \ + --arg sha "${sha256}" \ + --argjson sz "${size}" \ + '.files += {($key): {sha256: $sha, size: $sz, path: $key}}' \ + "${out_assets}/checksums.json" > "${tmp_json}" + mv "${tmp_json}" "${out_assets}/checksums.json" + } + while IFS= read -r skill_dir; do json_path="${skill_dir}/skill.json" md_path="${skill_dir}/SKILL.md" @@ -622,6 +695,58 @@ jobs: continue fi + # --- Generate release trust packet and include it in signed checksums --- + node scripts/ci/generate_skill_release_trust_packet.mjs \ + "${skill_dir}" \ + "${out_assets}" \ + --repository "${{ github.repository }}" \ + --tag "${tag}" \ + --source-ref "${HEAD_SHA}" + + # --- Generate SkillSpector report --- + if ! generate_skillspector_report "${skill_dir}" "${out_assets}/skillspector-report.md"; then + failures=$((failures + 1)) + rm -rf "${staging_dir}" + echo "::endgroup::" + continue + fi + + if ! add_release_asset_checksum "${out_assets}" "skill-card.md"; then + failures=$((failures + 1)) + rm -rf "${staging_dir}" + echo "::endgroup::" + continue + fi + + if ! add_release_asset_checksum "${out_assets}" "permissions.json"; then + failures=$((failures + 1)) + rm -rf "${staging_dir}" + echo "::endgroup::" + continue + fi + + if ! add_release_asset_checksum "${out_assets}" "install.md"; then + failures=$((failures + 1)) + rm -rf "${staging_dir}" + echo "::endgroup::" + continue + fi + + if ! add_release_asset_checksum "${out_assets}" "skillspector-report.md"; then + failures=$((failures + 1)) + rm -rf "${staging_dir}" + echo "::endgroup::" + continue + fi + + if ! jq -e . "${out_assets}/checksums.json" >/dev/null 2>&1; then + echo "::error::Generated checksums.json is invalid JSON after adding release trust artifacts." + failures=$((failures + 1)) + rm -rf "${staging_dir}" + echo "::endgroup::" + continue + fi + # --- Copy skill.json and root-level docs alongside the zip --- cp "${json_path}" "${out_assets}/skill.json" if [ -f "${skill_dir}/SKILL.md" ]; then @@ -652,6 +777,56 @@ jobs: echo "Release dry-run completed successfully for ${dry_run_count} changed skill(s)." + simulate-tag-release-build: + if: github.event_name == 'pull_request' + needs: validate-pr-version-sync + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 20 + + - name: Install SkillSpector + run: | + set -euo pipefail + python3 -m venv /tmp/skillspector-venv + . /tmp/skillspector-venv/bin/activate + git clone --depth 1 https://github.com/NVIDIA/SkillSpector.git /tmp/skillspector + make -C /tmp/skillspector install + echo "/tmp/skillspector-venv/bin" >> "$GITHUB_PATH" + skillspector --help >/dev/null + + - name: Simulate tag release build + run: | + set -euo pipefail + mkdir -p dist/tag-release-simulation + + for skill_json in skills/*/skill.json; do + skill_dir="${skill_json%/skill.json}" + skill_name="$(basename "${skill_dir}")" + echo "::group::Simulate tag release build for ${skill_name}" + node scripts/ci/simulate_skill_tag_release.mjs \ + "${skill_dir}" \ + "dist/tag-release-simulation/${skill_name}" \ + --repository "${{ github.repository }}" \ + --source-ref "${{ github.event.pull_request.head.sha }}" + jq -e '.simulated_version | test("^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9]+)?$")' \ + "dist/tag-release-simulation/${skill_name}/simulation-summary.json" >/dev/null + test -s "dist/tag-release-simulation/${skill_name}/release-assets/checksums.json" + test -s "dist/tag-release-simulation/${skill_name}/release-assets/checksums.sig" + test -s "dist/tag-release-simulation/${skill_name}/release-assets/signing-public.pem" + test -s "dist/tag-release-simulation/${skill_name}/release-assets/skillspector-report.md" + echo "::endgroup::" + done + release-tag: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest @@ -771,6 +946,19 @@ jobs: with: node-version: 20 + - name: Validate npx skills install docs + run: node scripts/ci/validate_skill_install_docs.mjs --skills "${{ steps.parse.outputs.skill_path }}" + + - name: Install SkillSpector + run: | + set -euo pipefail + python3 -m venv /tmp/skillspector-venv + . /tmp/skillspector-venv/bin/activate + git clone --depth 1 https://github.com/NVIDIA/SkillSpector.git /tmp/skillspector + make -C /tmp/skillspector install + echo "/tmp/skillspector-venv/bin" >> "$GITHUB_PATH" + skillspector --help >/dev/null + - name: Sign embedded advisory feed and verify if: hashFiles(format('skills/{0}/advisories/feed.json', steps.parse.outputs.skill_name)) != '' uses: ./.github/actions/sign-and-verify @@ -870,6 +1058,49 @@ jobs: [[ "$lower" == test/* || "$lower" == tests/* || "$lower" == */test/* || "$lower" == */tests/* ]] } + generate_skillspector_report() { + local skill_dir="$1" + local report_path="$2" + + set +e + skillspector scan "${skill_dir}" --no-llm --format markdown --output "${report_path}" + local status=$? + set -e + + if [ ! -s "${report_path}" ]; then + echo "::error file=${skill_dir}::SkillSpector did not produce a report." + return 1 + fi + + if [ "${status}" -ne 0 ]; then + echo "::warning file=${report_path}::SkillSpector returned exit code ${status}; report is included for review." + fi + } + + add_release_asset_checksum() { + local asset="$1" + local file_path="release-assets/${asset}" + local sha256 + local size + local tmp_json + + if [ ! -s "${file_path}" ]; then + echo "::error file=${file_path}::Required release trust artifact is missing or empty." + return 1 + fi + + sha256="$(sha256sum "${file_path}" | awk '{print $1}')" + size="$(stat -c%s "${file_path}" 2>/dev/null || stat -f%z "${file_path}")" + tmp_json="$(mktemp)" + jq \ + --arg key "${asset}" \ + --arg sha "${sha256}" \ + --argjson sz "${size}" \ + '.files += {($key): {sha256: $sha, size: $sz, path: $key}}' \ + release-assets/checksums.json > "${tmp_json}" + mv "${tmp_json}" release-assets/checksums.json + } + # --- Stage SBOM files preserving directory structure --- STAGING_DIR="$(mktemp -d)" INNER_DIR="$STAGING_DIR/$SKILL_NAME" @@ -971,6 +1202,32 @@ jobs: files: $files }' > "release-assets/checksums.json" + # --- Generate release trust packet and include it in signed checksums --- + node scripts/ci/generate_skill_release_trust_packet.mjs \ + "$SKILL_PATH" \ + release-assets \ + --repository "${{ github.repository }}" \ + --tag "$TAG" \ + --source-ref "$TAG" + + # --- Generate SkillSpector report --- + generate_skillspector_report "$SKILL_PATH" "release-assets/skillspector-report.md" + + test -s release-assets/skill-card.md + test -s release-assets/permissions.json + test -s release-assets/install.md + test -s release-assets/skillspector-report.md + + add_release_asset_checksum "skill-card.md" + add_release_asset_checksum "permissions.json" + add_release_asset_checksum "install.md" + add_release_asset_checksum "skillspector-report.md" + + if ! jq -e . "release-assets/checksums.json" >/dev/null 2>&1; then + echo "::error::Generated checksums.json is invalid JSON after adding release trust artifacts." + exit 1 + fi + # --- Copy skill.json and root-level docs alongside the zip --- cp "$SKILL_PATH/skill.json" release-assets/skill.json if [ -f "$SKILL_PATH/SKILL.md" ]; then @@ -1062,6 +1319,26 @@ jobs: { echo "quick_install< @@ -141,7 +141,7 @@ ClawSec Security skill suite for AI agents (integrity checks, drift detection, advisory feed). Agent install: -Available via clawhub: npx clawhub@latest install clawsec-suite +Available via npx skills: npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y OR curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md diff --git a/pages/Home.tsx b/pages/Home.tsx index 7d80f77..73e91fa 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -13,7 +13,7 @@ export const Home: React.FC = () => { const [currentFileIndex, setCurrentFileIndex] = useState(0); const [currentPlatformIndex, setCurrentPlatformIndex] = useState(0); - const curlCommand = `npx clawhub@latest install clawsec-suite`; + const curlCommand = `npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y`; // Rotate file names every 2-3 seconds useEffect(() => { @@ -44,7 +44,7 @@ export const Home: React.FC = () => { }; }, []); - const humanInstruction = `Please install clawsec-suite from clawhubnpx clawhub@latest install clawsec-suite`; + const humanInstruction = `Please install clawsec-suite with npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y`; const handleCopyCurl = () => { navigator.clipboard.writeText(curlCommand); @@ -285,6 +285,18 @@ export const Home: React.FC = () => { )} +

+ * For harnesses other than OpenClaw, consult the{' '} + + README Skill Feature Matrix + + . +

diff --git a/scripts/ci/generate_skill_release_trust_packet.mjs b/scripts/ci/generate_skill_release_trust_packet.mjs new file mode 100644 index 0000000..a8a547b --- /dev/null +++ b/scripts/ci/generate_skill_release_trust_packet.mjs @@ -0,0 +1,404 @@ +#!/usr/bin/env node +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; + +const PLATFORM_KEYS = ["openclaw", "nanoclaw", "hermes", "picoclaw"]; +const KNOWN_AGENT_TYPES = new Set(["codex", "hermes-agent", "openclaw", "universal"]); +const PLATFORM_AGENT_ALIASES = new Map([["hermes", "hermes-agent"]]); + +function usage() { + return [ + "Usage: node scripts/ci/generate_skill_release_trust_packet.mjs [options]", + "", + "Options:", + " --repository Source repository used in install instructions", + " --tag Release tag for this skill", + " --source-ref Source ref for npx skills examples", + ].join("\n"); +} + +function parseArgs(argv) { + const positional = []; + const options = { + repository: "prompt-security/clawsec", + tag: "", + sourceRef: "main", + }; + + for (let i = 0; i < argv.length; i += 1) { + const token = argv[i]; + if (token === "--repository") { + options.repository = argv[++i]; + } else if (token === "--tag") { + options.tag = argv[++i]; + } else if (token === "--source-ref") { + options.sourceRef = argv[++i]; + } else if (token === "--help" || token === "-h") { + console.log(usage()); + process.exit(0); + } else if (token.startsWith("--")) { + throw new Error(`Unknown option: ${token}`); + } else { + positional.push(token); + } + } + + if (positional.length !== 2) { + throw new Error(usage()); + } + + return { + skillDir: positional[0], + outputDir: positional[1], + ...options, + }; +} + +function parseFrontmatter(markdown) { + if (!markdown.startsWith("---\n")) { + return {}; + } + + const end = markdown.indexOf("\n---", 4); + if (end === -1) { + return {}; + } + + const result = {}; + const frontmatter = markdown.slice(4, end).split("\n"); + for (const line of frontmatter) { + const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/); + if (match) { + result[match[1]] = match[2].replace(/^["']|["']$/g, "").trim(); + } + } + return result; +} + +function asArray(value) { + if (Array.isArray(value)) { + return value.filter((item) => item !== null && item !== undefined).map(String); + } + if (typeof value === "string" && value.trim()) { + return [value.trim()]; + } + return []; +} + +function unique(values) { + return [...new Set(values.filter(Boolean))]; +} + +function detectPlatform(skill) { + for (const key of PLATFORM_KEYS) { + if (skill[key] && typeof skill[key] === "object") { + return key; + } + } + return skill.platform || "agent-skills"; +} + +function collectDeclaredPlatforms(skill) { + const platforms = new Set(); + if (typeof skill.platform === "string" && skill.platform.trim()) { + platforms.add(skill.platform.trim()); + } + if (Array.isArray(skill.platforms)) { + for (const platform of skill.platforms) { + if (typeof platform === "string" && platform.trim()) { + platforms.add(platform.trim()); + } + } + } + for (const key of PLATFORM_KEYS) { + if (skill[key] && typeof skill[key] === "object") { + platforms.add(key); + } + } + return [...platforms]; +} + +function installAgentForSkill(skill) { + const platforms = collectDeclaredPlatforms(skill); + if (platforms.length === 0) { + return "openclaw"; + } + + const matchedAgents = new Set(); + let allPlatformsMatched = true; + for (const platform of platforms) { + const candidate = PLATFORM_AGENT_ALIASES.get(platform) || platform; + if (KNOWN_AGENT_TYPES.has(candidate)) { + matchedAgents.add(candidate); + } else { + allPlatformsMatched = false; + } + } + + if (allPlatformsMatched && matchedAgents.size === 1) { + return [...matchedAgents][0]; + } + + return "openclaw"; +} + +function platformMetadata(skill, platform) { + const direct = skill[platform]; + return direct && typeof direct === "object" ? direct : {}; +} + +function collectRequiredBinaries(metadata) { + const requires = metadata.requires && typeof metadata.requires === "object" ? metadata.requires : {}; + const bins = asArray(requires.bins); + + for (const [key, value] of Object.entries(requires)) { + if (key !== "bins" && typeof value === "string") { + bins.push(key); + } + } + + return unique(bins); +} + +function collectOptionalBinaries(metadata) { + return unique([ + ...asArray(metadata.runtime?.optional_bins), + ...asArray(metadata.runtime?.optionalBins), + ]); +} + +function collectRequiredEnv(metadata) { + const requires = metadata.requires && typeof metadata.requires === "object" ? metadata.requires : {}; + return unique([ + ...asArray(requires.env), + ...asArray(metadata.runtime?.required_env), + ...asArray(metadata.runtime?.requiredEnv), + ]); +} + +function collectOptionalEnv(metadata) { + return unique([ + ...asArray(metadata.runtime?.optional_env), + ...asArray(metadata.runtime?.optionalEnv), + ]); +} + +function stringifyCapabilities(skill, metadata) { + const capabilities = metadata.capabilities ?? skill.capabilities ?? {}; + if (Array.isArray(capabilities)) { + return capabilities; + } + if (capabilities && typeof capabilities === "object") { + return Object.entries(capabilities).map(([key, value]) => `${key}: ${String(value)}`); + } + if (typeof capabilities === "string") { + return [capabilities]; + } + return []; +} + +function requireField(skill, fieldName) { + if (!skill[fieldName] || typeof skill[fieldName] !== "string" || !skill[fieldName].trim()) { + throw new Error(`skill.json missing required trust-packet field: ${fieldName}`); + } + return skill[fieldName].trim(); +} + +function codeBlock(command) { + return ["```bash", command, "```"].join("\n"); +} + +function buildPermissions({ skill, metadata, platform, generatedAt }) { + const execution = metadata.execution && typeof metadata.execution === "object" ? metadata.execution : {}; + const permissions = { + schema_version: "1", + generated_at: generatedAt, + skill: skill.name, + version: skill.version, + platform, + required_binaries: collectRequiredBinaries(metadata), + optional_binaries: collectOptionalBinaries(metadata), + required_env: collectRequiredEnv(metadata), + optional_env: collectOptionalEnv(metadata), + network_egress: execution.network_egress || "Not declared in skill metadata.", + persistence: execution.persistence || "Not declared in skill metadata.", + automatic_execution: typeof execution.always === "boolean" ? execution.always : "Not declared in skill metadata.", + capabilities: stringifyCapabilities(skill, metadata), + operator_review: asArray(metadata.operator_review), + }; + + return permissions; +} + +function buildSkillCard({ skill, frontmatter, permissions, repository, tag, sourceRef }) { + const homepage = skill.homepage || frontmatter.homepage || `https://github.com/${repository}`; + const supportRef = `${repository}@${tag || sourceRef}`; + const licenseRef = `https://github.com/${repository}/blob/${tag || sourceRef}/LICENSE`; + const outputTypes = ["Markdown instructions", "release artifact files"]; + if (permissions.capabilities.length > 0) { + outputTypes.push("local security findings or status reports"); + } + + return `# Skill Card + +## Description + +The \`${skill.name}\` skill provides this capability: ${skill.description} + +This skill is intended for operator-reviewed security workflows, not unattended production mutation without the review steps declared in the skill instructions. + +## Owner + +prompt-security + +## License/Terms of Use + +${skill.license} + +License reference: ${licenseRef} + +Project homepage: ${homepage} + +## Use Case + +Use this skill for ${permissions.platform} workflows where an agent or operator needs the capability described in \`${skill.name}\`. + +## Deployment Geography for Use + +Global, subject to the operator's local compliance, network, and data-handling requirements. + +## Known Risks and Mitigations + +Risk: The skill may run commands, inspect local files, install hooks, or fetch remote security metadata depending on the workflow. + +Mitigation: Review \`permissions.json\`, \`SKILL.md\`, and the signed \`checksums.json\` before enabling the skill. Keep high-impact actions approval-gated. + +Risk: Security findings and remediation guidance can be incomplete or wrong. + +Mitigation: Treat output as operator guidance. Review proposed removals, installs, configuration changes, and reports before acting. + +## References + +- Source release: ${supportRef} +- Skill instructions: SKILL.md +- Permission summary: permissions.json +- SkillSpector scan: skillspector-report.md +- Signed release manifest: checksums.json and checksums.sig + +## Skill Output + +Output type(s): ${outputTypes.join(", ")} + +Output format: Markdown, JSON, shell commands, or local files as documented by the skill. + +Output parameters: See \`SKILL.md\`, \`permissions.json\`, and release checksums for exact files and side effects. + +Other properties: Release assets are covered by signed SHA-256 checksums. + +## Skill Version + +${skill.version}${tag ? ` (${tag})` : ""} + +## Ethical Considerations + +Use this skill only on systems, agents, repositories, and workspaces where you have authorization. Review generated security reports before sharing them because they may contain operational details. +`; +} + +function buildInstallDoc({ skill, repository, tag, sourceRef }) { + const refSuffix = sourceRef && sourceRef !== "main" ? `#${sourceRef}` : ""; + const source = `${repository}${refSuffix}`; + const releaseUrl = tag ? `https://github.com/${repository}/releases/tag/${tag}` : `https://github.com/${repository}`; + const agent = installAgentForSkill(skill); + + return `# Install and Update ${skill.name} + +## Install With Agent Skills CLI + +Harness-aware global install: + +${codeBlock(`npx skills add ${source} --skill ${skill.name} --agent ${agent} --global --yes`)} + +Project-local install for compatible agents: + +${codeBlock(`npx skills add ${source} --skill ${skill.name} --yes`)} + +## Update + +Update this skill when installed through the Skills CLI: + +${codeBlock(`npx skills update ${skill.name}`)} + +List installed skills: + +${codeBlock("npx skills list")} + +## Verify Release Artifact + +When installing from a GitHub release instead of the Skills CLI, download the archive, \`checksums.json\`, \`checksums.sig\`, and \`signing-public.pem\` from: + +${releaseUrl} + +Verify \`checksums.json\` before trusting the archive or standalone files. +`; +} + +async function main() { + const args = parseArgs(process.argv.slice(2)); + const skillDir = path.resolve(args.skillDir); + const outputDir = path.resolve(args.outputDir); + + const skillJsonPath = path.join(skillDir, "skill.json"); + const skillMdPath = path.join(skillDir, "SKILL.md"); + const [skillJsonRaw, skillMdRaw] = await Promise.all([ + readFile(skillJsonPath, "utf8"), + readFile(skillMdPath, "utf8"), + ]); + + const skill = JSON.parse(skillJsonRaw); + const frontmatter = parseFrontmatter(skillMdRaw); + skill.name = requireField(skill, "name"); + skill.version = requireField(skill, "version"); + skill.description = requireField(skill, "description"); + skill.license = requireField(skill, "license"); + + const platform = detectPlatform(skill); + const metadata = platformMetadata(skill, platform); + const generatedAt = new Date().toISOString(); + const permissions = buildPermissions({ skill, metadata, platform, generatedAt }); + + await mkdir(outputDir, { recursive: true }); + await Promise.all([ + writeFile( + path.join(outputDir, "permissions.json"), + `${JSON.stringify(permissions, null, 2)}\n`, + ), + writeFile( + path.join(outputDir, "skill-card.md"), + buildSkillCard({ + skill, + frontmatter, + permissions, + repository: args.repository, + tag: args.tag, + sourceRef: args.sourceRef, + }), + ), + writeFile( + path.join(outputDir, "install.md"), + buildInstallDoc({ + skill, + repository: args.repository, + tag: args.tag, + sourceRef: args.sourceRef, + }), + ), + ]); + + console.log(`Generated release trust packet for ${skill.name} in ${outputDir}`); +} + +main().catch((error) => { + console.error(error.message); + process.exit(1); +}); diff --git a/scripts/ci/simulate_skill_tag_release.mjs b/scripts/ci/simulate_skill_tag_release.mjs new file mode 100644 index 0000000..00a88c1 --- /dev/null +++ b/scripts/ci/simulate_skill_tag_release.mjs @@ -0,0 +1,520 @@ +#!/usr/bin/env node +import { createHash } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { + cp, + mkdir, + mkdtemp, + readFile, + rm, + stat, + writeFile, +} from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; + +const TRUST_ARTIFACTS = [ + "skill-card.md", + "permissions.json", + "install.md", + "skillspector-report.md", +]; + +function usage() { + return [ + "Usage: node scripts/ci/simulate_skill_tag_release.mjs [options]", + "", + "Options:", + " --repository Source repository used in release metadata", + " --source-ref Source ref used in npx skills examples", + " --skillspector-bin SkillSpector executable to run", + ].join("\n"); +} + +function parseArgs(argv) { + const positional = []; + const options = { + repository: "prompt-security/clawsec", + sourceRef: "main", + skillspectorBin: "skillspector", + }; + + for (let i = 0; i < argv.length; i += 1) { + const token = argv[i]; + if (token === "--repository") { + options.repository = argv[++i]; + } else if (token === "--source-ref") { + options.sourceRef = argv[++i]; + } else if (token === "--skillspector-bin") { + options.skillspectorBin = argv[++i]; + } else if (token === "--help" || token === "-h") { + console.log(usage()); + process.exit(0); + } else if (token.startsWith("--")) { + throw new Error(`Unknown option: ${token}`); + } else { + positional.push(token); + } + } + + if (positional.length !== 2) { + throw new Error(usage()); + } + + return { + skillDir: positional[0], + outputDir: positional[1], + ...options, + }; +} + +function run(command, args, options = {}) { + const result = spawnSync(command, args, { + encoding: "utf8", + ...options, + }); + + if (result.status !== 0) { + throw new Error( + [ + `Command failed: ${command} ${args.join(" ")}`, + result.stdout ? `stdout:\n${result.stdout}` : "", + result.stderr ? `stderr:\n${result.stderr}` : "", + ].filter(Boolean).join("\n"), + ); + } + + return result.stdout; +} + +function runAllowFailure(command, args, options = {}) { + return spawnSync(command, args, { + encoding: "utf8", + ...options, + }); +} + +function nextSimulatedReleaseVersion(version) { + const versionMatch = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9]+))?$/); + if (!versionMatch) { + throw new Error(`Cannot derive simulated release version from unsupported version: ${version}`); + } + + const [, major, minor, patch, prerelease] = versionMatch; + if (!prerelease) { + return `${major}.${minor}.${Number(patch) + 1}`; + } + + const prereleaseMatch = prerelease.match(/^(.*?)(\d+)$/); + if (prereleaseMatch) { + const [, label, number] = prereleaseMatch; + return `${major}.${minor}.${patch}-${label}${Number(number) + 1}`; + } + + return `${major}.${minor}.${patch}-${prerelease}1`; +} + +function normalizeReleasePath(rawPath) { + let releasePath = rawPath.replaceAll("\\", "/"); + while (releasePath.startsWith("./")) { + releasePath = releasePath.slice(2); + } + while (releasePath.includes("//")) { + releasePath = releasePath.replaceAll("//", "/"); + } + + if ( + releasePath === "" || + releasePath.startsWith("/") || + /^[A-Za-z]:/.test(releasePath) || + releasePath === ".." || + releasePath.startsWith("../") || + releasePath.endsWith("/..") || + releasePath.includes("/../") + ) { + throw new Error(`Unsafe release path: ${rawPath}`); + } + + return releasePath; +} + +function isTestReleasePath(releasePath) { + const lower = releasePath.toLowerCase(); + return lower === "test" || + lower === "tests" || + lower.startsWith("test/") || + lower.startsWith("tests/") || + lower.includes("/test/") || + lower.includes("/tests/"); +} + +async function sha256File(filePath) { + const buffer = await readFile(filePath); + return createHash("sha256").update(buffer).digest("hex"); +} + +async function fileSize(filePath) { + return (await stat(filePath)).size; +} + +async function checksumEntry(filePath, releasePath) { + return { + sha256: await sha256File(filePath), + size: await fileSize(filePath), + path: releasePath, + }; +} + +function replaceSkillMarkdownVersion(markdown, version) { + if (!markdown.startsWith("---\n")) { + throw new Error("SKILL.md is missing YAML frontmatter"); + } + + const end = markdown.indexOf("\n---", 4); + if (end === -1) { + throw new Error("SKILL.md frontmatter is not closed"); + } + + const frontmatter = markdown.slice(0, end); + if (!/^version:\s*.+$/m.test(frontmatter)) { + throw new Error("SKILL.md frontmatter is missing a version field"); + } + + return markdown.replace(/^version:\s*.+$/m, `version: ${version}`); +} + +async function addSimulatedChangelogEntry(skillDir, version) { + const changelogPath = path.join(skillDir, "CHANGELOG.md"); + if (!existsSync(changelogPath)) { + return; + } + + const today = new Date().toISOString().slice(0, 10); + const original = await readFile(changelogPath, "utf8"); + if (original.includes(`## [${version}] -`)) { + return; + } + + const entry = [ + `## [${version}] - ${today}`, + "", + "- Simulated prerelease build for release-pipeline validation.", + "", + "---", + "", + ].join("\n"); + + await writeFile(changelogPath, `${entry}${original}`); +} + +async function writeJson(filePath, value) { + await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`); +} + +async function signFileBase64({ keyPath, inputPath, outputPath, tempRoot }) { + const sigBin = path.join(tempRoot, `${path.basename(outputPath)}.bin`); + run("openssl", ["pkeyutl", "-sign", "-rawin", "-inkey", keyPath, "-in", inputPath, "-out", sigBin]); + run("openssl", ["base64", "-A", "-in", sigBin, "-out", outputPath]); + await rm(sigBin, { force: true }); +} + +async function verifyFileBase64Signature({ publicKeyPath, inputPath, signaturePath, tempRoot }) { + const sigBin = path.join(tempRoot, `${path.basename(signaturePath)}.verify.bin`); + run("openssl", ["base64", "-d", "-A", "-in", signaturePath, "-out", sigBin]); + run("openssl", [ + "pkeyutl", + "-verify", + "-rawin", + "-pubin", + "-inkey", + publicKeyPath, + "-sigfile", + sigBin, + "-in", + inputPath, + ]); + await rm(sigBin, { force: true }); +} + +async function createSigningKeyPair(tempRoot) { + const keyDir = await mkdtemp(path.join(tempRoot, "signing-")); + const privateKeyPath = path.join(keyDir, "private.pem"); + const publicKeyPath = path.join(keyDir, "public.pem"); + + run("openssl", ["genpkey", "-algorithm", "ED25519", "-out", privateKeyPath]); + run("openssl", ["pkey", "-in", privateKeyPath, "-pubout", "-out", publicKeyPath]); + + return { privateKeyPath, publicKeyPath }; +} + +async function signAdvisoryArtifacts(skillDir, tempRoot) { + const advisoryDir = path.join(skillDir, "advisories"); + const feedPath = path.join(advisoryDir, "feed.json"); + if (!existsSync(feedPath)) { + return; + } + + const { privateKeyPath, publicKeyPath } = await createSigningKeyPair(tempRoot); + const feedSignaturePath = path.join(advisoryDir, "feed.json.sig"); + const checksumsPath = path.join(advisoryDir, "checksums.json"); + const checksumsSignaturePath = path.join(advisoryDir, "checksums.json.sig"); + const publicKeyOutputPath = path.join(advisoryDir, "feed-signing-public.pem"); + + await signFileBase64({ + keyPath: privateKeyPath, + inputPath: feedPath, + outputPath: feedSignaturePath, + tempRoot, + }); + await verifyFileBase64Signature({ + publicKeyPath, + inputPath: feedPath, + signaturePath: feedSignaturePath, + tempRoot, + }); + + await writeJson(checksumsPath, { + schema_version: "1", + algorithm: "sha256", + version: "simulation", + generated_at: new Date().toISOString(), + files: { + "advisories/feed.json": await checksumEntry(feedPath, "advisories/feed.json"), + "advisories/feed.json.sig": await checksumEntry(feedSignaturePath, "advisories/feed.json.sig"), + }, + }); + + await signFileBase64({ + keyPath: privateKeyPath, + inputPath: checksumsPath, + outputPath: checksumsSignaturePath, + tempRoot, + }); + await verifyFileBase64Signature({ + publicKeyPath, + inputPath: checksumsPath, + signaturePath: checksumsSignaturePath, + tempRoot, + }); + + await cp(publicKeyPath, publicKeyOutputPath); +} + +async function addReleaseAssetChecksum({ releaseAssetsDir, manifest, asset }) { + const filePath = path.join(releaseAssetsDir, asset); + if (!existsSync(filePath) || (await fileSize(filePath)) === 0) { + throw new Error(`Required release trust artifact is missing or empty: ${filePath}`); + } + + manifest.files[asset] = await checksumEntry(filePath, asset); +} + +async function stageSbomFiles({ skillDir, innerDir, sbomFiles }) { + for (const entry of sbomFiles) { + const releasePath = normalizeReleasePath(entry.path); + if (isTestReleasePath(releasePath)) { + continue; + } + + const fullPath = path.join(skillDir, releasePath); + if (!existsSync(fullPath)) { + throw new Error(`SBOM references missing file: ${releasePath}`); + } + + const destination = path.join(innerDir, releasePath); + await mkdir(path.dirname(destination), { recursive: true }); + await cp(fullPath, destination); + } +} + +async function buildFilesManifest({ skillDir, skillJsonPath, sbomFiles }) { + const files = {}; + for (const entry of sbomFiles) { + const releasePath = normalizeReleasePath(entry.path); + if (isTestReleasePath(releasePath)) { + continue; + } + + const fullPath = path.join(skillDir, releasePath); + if (existsSync(fullPath)) { + files[releasePath] = await checksumEntry(fullPath, releasePath); + } + } + + files["skill.json"] = { + sha256: await sha256File(skillJsonPath), + size: await fileSize(skillJsonPath), + }; + + return files; +} + +async function runSkillSpector({ skillspectorBin, skillDir, reportPath }) { + const result = runAllowFailure(skillspectorBin, [ + "scan", + skillDir, + "--no-llm", + "--format", + "markdown", + "--output", + reportPath, + ]); + + if (!existsSync(reportPath) || (await fileSize(reportPath)) === 0) { + throw new Error( + [ + "SkillSpector did not produce a report.", + result.stdout ? `stdout:\n${result.stdout}` : "", + result.stderr ? `stderr:\n${result.stderr}` : "", + ].filter(Boolean).join("\n"), + ); + } + + if (result.status !== 0) { + console.warn(`SkillSpector returned exit code ${result.status}; report is included for review.`); + } +} + +async function main() { + const args = parseArgs(process.argv.slice(2)); + const sourceSkillDir = path.resolve(args.skillDir); + const outputDir = path.resolve(args.outputDir); + const releaseAssetsDir = path.join(outputDir, "release-assets"); + const tempRoot = await mkdtemp(path.join(tmpdir(), "clawsec-release-sim-")); + + try { + const skillName = path.basename(sourceSkillDir); + const tempSkillDir = path.join(tempRoot, skillName); + await cp(sourceSkillDir, tempSkillDir, { recursive: true }); + + const skillJsonPath = path.join(tempSkillDir, "skill.json"); + const skillMdPath = path.join(tempSkillDir, "SKILL.md"); + const skill = JSON.parse(await readFile(skillJsonPath, "utf8")); + const originalVersion = skill.version; + const simulatedVersion = nextSimulatedReleaseVersion(originalVersion); + const tag = `${skillName}-v${simulatedVersion}`; + const zipName = `${tag}.zip`; + + skill.version = simulatedVersion; + await writeJson(skillJsonPath, skill); + await writeFile( + skillMdPath, + replaceSkillMarkdownVersion(await readFile(skillMdPath, "utf8"), simulatedVersion), + ); + await addSimulatedChangelogEntry(tempSkillDir, simulatedVersion); + await signAdvisoryArtifacts(tempSkillDir, tempRoot); + + if (!skill.sbom || !Array.isArray(skill.sbom.files)) { + throw new Error(`skill.json missing required release field: sbom.files`); + } + + await mkdir(releaseAssetsDir, { recursive: true }); + + const stagingDir = await mkdtemp(path.join(tempRoot, "staging-")); + const innerDir = path.join(stagingDir, skillName); + await mkdir(innerDir, { recursive: true }); + await stageSbomFiles({ + skillDir: tempSkillDir, + innerDir, + sbomFiles: skill.sbom.files, + }); + await cp(skillJsonPath, path.join(innerDir, "skill.json")); + + run("python3", ["scripts/ci/verify_skill_release_import_closure.py", innerDir], { + cwd: process.cwd(), + }); + + run("zip", ["-qr", path.join(releaseAssetsDir, zipName), "."], { + cwd: stagingDir, + }); + + const zipContents = run("unzip", ["-Z1", path.join(releaseAssetsDir, zipName)]); + if (zipContents.split("\n").some((entry) => /(^|\/)(test|tests)\//i.test(entry))) { + throw new Error(`Simulated release archive contains test-only files: ${zipName}`); + } + + const manifest = { + skill: skillName, + version: simulatedVersion, + generated_at: new Date().toISOString(), + repository: args.repository, + tag, + archive: { + filename: zipName, + sha256: await sha256File(path.join(releaseAssetsDir, zipName)), + size: await fileSize(path.join(releaseAssetsDir, zipName)), + url: `https://github.com/${args.repository}/releases/download/${tag}/${zipName}`, + }, + files: await buildFilesManifest({ + skillDir: tempSkillDir, + skillJsonPath, + sbomFiles: skill.sbom.files, + }), + }; + + await writeJson(path.join(releaseAssetsDir, "checksums.json"), manifest); + + run(process.execPath, [ + "scripts/ci/generate_skill_release_trust_packet.mjs", + tempSkillDir, + releaseAssetsDir, + "--repository", + args.repository, + "--tag", + tag, + "--source-ref", + args.sourceRef, + ]); + + await runSkillSpector({ + skillspectorBin: args.skillspectorBin, + skillDir: tempSkillDir, + reportPath: path.join(releaseAssetsDir, "skillspector-report.md"), + }); + + for (const artifact of TRUST_ARTIFACTS) { + await addReleaseAssetChecksum({ releaseAssetsDir, manifest, asset: artifact }); + } + await writeJson(path.join(releaseAssetsDir, "checksums.json"), manifest); + + await cp(skillJsonPath, path.join(releaseAssetsDir, "skill.json")); + await cp(skillMdPath, path.join(releaseAssetsDir, "SKILL.md")); + if (existsSync(path.join(tempSkillDir, "README.md"))) { + await cp(path.join(tempSkillDir, "README.md"), path.join(releaseAssetsDir, "README.md")); + } + + const { privateKeyPath, publicKeyPath } = await createSigningKeyPair(tempRoot); + await signFileBase64({ + keyPath: privateKeyPath, + inputPath: path.join(releaseAssetsDir, "checksums.json"), + outputPath: path.join(releaseAssetsDir, "checksums.sig"), + tempRoot, + }); + await verifyFileBase64Signature({ + publicKeyPath, + inputPath: path.join(releaseAssetsDir, "checksums.json"), + signaturePath: path.join(releaseAssetsDir, "checksums.sig"), + tempRoot, + }); + await cp(publicKeyPath, path.join(releaseAssetsDir, "signing-public.pem")); + + await writeJson(path.join(outputDir, "simulation-summary.json"), { + skill: skillName, + original_version: originalVersion, + simulated_version: simulatedVersion, + tag, + release_assets: path.relative(outputDir, releaseAssetsDir), + archive: `release-assets/${zipName}`, + }); + + console.log(`Simulated tag release build for ${skillName}: ${tag}`); + } finally { + await rm(tempRoot, { recursive: true, force: true }); + } +} + +main().catch((error) => { + console.error(error.message); + process.exit(1); +}); diff --git a/scripts/ci/validate_skill_install_docs.mjs b/scripts/ci/validate_skill_install_docs.mjs new file mode 100644 index 0000000..0ea5ecc --- /dev/null +++ b/scripts/ci/validate_skill_install_docs.mjs @@ -0,0 +1,316 @@ +#!/usr/bin/env node +import { readFile, readdir } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { spawnSync } from "node:child_process"; +import https from "node:https"; +import path from "node:path"; + +const DEFAULT_REPOSITORY = "prompt-security/clawsec"; +const DEFAULT_AGENT_TYPES_URL = "https://raw.githubusercontent.com/vercel-labs/skills/main/src/types.ts"; +const DOC_FILENAMES = ["README.md", "SKILL.md"]; +const KNOWN_PLATFORM_KEYS = ["openclaw", "nanoclaw", "picoclaw", "hermes"]; +const PLATFORM_AGENT_ALIASES = new Map([["hermes", "hermes-agent"]]); + +function usage() { + return [ + "Usage: node scripts/ci/validate_skill_install_docs.mjs [options]", + "", + "Options:", + " --root Repository root. Defaults to current working directory.", + " --repository Expected npx skills source. Defaults to prompt-security/clawsec.", + " --base Base ref for changed-skill detection.", + " --head Head ref for changed-skill detection.", + " --skills Skill directories to validate.", + " --all Validate every skill directory with skill.json.", + " --agent-types-file Read Vercel AgentType source from a local file.", + " --agent-types-url Read Vercel AgentType source from a URL.", + ].join("\n"); +} + +function parseArgs(argv) { + const options = { + root: process.cwd(), + repository: DEFAULT_REPOSITORY, + base: process.env.BASE_SHA || "", + head: process.env.HEAD_SHA || "", + skillDirs: [], + all: false, + agentTypesFile: "", + agentTypesUrl: DEFAULT_AGENT_TYPES_URL, + }; + + for (let i = 0; i < argv.length; i += 1) { + const token = argv[i]; + if (token === "--root") { + options.root = argv[++i]; + } else if (token === "--repository") { + options.repository = argv[++i]; + } else if (token === "--base") { + options.base = argv[++i]; + } else if (token === "--head") { + options.head = argv[++i]; + } else if (token === "--skills") { + options.skillDirs.push(...argv[++i].split(",").map((item) => item.trim()).filter(Boolean)); + } else if (token === "--all") { + options.all = true; + } else if (token === "--agent-types-file") { + options.agentTypesFile = argv[++i]; + } else if (token === "--agent-types-url") { + options.agentTypesUrl = argv[++i]; + } else if (token === "--help" || token === "-h") { + console.log(usage()); + process.exit(0); + } else { + throw new Error(`Unknown option: ${token}\n${usage()}`); + } + } + + return { + ...options, + root: path.resolve(options.root), + }; +} + +function fetchText(url) { + return new Promise((resolve, reject) => { + https + .get(url, (response) => { + if (response.statusCode !== 200) { + reject(new Error(`Failed to fetch ${url}: HTTP ${response.statusCode}`)); + response.resume(); + return; + } + + response.setEncoding("utf8"); + let body = ""; + response.on("data", (chunk) => { + body += chunk; + }); + response.on("end", () => resolve(body)); + }) + .on("error", reject); + }); +} + +async function readAgentTypeSource(options) { + if (options.agentTypesFile) { + return readFile(path.resolve(options.agentTypesFile), "utf8"); + } + + return fetchText(options.agentTypesUrl); +} + +function parseAgentTypes(source) { + const match = source.match(/export\s+type\s+AgentType\s*=\s*([\s\S]*?);/); + if (!match) { + throw new Error("Could not find export type AgentType in Vercel skills type source."); + } + + const agents = new Set(); + const agentTypeBody = match[1]; + for (const agentMatch of agentTypeBody.matchAll(/['"]([^'"]+)['"]/g)) { + agents.add(agentMatch[1]); + } + + if (agents.size === 0) { + throw new Error("Vercel AgentType list was empty."); + } + + return agents; +} + +async function listAllSkillDirs(root) { + const skillsRoot = path.join(root, "skills"); + const entries = await readdir(skillsRoot, { withFileTypes: true }); + return entries + .filter((entry) => entry.isDirectory()) + .map((entry) => `skills/${entry.name}`) + .filter((skillDir) => existsSync(path.join(root, skillDir, "skill.json"))) + .sort(); +} + +function changedSkillDirs({ root, base, head }) { + if (!base || !head) { + throw new Error("Provide --skills, --all, or both --base and --head for changed-skill detection."); + } + + const result = spawnSync( + "git", + [ + "-C", + root, + "diff", + "--name-only", + `${base}...${head}`, + "--", + "skills/*/**", + ":(exclude)skills/*/test/**", + ":(exclude)skills/*/tests/**", + ], + { encoding: "utf8" }, + ); + + if (result.status !== 0) { + throw new Error(`git diff failed\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`); + } + + return [ + ...new Set( + result.stdout + .split("\n") + .map((line) => line.trim()) + .filter(Boolean) + .map((filePath) => filePath.split("/").slice(0, 2).join("/")) + .filter((skillDir) => /^skills\/[^/]+$/.test(skillDir)), + ), + ].sort(); +} + +async function readJson(filePath) { + return JSON.parse(await readFile(filePath, "utf8")); +} + +function collectDeclaredPlatforms(skill) { + const platforms = new Set(); + + if (typeof skill.platform === "string" && skill.platform.trim()) { + platforms.add(skill.platform.trim()); + } + + if (Array.isArray(skill.platforms)) { + for (const platform of skill.platforms) { + if (typeof platform === "string" && platform.trim()) { + platforms.add(platform.trim()); + } + } + } + + for (const key of KNOWN_PLATFORM_KEYS) { + if (skill[key] && typeof skill[key] === "object") { + platforms.add(key); + } + } + + return [...platforms]; +} + +function agentForSkill(skill, agentTypes) { + const platforms = collectDeclaredPlatforms(skill); + if (platforms.length === 0) { + return "openclaw"; + } + + const matchedAgents = new Set(); + let allPlatformsMatched = true; + + for (const platform of platforms) { + const aliasedPlatform = PLATFORM_AGENT_ALIASES.get(platform) || platform; + if (agentTypes.has(aliasedPlatform)) { + matchedAgents.add(aliasedPlatform); + } else { + allPlatformsMatched = false; + } + } + + if (allPlatformsMatched && matchedAgents.size === 1) { + return [...matchedAgents][0]; + } + + return "openclaw"; +} + +function hasRequiredCommand(markdown, { repository, skillName, agent }) { + return markdown + .split("\n") + .map((line) => line.replace(/\s+/g, " ").trim()) + .filter((line) => line.includes("npx skills add")) + .some((line) => { + return ( + line.includes(`npx skills add ${repository}`) && + line.includes(`--skill ${skillName}`) && + (line.includes(`-a ${agent}`) || line.includes(`--agent ${agent}`)) && + (line.includes(" -y") || line.includes(" --yes")) + ); + }); +} + +async function validateSkill({ root, skillDir, repository, agentTypes }) { + const skillJsonPath = path.join(root, skillDir, "skill.json"); + const skill = await readJson(skillJsonPath); + const skillName = skill.name || path.basename(skillDir); + const agent = agentForSkill(skill, agentTypes); + const command = `npx skills add ${repository} --skill ${skillName} -a ${agent} -y`; + const failures = []; + + for (const filename of DOC_FILENAMES) { + const docPath = path.join(root, skillDir, filename); + if (!existsSync(docPath)) { + failures.push(`Missing required install documentation file: ${path.join(skillDir, filename)}`); + continue; + } + + const markdown = await readFile(docPath, "utf8"); + if (!hasRequiredCommand(markdown, { repository, skillName, agent })) { + failures.push(`Missing required npx skills install command in ${path.join(skillDir, filename)}: ${command}`); + } + } + + return { + skillDir, + skillName, + agent, + failures, + }; +} + +async function main() { + const options = parseArgs(process.argv.slice(2)); + const agentTypes = parseAgentTypes(await readAgentTypeSource(options)); + let skillDirs = options.skillDirs; + + if (options.all) { + skillDirs = await listAllSkillDirs(options.root); + } else if (skillDirs.length === 0) { + skillDirs = changedSkillDirs(options); + } + + if (skillDirs.length === 0) { + console.log("No skill install docs to validate."); + return; + } + + const results = []; + for (const skillDir of skillDirs) { + const skillJsonPath = path.join(options.root, skillDir, "skill.json"); + if (!existsSync(skillJsonPath)) { + console.log(`Skipping removed skill directory: ${skillDir}`); + continue; + } + + results.push( + await validateSkill({ + root: options.root, + skillDir, + repository: options.repository, + agentTypes, + }), + ); + } + + const failures = results.flatMap((result) => result.failures); + if (failures.length > 0) { + for (const failure of failures) { + console.error(`::error::${failure}`); + } + throw new Error(`Found ${failures.length} npx skills install documentation issue(s).`); + } + + for (const result of results) { + console.log(`npx skills install docs OK for ${result.skillName}: -a ${result.agent}`); + } +} + +main().catch((error) => { + console.error(error.message); + process.exit(1); +}); diff --git a/scripts/test-skill-install-docs.mjs b/scripts/test-skill-install-docs.mjs new file mode 100644 index 0000000..845759d --- /dev/null +++ b/scripts/test-skill-install-docs.mjs @@ -0,0 +1,137 @@ +import assert from "node:assert/strict"; +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; + +const validator = "scripts/ci/validate_skill_install_docs.mjs"; +const workflow = await readFile(".github/workflows/skill-release.yml", "utf8"); +const tempRoot = await mkdtemp(path.join(tmpdir(), "clawsec-install-docs-")); +const agentTypesPath = path.join(tempRoot, "vercel-types.ts"); + +function runValidator(args) { + return spawnSync( + process.execPath, + [validator, "--root", tempRoot, "--agent-types-file", agentTypesPath, ...args], + { + encoding: "utf8", + }, + ); +} + +async function writeSkill({ name, metadata, readme, skillMd }) { + const skillDir = path.join(tempRoot, "skills", name); + await mkdir(skillDir, { recursive: true }); + await writeFile( + path.join(skillDir, "skill.json"), + JSON.stringify( + { + name, + version: "1.0.0", + description: `${name} test skill`, + license: "AGPL-3.0-or-later", + ...metadata, + }, + null, + 2, + ), + ); + await writeFile(path.join(skillDir, "README.md"), readme); + await writeFile(path.join(skillDir, "SKILL.md"), skillMd); +} + +try { + await writeFile( + agentTypesPath, + "export type AgentType = | 'codex' | 'hermes-agent' | 'openclaw' | 'universal';\n", + ); + + await writeSkill({ + name: "hermes-example", + metadata: { hermes: { category: "security" } }, + readme: "# Hermes Example\n\n## Installation\n\nMissing the Skills CLI command.\n", + skillMd: "---\nname: hermes-example\nversion: 1.0.0\n---\n\n## Installation\n\nMissing the Skills CLI command.\n", + }); + + const missingHermes = runValidator(["--skills", "skills/hermes-example"]); + assert.equal(missingHermes.status, 1, "missing Hermes install docs must fail validation"); + assert.match( + missingHermes.stderr, + /npx skills add prompt-security\/clawsec --skill hermes-example -a hermes-agent -y/, + "Hermes skills must require the hermes-agent installer target", + ); + + await writeSkill({ + name: "hermes-example", + metadata: { hermes: { category: "security" } }, + readme: + "# Hermes Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill hermes-example -a hermes-agent -y\n```\n", + skillMd: + "---\nname: hermes-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill hermes-example -a hermes-agent -y\n```\n", + }); + + const validHermes = runValidator(["--skills", "skills/hermes-example"]); + assert.equal( + validHermes.status, + 0, + `valid Hermes install docs should pass\nstdout:\n${validHermes.stdout}\nstderr:\n${validHermes.stderr}`, + ); + + await writeSkill({ + name: "codex-example", + metadata: { platform: "codex" }, + readme: + "# Codex Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill codex-example -a openclaw -y\n```\n", + skillMd: + "---\nname: codex-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill codex-example -a openclaw -y\n```\n", + }); + + const wrongExactTarget = runValidator(["--skills", "skills/codex-example"]); + assert.equal(wrongExactTarget.status, 1, "exact AgentType matches must use their matched target"); + assert.match( + wrongExactTarget.stderr, + /npx skills add prompt-security\/clawsec --skill codex-example -a codex -y/, + "Exact AgentType matches must not fall back to openclaw", + ); + + await writeSkill({ + name: "nanoclaw-example", + metadata: { platform: "nanoclaw", nanoclaw: { category: "security" } }, + readme: + "# NanoClaw Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a hermes-agent -y\n```\n", + skillMd: + "---\nname: nanoclaw-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a hermes-agent -y\n```\n", + }); + + const wrongNanoTarget = runValidator(["--skills", "skills/nanoclaw-example"]); + assert.equal(wrongNanoTarget.status, 1, "NanoClaw docs must fail when they use the Hermes target"); + assert.match( + wrongNanoTarget.stderr, + /npx skills add prompt-security\/clawsec --skill nanoclaw-example -a openclaw -y/, + "NanoClaw skills must install through the openclaw target", + ); + + await writeSkill({ + name: "nanoclaw-example", + metadata: { platform: "nanoclaw", nanoclaw: { category: "security" } }, + readme: + "# NanoClaw Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a openclaw -y\n```\n", + skillMd: + "---\nname: nanoclaw-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a openclaw -y\n```\n", + }); + + const validNano = runValidator(["--skills", "skills/nanoclaw-example"]); + assert.equal( + validNano.status, + 0, + `valid NanoClaw install docs should pass\nstdout:\n${validNano.stdout}\nstderr:\n${validNano.stderr}`, + ); + + assert.match( + workflow, + /Validate npx skills install docs/, + "Skill release workflow must run the install-doc validator", + ); +} finally { + await rm(tempRoot, { recursive: true, force: true }); +} diff --git a/scripts/test-skill-release-workflow.mjs b/scripts/test-skill-release-workflow.mjs index 7423313..ac2e0b1 100644 --- a/scripts/test-skill-release-workflow.mjs +++ b/scripts/test-skill-release-workflow.mjs @@ -2,7 +2,9 @@ import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; const workflowPath = new URL('../.github/workflows/skill-release.yml', import.meta.url); +const ciWorkflowPath = new URL('../.github/workflows/ci.yml', import.meta.url); const workflow = await readFile(workflowPath, 'utf8'); +const ciWorkflow = await readFile(ciWorkflowPath, 'utf8'); assert.match( workflow, @@ -10,6 +12,22 @@ assert.match( 'Skill release workflow must run when any skill package file changes', ); +assert.match( + workflow, + /pull_request:[\s\S]*paths:[\s\S]*- '\.github\/workflows\/skill-release\.yml'[\s\S]*- 'scripts\/ci\/\*\*'/, + 'Skill release workflow must also run when the release pipeline itself changes', +); + +assert.ok( + ciWorkflow.includes(` - name: Skill Release Tooling Tests + run: | + set -euo pipefail + for test_file in scripts/test-skill-*.mjs; do + node "$test_file" + done`), + 'CI must run every scripts/test-skill-*.mjs file so new skill release tests are not orphaned', +); + assert.match( workflow, /git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/, @@ -27,3 +45,83 @@ assert.match( /::error file=\$\{skill_dir\}::Changed skill package has no version bump\./, 'Skill release validation must emit an explicit missing-version-bump error', ); + +assert.match( + workflow, + /Install SkillSpector/, + 'Skill release workflow must install SkillSpector before publishing release evidence', +); + +assert.match( + workflow, + /Generate SkillSpector report/, + 'Skill release workflow must generate a SkillSpector report for each released skill', +); + +assert.match( + workflow, + /Generate release trust packet/, + 'Skill release workflow must generate skill cards, permission summaries, and npx install instructions', +); + +for (const artifact of ['skill-card.md', 'permissions.json', 'install.md', 'skillspector-report.md']) { + assert.match( + workflow, + new RegExp(`release-assets/${artifact.replace('.', '\\.')}`), + `Skill release workflow must publish ${artifact} in release assets`, + ); +} + +const escapeRegExp = (literal) => literal.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + +for (const artifact of ['skill-card.md', 'permissions.json', 'install.md', 'skillspector-report.md']) { + assert.match( + workflow, + new RegExp( + String.raw`if ! add_release_asset_checksum "\$\{out_assets\}" "${escapeRegExp(artifact)}"; then` + + String.raw`[\s\S]*?failures=\$\(\(failures \+ 1\)\)[\s\S]*?continue[\s\S]*?fi`, + ), + `PR dry-run validation must aggregate and continue when ${artifact} cannot be checksummed`, + ); +} + +assert.match( + workflow, + /add_release_asset_checksum "skill-card\.md"/, + 'Skill card must be included in the signed checksums manifest', +); + +assert.match( + workflow, + /add_release_asset_checksum "permissions\.json"/, + 'Permissions summary must be included in the signed checksums manifest', +); + +assert.match( + workflow, + /add_release_asset_checksum "install\.md"/, + 'npx install/update instructions must be included in the signed checksums manifest', +); + +assert.match( + workflow, + /add_release_asset_checksum "skillspector-report\.md"/, + 'SkillSpector report must be included in the signed checksums manifest', +); + +assert.match( + workflow, + /Simulate tag release build/, + 'Skill release workflow must simulate a tag release build during PR validation', +); + +assert.match( + workflow, + /simulate_skill_tag_release\.mjs/, + 'Skill release workflow must call the tag release simulation script', +); + +assert.ok( + workflow.includes('simulated_version | test("^[0-9]+\\\\.[0-9]+\\\\.[0-9]+(-[a-zA-Z0-9]+)?$")'), + 'Skill release workflow must accept every prerelease version format that release-skill.sh accepts', +); diff --git a/scripts/test-skill-tag-release-simulation.mjs b/scripts/test-skill-tag-release-simulation.mjs new file mode 100644 index 0000000..0d12166 --- /dev/null +++ b/scripts/test-skill-tag-release-simulation.mjs @@ -0,0 +1,155 @@ +import assert from "node:assert/strict"; +import { chmod, cp, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; + +const tempRoot = await mkdtemp(path.join(tmpdir(), "clawsec-tag-release-sim-")); +const fakeSkillspector = path.join(tempRoot, "skillspector"); + +async function prereleaseFixture(sourceSkillDir, version, fixtureGroup) { + const fixtureDir = path.join(tempRoot, fixtureGroup, path.basename(sourceSkillDir)); + await cp(sourceSkillDir, fixtureDir, { recursive: true }); + + const skillJsonPath = path.join(fixtureDir, "skill.json"); + const skill = JSON.parse(await readFile(skillJsonPath, "utf8")); + skill.version = version; + await writeFile(skillJsonPath, `${JSON.stringify(skill, null, 2)}\n`); + + const skillMdPath = path.join(fixtureDir, "SKILL.md"); + const skillMd = await readFile(skillMdPath, "utf8"); + await writeFile(skillMdPath, skillMd.replace(/^version:\s*.+$/m, `version: ${version}`)); + + return fixtureDir; +} + +async function runSimulation({ skillDir, outputDir, expectedOriginal, expectedSimulated, expectedAgent }) { + const result = spawnSync( + process.execPath, + [ + "scripts/ci/simulate_skill_tag_release.mjs", + skillDir, + outputDir, + "--repository", + "prompt-security/clawsec", + "--source-ref", + "pull-request-head", + "--skillspector-bin", + fakeSkillspector, + ], + { encoding: "utf8" }, + ); + + assert.equal( + result.status, + 0, + `tag release simulation failed\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`, + ); + + const skillName = path.basename(skillDir); + const expectedTag = `${skillName}-v${expectedSimulated}`; + const summary = JSON.parse(await readFile(path.join(outputDir, "simulation-summary.json"), "utf8")); + assert.equal(summary.skill, skillName); + assert.equal(summary.original_version, expectedOriginal); + assert.equal(summary.simulated_version, expectedSimulated); + assert.equal(summary.tag, expectedTag); + + const releaseAssetsDir = path.join(outputDir, "release-assets"); + const checksums = JSON.parse(await readFile(path.join(releaseAssetsDir, "checksums.json"), "utf8")); + assert.equal(checksums.skill, skillName); + assert.equal(checksums.version, expectedSimulated); + assert.equal(checksums.tag, expectedTag); + assert.equal(checksums.archive.filename, `${expectedTag}.zip`); + + for (const artifact of [ + "skill-card.md", + "permissions.json", + "install.md", + "skillspector-report.md", + "checksums.sig", + "signing-public.pem", + ]) { + assert.ok( + checksums.files[artifact] || artifact.endsWith(".sig") || artifact === "signing-public.pem", + `expected ${artifact} to be represented in the release output`, + ); + const file = await readFile(path.join(releaseAssetsDir, artifact)); + assert.ok(file.length > 0, `${artifact} should not be empty`); + } + + const archive = await readFile(path.join(releaseAssetsDir, `${expectedTag}.zip`)); + assert.ok(archive.length > 0, "release archive should not be empty"); + + const install = await readFile(path.join(releaseAssetsDir, "install.md"), "utf8"); + assert.match( + install, + new RegExp( + `npx skills add prompt-security/clawsec#pull-request-head --skill ${skillName} --agent ${expectedAgent} --global --yes`, + ), + ); + assert.match(install, new RegExp(`npx skills update ${skillName}`)); +} + +try { + await writeFile( + fakeSkillspector, + `#!/usr/bin/env node +import { writeFileSync } from "node:fs"; + +const outputIndex = process.argv.indexOf("--output"); +if (outputIndex === -1 || !process.argv[outputIndex + 1]) { + console.error("missing --output"); + process.exit(2); +} + +writeFileSync(process.argv[outputIndex + 1], "# Fake SkillSpector Report\\n\\nNo live scan executed in unit test.\\n"); +`, + { mode: 0o700 }, + ); + await chmod(fakeSkillspector, 0o700); + + await runSimulation({ + skillDir: "skills/clawsec-suite", + outputDir: path.join(tempRoot, "stable"), + expectedOriginal: "0.1.10", + expectedSimulated: "0.1.11", + expectedAgent: "openclaw", + }); + + await runSimulation({ + skillDir: "skills/hermes-traffic-guardian", + outputDir: path.join(tempRoot, "beta"), + expectedOriginal: "0.0.1-beta3", + expectedSimulated: "0.0.1-beta4", + expectedAgent: "hermes-agent", + }); + + const alphaSkillDir = await prereleaseFixture("skills/picoclaw-self-pen-testing", "0.0.3-alpha1", "alpha-fixture"); + await runSimulation({ + skillDir: alphaSkillDir, + outputDir: path.join(tempRoot, "alpha"), + expectedOriginal: "0.0.3-alpha1", + expectedSimulated: "0.0.3-alpha2", + expectedAgent: "openclaw", + }); + + const rcSkillDir = await prereleaseFixture("skills/picoclaw-security-guardian", "0.0.4-rc1", "rc-fixture"); + await runSimulation({ + skillDir: rcSkillDir, + outputDir: path.join(tempRoot, "rc"), + expectedOriginal: "0.0.4-rc1", + expectedSimulated: "0.0.4-rc2", + expectedAgent: "openclaw", + }); + + const previewSkillDir = await prereleaseFixture("skills/openclaw-traffic-guardian", "0.0.1-preview", "preview-fixture"); + await runSimulation({ + skillDir: previewSkillDir, + outputDir: path.join(tempRoot, "preview"), + expectedOriginal: "0.0.1-preview", + expectedSimulated: "0.0.1-preview1", + expectedAgent: "openclaw", + }); +} finally { + await rm(tempRoot, { recursive: true, force: true }); +} diff --git a/scripts/test-skill-trust-packet.mjs b/scripts/test-skill-trust-packet.mjs new file mode 100644 index 0000000..a7d55e5 --- /dev/null +++ b/scripts/test-skill-trust-packet.mjs @@ -0,0 +1,79 @@ +import assert from "node:assert/strict"; +import { mkdtemp, readFile, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; + +const outputDir = await mkdtemp(path.join(tmpdir(), "clawsec-trust-packet-")); + +function runTrustPacket(skillDir, targetDir, tag) { + return spawnSync( + process.execPath, + [ + "scripts/ci/generate_skill_release_trust_packet.mjs", + skillDir, + targetDir, + "--repository", + "prompt-security/clawsec", + "--tag", + tag, + "--source-ref", + "main", + ], + { encoding: "utf8" }, + ); +} + +try { + const result = runTrustPacket("skills/clawsec-suite", outputDir, "clawsec-suite-v0.1.10"); + + assert.equal( + result.status, + 0, + `trust packet generator failed\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`, + ); + + const skillCard = await readFile(path.join(outputDir, "skill-card.md"), "utf8"); + const permissions = JSON.parse(await readFile(path.join(outputDir, "permissions.json"), "utf8")); + const install = await readFile(path.join(outputDir, "install.md"), "utf8"); + + assert.match(skillCard, /^# Skill Card/m); + assert.match(skillCard, /## License\/Terms of Use/); + assert.match(skillCard, /AGPL-3\.0-or-later/); + assert.match(skillCard, /skillspector-report\.md/); + assert.match(skillCard, /clawsec-suite-v0\.1\.10/); + + assert.equal(permissions.skill, "clawsec-suite"); + assert.equal(permissions.version, "0.1.10"); + assert.equal(permissions.platform, "openclaw"); + assert.deepEqual( + permissions.required_binaries, + ["node", "npx", "openclaw", "curl", "jq", "shasum", "openssl", "unzip"], + ); + assert.match(permissions.network_egress, /signed advisory feed/); + assert.match(permissions.persistence, /OpenClaw advisory hook/); + assert.ok(Array.isArray(permissions.operator_review)); + assert.ok(permissions.operator_review.length > 0); + + assert.match(install, /npx skills add prompt-security\/clawsec --skill clawsec-suite --agent openclaw --global --yes/); + assert.match(install, /npx skills update clawsec-suite/); + + const hermesOutputDir = path.join(outputDir, "hermes"); + const hermesResult = runTrustPacket( + "skills/hermes-attestation-guardian", + hermesOutputDir, + "hermes-attestation-guardian-v0.1.4", + ); + assert.equal( + hermesResult.status, + 0, + `Hermes trust packet generator failed\nstdout:\n${hermesResult.stdout}\nstderr:\n${hermesResult.stderr}`, + ); + const hermesInstall = await readFile(path.join(hermesOutputDir, "install.md"), "utf8"); + assert.match( + hermesInstall, + /npx skills add prompt-security\/clawsec --skill hermes-attestation-guardian --agent hermes-agent --global --yes/, + ); +} finally { + await rm(outputDir, { recursive: true, force: true }); +} diff --git a/skills/claw-release/CHANGELOG.md b/skills/claw-release/CHANGELOG.md index 533e606..c16f195 100644 --- a/skills/claw-release/CHANGELOG.md +++ b/skills/claw-release/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.0.4] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. +- Marked the release helper with top-level internal metadata so compatible installers can hide it from normal agent-facing discovery. + ## [0.0.3] - 2026-05-14 ### Security diff --git a/skills/claw-release/README.md b/skills/claw-release/README.md new file mode 100644 index 0000000..af621ff --- /dev/null +++ b/skills/claw-release/README.md @@ -0,0 +1,11 @@ +# Claw Release + +Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification. + +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill claw-release -a openclaw -y +``` diff --git a/skills/claw-release/SKILL.md b/skills/claw-release/SKILL.md index 6d9862e..404da81 100644 --- a/skills/claw-release/SKILL.md +++ b/skills/claw-release/SKILL.md @@ -1,9 +1,14 @@ --- name: claw-release -version: 0.0.3 +version: 0.0.4 description: Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification. homepage: https://clawsec.prompt.security -metadata: {"openclaw":{"emoji":"🚀","category":"utility","internal":true}} +metadata: + internal: true + openclaw: + emoji: "🚀" + category: "utility" + internal: true clawdis: emoji: "🚀" requires: @@ -18,6 +23,14 @@ Internal tool for releasing skills and managing the ClawSec catalog. --- +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill claw-release -a openclaw -y +``` + ## Operational Notes - Internal maintainer workflow only. @@ -26,7 +39,6 @@ Internal tool for releasing skills and managing the ClawSec catalog. - Side effects: creates commits, tags, pushes to remote, and publishes GitHub Releases - Trust model: run only from a trusted checkout with a clean working tree and maintainer approval - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. @@ -35,7 +47,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="claw-release" -VERSION="0.0.3" +VERSION="0.0.4" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/claw-release/skill.json b/skills/claw-release/skill.json index 3b7a574..641572d 100644 --- a/skills/claw-release/skill.json +++ b/skills/claw-release/skill.json @@ -1,6 +1,6 @@ { "name": "claw-release", - "version": "0.0.3", + "version": "0.0.4", "description": "Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/clawsec-clawhub-checker/CHANGELOG.md b/skills/clawsec-clawhub-checker/CHANGELOG.md index e4a2859..c20dfba 100644 --- a/skills/clawsec-clawhub-checker/CHANGELOG.md +++ b/skills/clawsec-clawhub-checker/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.6] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.5] - 2026-06-07 ### Security diff --git a/skills/clawsec-clawhub-checker/README.md b/skills/clawsec-clawhub-checker/README.md index 987e1d4..c1c7279 100644 --- a/skills/clawsec-clawhub-checker/README.md +++ b/skills/clawsec-clawhub-checker/README.md @@ -2,6 +2,14 @@ A `clawsec-suite` companion skill that adds a standalone reputation gate before guarded installs. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-clawhub-checker -a openclaw -y +``` + ## Operational Notes - Required runtime: `node`, `clawhub`, `openclaw` diff --git a/skills/clawsec-clawhub-checker/SKILL.md b/skills/clawsec-clawhub-checker/SKILL.md index fb9e7cb..678c611 100644 --- a/skills/clawsec-clawhub-checker/SKILL.md +++ b/skills/clawsec-clawhub-checker/SKILL.md @@ -1,6 +1,6 @@ --- name: clawsec-clawhub-checker -version: 0.0.5 +version: 0.0.6 description: ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation. homepage: https://clawsec.prompt.security clawdis: @@ -14,6 +14,14 @@ clawdis: Adds a reputation gate on top of the `clawsec-suite` guarded installer. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-clawhub-checker -a openclaw -y +``` + ## Operational Notes - Required runtime: `node`, `clawhub`, `openclaw` @@ -45,7 +53,6 @@ Optional preflight check (validates local paths and prints recommended command): node ~/.openclaw/skills/clawsec-clawhub-checker/scripts/setup_reputation_hook.mjs ``` - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. diff --git a/skills/clawsec-clawhub-checker/skill.json b/skills/clawsec-clawhub-checker/skill.json index 7686890..82bd34d 100644 --- a/skills/clawsec-clawhub-checker/skill.json +++ b/skills/clawsec-clawhub-checker/skill.json @@ -1,6 +1,6 @@ { "name": "clawsec-clawhub-checker", - "version": "0.0.5", + "version": "0.0.6", "description": "ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.", "author": "abutbul", "license": "AGPL-3.0-or-later", diff --git a/skills/clawsec-feed/CHANGELOG.md b/skills/clawsec-feed/CHANGELOG.md index 7caead3..90a014a 100644 --- a/skills/clawsec-feed/CHANGELOG.md +++ b/skills/clawsec-feed/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.9] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.8] - 2026-05-24 ### Changed diff --git a/skills/clawsec-feed/README.md b/skills/clawsec-feed/README.md index 893c019..8cc995f 100644 --- a/skills/clawsec-feed/README.md +++ b/skills/clawsec-feed/README.md @@ -2,6 +2,14 @@ Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence and stay informed about emerging threats. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-feed -a openclaw -y +``` + ## Operational Notes - Required runtime for standalone installation: `bash`, `curl`, `jq`, `shasum`, `unzip` diff --git a/skills/clawsec-feed/SKILL.md b/skills/clawsec-feed/SKILL.md index a474dca..e35e9ab 100644 --- a/skills/clawsec-feed/SKILL.md +++ b/skills/clawsec-feed/SKILL.md @@ -1,6 +1,6 @@ --- name: clawsec-feed -version: 0.0.8 +version: 0.0.9 description: Security advisory feed package for OpenClaw-related threats and vulnerabilities. The upstream feed is updated daily; local automation is handled by clawsec-suite or the operator. homepage: https://clawsec.prompt.security metadata: {"openclaw":{"emoji":"📡","category":"security"}} @@ -16,6 +16,14 @@ Security advisory feed monitoring for AI agents. Subscribe to community-driven t The default `feed.json` is the consolidated agent feed. It includes NVD CVEs, approved community advisories, and provisional GitHub Security Advisories that do not have CVE IDs yet. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-feed -a openclaw -y +``` + ## Operational Notes - Required runtime for standalone installation: `bash`, `curl`, `jq`, `shasum`, `unzip` @@ -66,7 +74,6 @@ Standalone installation is a network download workflow. Verify the release sourc Installation Steps: Agents should review scripts before execution! - ```bash # Get latest clawsec-feed release tag LATEST_TAG=$(curl -sSL https://api.github.com/repos/prompt-security/ClawSec/releases | \ @@ -81,7 +88,6 @@ Once you have this skill file, proceed to **[Deploy ClawSec Feed](#deploy-clawse --- - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. @@ -90,7 +96,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="clawsec-feed" -VERSION="0.0.8" +VERSION="0.0.9" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/clawsec-feed/skill.json b/skills/clawsec-feed/skill.json index 4847bc6..35f8ede 100644 --- a/skills/clawsec-feed/skill.json +++ b/skills/clawsec-feed/skill.json @@ -1,6 +1,6 @@ { "name": "clawsec-feed", - "version": "0.0.8", + "version": "0.0.9", "description": "Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/clawsec-nanoclaw/CHANGELOG.md b/skills/clawsec-nanoclaw/CHANGELOG.md index 2c8951f..3a8db93 100644 --- a/skills/clawsec-nanoclaw/CHANGELOG.md +++ b/skills/clawsec-nanoclaw/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.8] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.7] - 2026-06-07 ### Security diff --git a/skills/clawsec-nanoclaw/README.md b/skills/clawsec-nanoclaw/README.md index 9ca28a9..4c9401c 100644 --- a/skills/clawsec-nanoclaw/README.md +++ b/skills/clawsec-nanoclaw/README.md @@ -2,6 +2,14 @@ ClawSec now supports NanoClaw, a containerized WhatsApp bot powered by Claude agents. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-nanoclaw -a openclaw -y +``` + ## What Changed ### Advisory Feed Monitoring diff --git a/skills/clawsec-nanoclaw/SKILL.md b/skills/clawsec-nanoclaw/SKILL.md index bb23fbf..d99ad36 100644 --- a/skills/clawsec-nanoclaw/SKILL.md +++ b/skills/clawsec-nanoclaw/SKILL.md @@ -1,6 +1,6 @@ --- name: clawsec-nanoclaw -version: 0.0.7 +version: 0.0.8 description: Use when checking for security vulnerabilities in NanoClaw skills, before installing new skills, or when asked about security advisories affecting the bot --- @@ -8,6 +8,14 @@ description: Use when checking for security vulnerabilities in NanoClaw skills, Security advisory monitoring that protects your WhatsApp bot from known vulnerabilities in skills and dependencies. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-nanoclaw -a openclaw -y +``` + ## Overview ClawSec provides MCP tools that check installed skills against a curated feed of security advisories. It prevents installation of vulnerable skills, includes exploitability context for triage, and alerts you to issues in existing ones. @@ -201,7 +209,6 @@ See [INSTALL.md](./INSTALL.md) for setup and [docs/](./docs/) for advanced usage - Provides actionable remediation steps - Zero false positives (curated feed only) - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. diff --git a/skills/clawsec-nanoclaw/skill.json b/skills/clawsec-nanoclaw/skill.json index cbb0f71..410af20 100644 --- a/skills/clawsec-nanoclaw/skill.json +++ b/skills/clawsec-nanoclaw/skill.json @@ -1,6 +1,6 @@ { "name": "clawsec-nanoclaw", - "version": "0.0.7", + "version": "0.0.8", "description": "ClawSec security suite for NanoClaw - Advisory feed monitoring, MCP tools for vulnerability checking, and Ed25519 signature verification for containerized WhatsApp bot agents", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/clawsec-scanner/CHANGELOG.md b/skills/clawsec-scanner/CHANGELOG.md index 747f861..9f1c16d 100644 --- a/skills/clawsec-scanner/CHANGELOG.md +++ b/skills/clawsec-scanner/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.5] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.4] - 2026-06-07 ### Security diff --git a/skills/clawsec-scanner/README.md b/skills/clawsec-scanner/README.md new file mode 100644 index 0000000..5cf48d3 --- /dev/null +++ b/skills/clawsec-scanner/README.md @@ -0,0 +1,11 @@ +# Clawsec Scanner + +Automated vulnerability scanner for agent platforms. Performs dependency scanning (npm audit, pip-audit), multi-database CVE lookup (OSV, NVD, GitHub Advisory), SAST analysis (Semgrep, Bandit), and agent-specific static hook inspection for OpenClaw hooks. + +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-scanner -a openclaw -y +``` diff --git a/skills/clawsec-scanner/SKILL.md b/skills/clawsec-scanner/SKILL.md index 03227ea..91ec4e0 100644 --- a/skills/clawsec-scanner/SKILL.md +++ b/skills/clawsec-scanner/SKILL.md @@ -1,6 +1,6 @@ --- name: clawsec-scanner -version: 0.0.4 +version: 0.0.5 description: Automated vulnerability scanner for agent platforms. Performs dependency scanning (npm audit, pip-audit), multi-database CVE lookup (OSV, NVD, GitHub Advisory), SAST analysis (Semgrep, Bandit), and agent-specific static hook inspection for OpenClaw hooks. homepage: https://clawsec.prompt.security clawdis: @@ -20,6 +20,14 @@ Comprehensive security scanner for agent platforms that automates vulnerability - **Unified Reporting**: Consolidated vulnerability reports with severity classification and remediation guidance - **Continuous Monitoring**: OpenClaw hook integration for automated periodic scanning +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-scanner -a openclaw -y +``` + ## Features ### Multi-Engine Scanning diff --git a/skills/clawsec-scanner/skill.json b/skills/clawsec-scanner/skill.json index 0fbaa92..ea539c5 100644 --- a/skills/clawsec-scanner/skill.json +++ b/skills/clawsec-scanner/skill.json @@ -1,6 +1,6 @@ { "name": "clawsec-scanner", - "version": "0.0.4", + "version": "0.0.5", "description": "Automated vulnerability scanner for agent platforms. Performs dependency scanning (npm audit, pip-audit), multi-database CVE lookup (OSV, NVD, GitHub Advisory), SAST analysis (Semgrep, Bandit), and agent-specific static hook inspection for OpenClaw hooks.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/clawsec-suite/CHANGELOG.md b/skills/clawsec-suite/CHANGELOG.md index 6d52ef7..937e968 100644 --- a/skills/clawsec-suite/CHANGELOG.md +++ b/skills/clawsec-suite/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.1.10] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + All notable changes to the ClawSec Suite will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), diff --git a/skills/clawsec-suite/README.md b/skills/clawsec-suite/README.md new file mode 100644 index 0000000..1473517 --- /dev/null +++ b/skills/clawsec-suite/README.md @@ -0,0 +1,11 @@ +# Clawsec Suite + +ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills. + +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y +``` diff --git a/skills/clawsec-suite/SKILL.md b/skills/clawsec-suite/SKILL.md index a7358ca..2df88bf 100644 --- a/skills/clawsec-suite/SKILL.md +++ b/skills/clawsec-suite/SKILL.md @@ -1,6 +1,6 @@ --- name: clawsec-suite -version: 0.1.9 +version: 0.1.10 description: ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills. homepage: https://clawsec.prompt.security clawdis: @@ -11,6 +11,14 @@ clawdis: # ClawSec Suite +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y +``` + ## Operational Notes - Required runtime: `node`, `npx`, `openclaw`, `curl`, `jq`, `shasum`, `openssl`, `unzip` diff --git a/skills/clawsec-suite/skill.json b/skills/clawsec-suite/skill.json index 8a8e5a6..da46a9e 100644 --- a/skills/clawsec-suite/skill.json +++ b/skills/clawsec-suite/skill.json @@ -1,6 +1,6 @@ { "name": "clawsec-suite", - "version": "0.1.9", + "version": "0.1.10", "description": "ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/clawtributor/CHANGELOG.md b/skills/clawtributor/CHANGELOG.md index cfec233..eea04ce 100644 --- a/skills/clawtributor/CHANGELOG.md +++ b/skills/clawtributor/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.0.7] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. +- Marked Clawtributor as a harness-neutral global skill for OpenClaw, NanoClaw, Hermes, and Picoclaw installer grouping. +- Removed OpenClaw CLI as a declared runtime requirement because reporting is manual, approval-gated, and not tied to an OpenClaw command path. +- Documented Vercel skills installer usage alongside the OpenClaw/ClawHub install path. +- Moved local report/state guidance to `~/.clawsec/clawtributor/`. + ## [0.0.6] - 2026-05-14 ### Security diff --git a/skills/clawtributor/README.md b/skills/clawtributor/README.md index 7553ddc..0b8c395 100644 --- a/skills/clawtributor/README.md +++ b/skills/clawtributor/README.md @@ -2,6 +2,20 @@ Community incident reporting for AI agents. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawtributor -a openclaw -y +``` + +Codex install is also supported: + +```bash +npx skills add prompt-security/clawsec --skill clawtributor -a codex -y +``` + ## Operational Notes - Reporting is opt-in for every submission @@ -17,6 +31,14 @@ Community incident reporting for AI agents. ## Quick Install +Vercel skills installer: + +```bash +npx skills add prompt-security/clawsec --skill clawtributor -a codex -y +``` + +OpenClaw/ClawHub: + ```bash npx clawhub@latest install clawtributor ``` diff --git a/skills/clawtributor/SKILL.md b/skills/clawtributor/SKILL.md index 9e74302..eb1703d 100644 --- a/skills/clawtributor/SKILL.md +++ b/skills/clawtributor/SKILL.md @@ -1,23 +1,44 @@ --- name: clawtributor -version: 0.0.6 -description: Community incident reporting for AI agents. Contribute to collective security by reporting threats. +version: 0.0.7 +description: Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats. homepage: https://clawsec.prompt.security -metadata: {"openclaw":{"emoji":"🤝","category":"security"}} +platforms: + - openclaw + - nanoclaw + - hermes + - picoclaw +metadata: + global: true + openclaw: + emoji: "🤝" + category: "security" clawdis: emoji: "🤝" - requires: - bins: [openclaw] --- # Clawtributor 🤝 Community incident reporting for AI agents. Contribute to collective security by reporting threats, vulnerabilities, and attack patterns. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill clawtributor -a openclaw -y +``` + +Codex install is also supported: + +```bash +npx skills add prompt-security/clawsec --skill clawtributor -a codex -y +``` + ## Operational Notes -- Recommended install path: ClawHub registry (`npx clawhub@latest install clawtributor`) -- Side effects: creates local report/state files under `~/.openclaw/` +- Recommended install path: harness-native skills installer; use ClawHub for OpenClaw/ClawHub environments (`npx clawhub@latest install clawtributor`) +- Side effects: creates local report/state files under `~/.clawsec/clawtributor/` - Network behavior: none unless the user explicitly approves manual submission - Trust model: reporting is opt-in for every submission; sanitize evidence before it leaves the host @@ -27,7 +48,13 @@ Community incident reporting for AI agents. Contribute to collective security by ## Installation -Install from the registry: +Install with your harness-native skills installer. For the Vercel skills installer: + +```bash +npx skills add prompt-security/clawsec --skill clawtributor -a codex -y +``` + +For OpenClaw/ClawHub environments, install from the registry: ```bash npx clawhub@latest install clawtributor @@ -44,7 +71,6 @@ I will keep reports local unless you explicitly approve submission. --- - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. @@ -53,7 +79,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="clawtributor" -VERSION="0.0.6" +VERSION="0.0.7" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" @@ -233,7 +259,7 @@ See [reporting.md](./reporting.md) for the full report format and submission gui ### Step 1: Prepare report locally -- Save the report JSON under `~/.openclaw/clawtributor-reports/` +- Save the report JSON under `~/.clawsec/clawtributor/reports/` - Keep file permissions private (`chmod 600`) - Confirm the report is sanitized before sharing @@ -284,7 +310,7 @@ DO NOT include: ## State Tracking -Track submitted reports in `~/.openclaw/clawtributor-state.json`. +Track submitted reports in `~/.clawsec/clawtributor/state.json`. Example: diff --git a/skills/clawtributor/skill.json b/skills/clawtributor/skill.json index 32b59c0..ead690f 100644 --- a/skills/clawtributor/skill.json +++ b/skills/clawtributor/skill.json @@ -1,16 +1,24 @@ { "name": "clawtributor", - "version": "0.0.6", - "description": "Community incident reporting for AI agents. Contribute to collective security by reporting threats.", + "version": "0.0.7", + "description": "Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.", "author": "prompt-security", "license": "AGPL-3.0-or-later", "homepage": "https://clawsec.prompt.security", + "platforms": [ + "openclaw", + "nanoclaw", + "hermes", + "picoclaw" + ], "keywords": [ "security", "reporting", "community", "agents", "ai", + "global", + "harness-neutral", "vulnerability", "contribution" ], @@ -36,11 +44,6 @@ "openclaw": { "emoji": "🤝", "category": "security", - "requires": { - "bins": [ - "openclaw" - ] - }, "execution": { "always": false, "persistence": "Stores local report/state files only; no recurring automation is created by default.", diff --git a/skills/hermes-attestation-guardian/CHANGELOG.md b/skills/hermes-attestation-guardian/CHANGELOG.md index cc01418..9601ae5 100644 --- a/skills/hermes-attestation-guardian/CHANGELOG.md +++ b/skills/hermes-attestation-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.1.4] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.1.3] - 2026-05-24 ### Changed diff --git a/skills/hermes-attestation-guardian/README.md b/skills/hermes-attestation-guardian/README.md index 9c69974..f9e44d7 100644 --- a/skills/hermes-attestation-guardian/README.md +++ b/skills/hermes-attestation-guardian/README.md @@ -4,6 +4,14 @@ Hermes-only attestation, advisory verification, and guarded verification workflo Status: implemented (v0.1.0), Hermes-only. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill hermes-attestation-guardian -a hermes-agent -y +``` + ## Capabilities This skill now covers the full Hermes-side capability set expected from the clawsec-suite parity workstream: diff --git a/skills/hermes-attestation-guardian/SKILL.md b/skills/hermes-attestation-guardian/SKILL.md index e216353..a512e73 100644 --- a/skills/hermes-attestation-guardian/SKILL.md +++ b/skills/hermes-attestation-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: hermes-attestation-guardian -version: 0.1.3 +version: 0.1.4 description: Hermes-only runtime security attestation and drift detection skill for operator-managed Hermes infrastructure. homepage: https://clawsec.prompt.security hermes: @@ -15,6 +15,13 @@ IMPORTANT SCOPE: - This skill targets Hermes infrastructure only (CLI/Gateway/profile-managed deployments). - This skill is not an OpenClaw runtime hook package. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill hermes-attestation-guardian -a hermes-agent -y +``` ## Release Artifact Verification @@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="hermes-attestation-guardian" -VERSION="0.1.3" +VERSION="0.1.4" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/hermes-attestation-guardian/skill.json b/skills/hermes-attestation-guardian/skill.json index cd72ed8..b2ba846 100644 --- a/skills/hermes-attestation-guardian/skill.json +++ b/skills/hermes-attestation-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "hermes-attestation-guardian", - "version": "0.1.3", + "version": "0.1.4", "description": "Hermes-only runtime security attestation and drift detection skill. Generates deterministic posture artifacts, verifies integrity fail-closed, and classifies baseline drift severity.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/hermes-traffic-guardian/CHANGELOG.md b/skills/hermes-traffic-guardian/CHANGELOG.md index 776829a..95e8cf5 100644 --- a/skills/hermes-traffic-guardian/CHANGELOG.md +++ b/skills/hermes-traffic-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.1-beta3] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.1-beta2] - 2026-05-13 ### Security diff --git a/skills/hermes-traffic-guardian/README.md b/skills/hermes-traffic-guardian/README.md index 6d39ad0..4fdff96 100644 --- a/skills/hermes-traffic-guardian/README.md +++ b/skills/hermes-traffic-guardian/README.md @@ -4,6 +4,14 @@ Baseline skill for Hermes runtime traffic monitoring. This package is intentionally a spec scaffold. Builders should add the Hermes-specific monitor implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill hermes-traffic-guardian -a hermes-agent -y +``` + ## Intended Capability - detect outbound secret exfiltration in Hermes HTTP/HTTPS traffic @@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the Hermes-sp ## Builder Notes Keep runtime ownership in this skill. `hermes-attestation-guardian` should only attest this skill's state, config, and output fingerprints. - diff --git a/skills/hermes-traffic-guardian/SKILL.md b/skills/hermes-traffic-guardian/SKILL.md index a7ae340..28bea43 100644 --- a/skills/hermes-traffic-guardian/SKILL.md +++ b/skills/hermes-traffic-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: hermes-traffic-guardian -version: 0.0.1-beta2 +version: 0.0.1-beta3 description: Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture. homepage: https://clawsec.prompt.security author: prompt-security @@ -15,6 +15,13 @@ hermes: This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill hermes-traffic-guardian -a hermes-agent -y +``` ## Release Artifact Verification @@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="hermes-traffic-guardian" -VERSION="0.0.1-beta2" +VERSION="0.0.1-beta3" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" @@ -145,4 +152,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows: - default blocking - sending traffic to external services - collecting full request/response bodies - diff --git a/skills/hermes-traffic-guardian/skill.json b/skills/hermes-traffic-guardian/skill.json index 3a6308b..adb391b 100644 --- a/skills/hermes-traffic-guardian/skill.json +++ b/skills/hermes-traffic-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "hermes-traffic-guardian", - "version": "0.0.1-beta2", + "version": "0.0.1-beta3", "description": "Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/nanoclaw-traffic-guardian/CHANGELOG.md b/skills/nanoclaw-traffic-guardian/CHANGELOG.md index 3987cc5..c747c2e 100644 --- a/skills/nanoclaw-traffic-guardian/CHANGELOG.md +++ b/skills/nanoclaw-traffic-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.1-beta3] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.1-beta2] - 2026-05-13 ### Security diff --git a/skills/nanoclaw-traffic-guardian/README.md b/skills/nanoclaw-traffic-guardian/README.md index 258e8d4..6f9235e 100644 --- a/skills/nanoclaw-traffic-guardian/README.md +++ b/skills/nanoclaw-traffic-guardian/README.md @@ -4,6 +4,14 @@ Baseline skill for NanoClaw runtime traffic monitoring. This package is intentionally a spec scaffold. Builders should add the NanoClaw-specific host-service, IPC, and MCP implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill nanoclaw-traffic-guardian -a openclaw -y +``` + ## Intended Capability - detect outbound secret exfiltration in NanoClaw host-managed traffic @@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the NanoClaw- ## Builder Notes Follow the existing `clawsec-nanoclaw` pattern: host services own privileged operations, while MCP tools expose bounded requests and redacted responses. - diff --git a/skills/nanoclaw-traffic-guardian/SKILL.md b/skills/nanoclaw-traffic-guardian/SKILL.md index 777f49b..e503c59 100644 --- a/skills/nanoclaw-traffic-guardian/SKILL.md +++ b/skills/nanoclaw-traffic-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: nanoclaw-traffic-guardian -version: 0.0.1-beta2 +version: 0.0.1-beta3 description: NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces. homepage: https://clawsec.prompt.security author: prompt-security @@ -14,6 +14,13 @@ nanoclaw: This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill nanoclaw-traffic-guardian -a openclaw -y +``` ## Release Artifact Verification @@ -23,7 +30,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="nanoclaw-traffic-guardian" -VERSION="0.0.1-beta2" +VERSION="0.0.1-beta3" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" @@ -146,4 +153,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows: - default blocking - sending traffic to external services - exposing raw request/response bodies to the container - diff --git a/skills/nanoclaw-traffic-guardian/skill.json b/skills/nanoclaw-traffic-guardian/skill.json index f513ab5..57f812d 100644 --- a/skills/nanoclaw-traffic-guardian/skill.json +++ b/skills/nanoclaw-traffic-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "nanoclaw-traffic-guardian", - "version": "0.0.1-beta2", + "version": "0.0.1-beta3", "description": "NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/openclaw-audit-watchdog/CHANGELOG.md b/skills/openclaw-audit-watchdog/CHANGELOG.md index 7bea9c7..1796ec2 100644 --- a/skills/openclaw-audit-watchdog/CHANGELOG.md +++ b/skills/openclaw-audit-watchdog/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.1.7] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.1.6] - 2026-05-16 ### Fixed diff --git a/skills/openclaw-audit-watchdog/README.md b/skills/openclaw-audit-watchdog/README.md index dcf0dd0..de8e167 100644 --- a/skills/openclaw-audit-watchdog/README.md +++ b/skills/openclaw-audit-watchdog/README.md @@ -2,6 +2,14 @@ Automated daily security audits for OpenClaw agents with DM delivery and optional email reporting. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill openclaw-audit-watchdog -a openclaw -y +``` + ## Overview The Audit Watchdog provides automated security monitoring for your OpenClaw agent deployments: diff --git a/skills/openclaw-audit-watchdog/SKILL.md b/skills/openclaw-audit-watchdog/SKILL.md index b71f747..24745a2 100644 --- a/skills/openclaw-audit-watchdog/SKILL.md +++ b/skills/openclaw-audit-watchdog/SKILL.md @@ -1,6 +1,6 @@ --- name: openclaw-audit-watchdog -version: 0.1.6 +version: 0.1.7 description: Automated daily security audits for OpenClaw agents with DM delivery and optional email reporting. Runs deep audits, creates or updates a recurring cron job, and sends formatted reports to configured recipients. homepage: https://clawsec.prompt.security metadata: @@ -29,6 +29,14 @@ clawdis: # Prompt Security Audit (openclaw) +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill openclaw-audit-watchdog -a openclaw -y +``` + ## Installation Options You can get openclaw-audit-watchdog in two ways: @@ -65,7 +73,6 @@ Continue below for standalone installation instructions. --- - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. @@ -74,7 +81,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="openclaw-audit-watchdog" -VERSION="0.1.6" +VERSION="0.1.7" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/openclaw-audit-watchdog/skill.json b/skills/openclaw-audit-watchdog/skill.json index 3b01521..342cfe0 100644 --- a/skills/openclaw-audit-watchdog/skill.json +++ b/skills/openclaw-audit-watchdog/skill.json @@ -1,6 +1,6 @@ { "name": "openclaw-audit-watchdog", - "version": "0.1.6", + "version": "0.1.7", "description": "Automated daily security audits for OpenClaw agents with DM delivery and optional email reporting. Creates or updates an unattended cron job and sends formatted reports to configured recipients.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/openclaw-traffic-guardian/CHANGELOG.md b/skills/openclaw-traffic-guardian/CHANGELOG.md index fcf693d..0206a47 100644 --- a/skills/openclaw-traffic-guardian/CHANGELOG.md +++ b/skills/openclaw-traffic-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.1-beta3] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.1-beta2] - 2026-05-13 ### Security diff --git a/skills/openclaw-traffic-guardian/README.md b/skills/openclaw-traffic-guardian/README.md index 30dec3c..17fba1f 100644 --- a/skills/openclaw-traffic-guardian/README.md +++ b/skills/openclaw-traffic-guardian/README.md @@ -4,6 +4,14 @@ Baseline skill for OpenClaw runtime traffic monitoring. This package is intentionally a spec scaffold. Builders should add the OpenClaw-specific monitor implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill openclaw-traffic-guardian -a openclaw -y +``` + ## Intended Capability - detect outbound secret exfiltration in agent HTTP/HTTPS traffic @@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the OpenClaw- ## Builder Notes Use `SPEC.md` as the implementation contract. Keep runtime changes opt-in and scoped to the OpenClaw process being monitored. - diff --git a/skills/openclaw-traffic-guardian/SKILL.md b/skills/openclaw-traffic-guardian/SKILL.md index 5601c8f..288316b 100644 --- a/skills/openclaw-traffic-guardian/SKILL.md +++ b/skills/openclaw-traffic-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: openclaw-traffic-guardian -version: 0.0.1-beta2 +version: 0.0.1-beta3 description: OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, and inbound injection detection. homepage: https://clawsec.prompt.security author: prompt-security @@ -15,6 +15,13 @@ clawdis: This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill openclaw-traffic-guardian -a openclaw -y +``` ## Release Artifact Verification @@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="openclaw-traffic-guardian" -VERSION="0.0.1-beta2" +VERSION="0.0.1-beta3" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" @@ -146,4 +153,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows: - default blocking - sending traffic to external services - collecting full request/response bodies - diff --git a/skills/openclaw-traffic-guardian/skill.json b/skills/openclaw-traffic-guardian/skill.json index 767164a..3f0845e 100644 --- a/skills/openclaw-traffic-guardian/skill.json +++ b/skills/openclaw-traffic-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "openclaw-traffic-guardian", - "version": "0.0.1-beta2", + "version": "0.0.1-beta3", "description": "OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, and inbound injection detection.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/picoclaw-security-guardian/CHANGELOG.md b/skills/picoclaw-security-guardian/CHANGELOG.md index 4ab12fe..0e0c7b2 100644 --- a/skills/picoclaw-security-guardian/CHANGELOG.md +++ b/skills/picoclaw-security-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.4] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.3] - 2026-05-24 ### Changed diff --git a/skills/picoclaw-security-guardian/README.md b/skills/picoclaw-security-guardian/README.md index 669f92f..45644a0 100644 --- a/skills/picoclaw-security-guardian/README.md +++ b/skills/picoclaw-security-guardian/README.md @@ -6,6 +6,14 @@ Status: implemented (v0.0.1), Picoclaw-specific. Detailed architecture/operator docs: `wiki/modules/picoclaw-security-guardian.md`. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill picoclaw-security-guardian -a openclaw -y +``` + ## Support matrix mapping | Skill name | supported platform | security feed | config drift | agent posture-review lane | chain of supply verification | @@ -48,4 +56,3 @@ test/picoclaw_security_guardian_sandbox_regression.sh ``` It uses Docker to publish the skill through a local ClawHub-compatible registry, installs it with Picoclaw's own `find_skills` / `install_skill` flow into an isolated Picoclaw workspace, confirms Picoclaw's skill loader can list/load it, then verifies the installed copy's profile, drift, advisory, and supply-chain paths. - diff --git a/skills/picoclaw-security-guardian/SKILL.md b/skills/picoclaw-security-guardian/SKILL.md index 9f2d4bf..05c3aa4 100644 --- a/skills/picoclaw-security-guardian/SKILL.md +++ b/skills/picoclaw-security-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: picoclaw-security-guardian -version: 0.0.3 +version: 0.0.4 description: Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance. homepage: https://clawsec.prompt.security author: prompt-security @@ -18,6 +18,13 @@ picoclaw: Detailed architecture/operator docs: `wiki/modules/picoclaw-security-guardian.md`. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill picoclaw-security-guardian -a openclaw -y +``` ## Release Artifact Verification @@ -27,7 +34,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="picoclaw-security-guardian" -VERSION="0.0.3" +VERSION="0.0.4" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/picoclaw-security-guardian/skill.json b/skills/picoclaw-security-guardian/skill.json index 0564e0e..ceb0037 100644 --- a/skills/picoclaw-security-guardian/skill.json +++ b/skills/picoclaw-security-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "picoclaw-security-guardian", - "version": "0.0.3", + "version": "0.0.4", "description": "Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/picoclaw-self-pen-testing/CHANGELOG.md b/skills/picoclaw-self-pen-testing/CHANGELOG.md index 99fae0b..6ac5783 100644 --- a/skills/picoclaw-self-pen-testing/CHANGELOG.md +++ b/skills/picoclaw-self-pen-testing/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.3] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.2] - 2026-05-13 ### Security diff --git a/skills/picoclaw-self-pen-testing/README.md b/skills/picoclaw-self-pen-testing/README.md index d3282f4..4c4846b 100644 --- a/skills/picoclaw-self-pen-testing/README.md +++ b/skills/picoclaw-self-pen-testing/README.md @@ -4,6 +4,14 @@ Picoclaw-only local posture-review findings package for ClawSec. Status: implemented (v0.0.1), Picoclaw-specific. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill picoclaw-self-pen-testing -a openclaw -y +``` + ## What it does Given a generated Picoclaw posture profile, it emits severity-ranked findings and a summary count for local operator review. diff --git a/skills/picoclaw-self-pen-testing/SKILL.md b/skills/picoclaw-self-pen-testing/SKILL.md index abed8ad..3958f72 100644 --- a/skills/picoclaw-self-pen-testing/SKILL.md +++ b/skills/picoclaw-self-pen-testing/SKILL.md @@ -1,6 +1,6 @@ --- name: picoclaw-self-pen-testing -version: 0.0.2 +version: 0.0.3 description: Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance. homepage: https://clawsec.prompt.security author: prompt-security @@ -18,6 +18,13 @@ picoclaw: Purpose: keep Picoclaw posture-review checks isolated from the broader guardian package so moderation-sensitive checks can be versioned/published independently. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill picoclaw-self-pen-testing -a openclaw -y +``` ## Release Artifact Verification @@ -27,7 +34,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="picoclaw-self-pen-testing" -VERSION="0.0.2" +VERSION="0.0.3" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/picoclaw-self-pen-testing/skill.json b/skills/picoclaw-self-pen-testing/skill.json index 00762c8..1aaf79d 100644 --- a/skills/picoclaw-self-pen-testing/skill.json +++ b/skills/picoclaw-self-pen-testing/skill.json @@ -1,6 +1,6 @@ { "name": "picoclaw-self-pen-testing", - "version": "0.0.2", + "version": "0.0.3", "description": "Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/picoclaw-traffic-guardian/CHANGELOG.md b/skills/picoclaw-traffic-guardian/CHANGELOG.md index 409ec03..30314ed 100644 --- a/skills/picoclaw-traffic-guardian/CHANGELOG.md +++ b/skills/picoclaw-traffic-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.1-beta3] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.1-beta2] - 2026-05-13 ### Security diff --git a/skills/picoclaw-traffic-guardian/README.md b/skills/picoclaw-traffic-guardian/README.md index 697d7c7..7926f10 100644 --- a/skills/picoclaw-traffic-guardian/README.md +++ b/skills/picoclaw-traffic-guardian/README.md @@ -4,6 +4,14 @@ Baseline skill for Picoclaw runtime traffic monitoring. This package is intentionally a spec scaffold. Builders should add the Picoclaw-specific monitor implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill picoclaw-traffic-guardian -a openclaw -y +``` + ## Intended Capability - detect outbound secret exfiltration in Picoclaw gateway HTTP/HTTPS traffic @@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the Picoclaw- ## Builder Notes Keep runtime ownership in this skill. `picoclaw-security-guardian` should only profile and drift-check this skill's state, config, and output fingerprints. - diff --git a/skills/picoclaw-traffic-guardian/SKILL.md b/skills/picoclaw-traffic-guardian/SKILL.md index ff467b1..8a331a3 100644 --- a/skills/picoclaw-traffic-guardian/SKILL.md +++ b/skills/picoclaw-traffic-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: picoclaw-traffic-guardian -version: 0.0.1-beta2 +version: 0.0.1-beta3 description: Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration. homepage: https://clawsec.prompt.security author: prompt-security @@ -15,6 +15,13 @@ picoclaw: This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill picoclaw-traffic-guardian -a openclaw -y +``` ## Release Artifact Verification @@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="picoclaw-traffic-guardian" -VERSION="0.0.1-beta2" +VERSION="0.0.1-beta3" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" @@ -145,4 +152,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows: - default blocking - sending traffic to external services - collecting full request/response bodies - diff --git a/skills/picoclaw-traffic-guardian/skill.json b/skills/picoclaw-traffic-guardian/skill.json index 2460729..ea9928b 100644 --- a/skills/picoclaw-traffic-guardian/skill.json +++ b/skills/picoclaw-traffic-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "picoclaw-traffic-guardian", - "version": "0.0.1-beta2", + "version": "0.0.1-beta3", "description": "Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration.", "author": "prompt-security", "license": "AGPL-3.0-or-later", diff --git a/skills/soul-guardian/CHANGELOG.md b/skills/soul-guardian/CHANGELOG.md index 18426ca..6737a80 100644 --- a/skills/soul-guardian/CHANGELOG.md +++ b/skills/soul-guardian/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.0.7] - 2026-06-10 + +### Changed + +- Re-released skill package with updated marketplace grouping and signed release trust artifacts for Vercel-compatible skill installation. + ## [0.0.6] - 2026-05-14 ### Security diff --git a/skills/soul-guardian/README.md b/skills/soul-guardian/README.md index 0d8cd9f..12f87f7 100644 --- a/skills/soul-guardian/README.md +++ b/skills/soul-guardian/README.md @@ -2,6 +2,14 @@ A small, dependency-free integrity guard for OpenClaw agent workspaces. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill soul-guardian -a openclaw -y +``` + ## Operational Notes - Required runtime: `python3` diff --git a/skills/soul-guardian/SKILL.md b/skills/soul-guardian/SKILL.md index 5478a63..508c78c 100644 --- a/skills/soul-guardian/SKILL.md +++ b/skills/soul-guardian/SKILL.md @@ -1,6 +1,6 @@ --- name: soul-guardian -version: 0.0.6 +version: 0.0.7 description: Drift detection + baseline integrity guard for agent workspace files with automatic alerting support homepage: https://clawsec.prompt.security metadata: {"openclaw":{"emoji":"👻","category":"security"}} @@ -14,6 +14,14 @@ clawdis: Protects your agent's core files (SOUL.md, AGENTS.md, etc.) from unauthorized changes with automatic detection, restoration, and **user alerting**. +## Vercel Skills Installation + +Install with the Vercel Skills CLI for this harness: + +```bash +npx skills add prompt-security/clawsec --skill soul-guardian -a openclaw -y +``` + ## Operational Notes - Required runtime: `python3` @@ -22,7 +30,6 @@ Protects your agent's core files (SOUL.md, AGENTS.md, etc.) from unauthorized ch - Network behavior: none by default - Trust model: any scheduling is opt-in, but restore mode intentionally overwrites drifted files - ## Release Artifact Verification For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key. @@ -31,7 +38,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI set -euo pipefail SKILL_NAME="soul-guardian" -VERSION="0.0.6" +VERSION="0.0.7" REPO="prompt-security/clawsec" TAG="${SKILL_NAME}-v${VERSION}" BASE="https://github.com/${REPO}/releases/download/${TAG}" diff --git a/skills/soul-guardian/skill.json b/skills/soul-guardian/skill.json index 34f0350..f425c39 100644 --- a/skills/soul-guardian/skill.json +++ b/skills/soul-guardian/skill.json @@ -1,6 +1,6 @@ { "name": "soul-guardian", - "version": "0.0.6", + "version": "0.0.7", "description": "Drift detection and baseline integrity guard for agent workspace prompt files. Auto-restore critical files with tamper-evident audit logging.", "author": "prompt-security", "license": "AGPL-3.0-or-later",