Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] 543b256901 chore(deps): bump github/codeql-action from 4.36.0 to 4.36.2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.36.0 to 4.36.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/7211b7c8077ea37d8641b6271f6a365a22a5fbfa...8aad20d150bbac5944a9f9d289da16a4b0d87c1e)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.36.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-08 21:35:02 +00:00
94 changed files with 1506 additions and 4135 deletions
-56
View File
@@ -1,56 +0,0 @@
{
"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"
]
}
]
}
+2 -14
View File
@@ -53,21 +53,9 @@ jobs:
- name: Collect traffic
env:
# Traffic endpoints reject the Actions GITHUB_TOKEN ("Resource not
# accessible by integration") — a PAT from a user with push access
# is required: classic with repo scope, or fine-grained with read
# access to Administration on this repository.
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN }}
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN || github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
if [ -z "${GH_TRAFFIC_TOKEN}" ]; then
echo "::error::No traffic-capable token configured. Set the TRAFFIC_ARCHIVE_TOKEN secret to a PAT with push access (classic: repo scope; fine-grained: Administration read)."
exit 1
fi
node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}"
run: node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}"
- name: Commit archive
run: |
+2 -6
View File
@@ -111,12 +111,8 @@ 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 Tooling Tests
run: |
set -euo pipefail
for test_file in scripts/test-skill-*.mjs; do
node "$test_file"
done
- name: Skill Release Workflow Tests
run: node scripts/test-skill-release-workflow.mjs
- name: Deploy Pages Advisory Checksums Tests
run: node scripts/test-deploy-pages-checksums.mjs
- name: GitHub Traffic Archive Tests
+2 -2
View File
@@ -27,7 +27,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialize CodeQL
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
@@ -38,4 +38,4 @@ jobs:
- name: Build project
run: npm run build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
+1 -1
View File
@@ -1069,7 +1069,7 @@ jobs:
--event workflow_dispatch \
--limit 50 \
--json databaseId,createdAt,headSha \
| jq -r --arg since "$DISPATCHED_AT" --arg sha "$EXPECTED_HEAD_SHA" '
--jq --arg since "$DISPATCHED_AT" --arg sha "$EXPECTED_HEAD_SHA" '
map(select(.createdAt >= $since and .headSha == $sha))
| sort_by(.createdAt)
| last
+1 -1
View File
@@ -84,6 +84,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with:
sarif_file: results.sarif
+44 -371
View File
@@ -7,9 +7,6 @@ on:
pull_request:
paths:
- 'skills/**'
- '.github/workflows/skill-release.yml'
- 'scripts/ci/**'
- 'scripts/test-skill-*.mjs'
workflow_dispatch:
inputs:
tag:
@@ -38,11 +35,6 @@ 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
@@ -152,6 +144,14 @@ jobs:
md_version_changed=true
fi
if [ "${json_version_changed}" != "true" ] && [ "${md_version_changed}" != "true" ]; then
echo "::error file=${skill_dir}::Changed skill package has no version bump. Update skill.json and SKILL.md versions and add CHANGELOG.md release notes."
failures=$((failures + 1))
continue
fi
echo "Version bump detected for ${skill_dir} (skill.json changed: ${json_version_changed}, SKILL.md changed: ${md_version_changed})"
if [ ! -f "${json_path}" ]; then
echo "::error file=${json_path}::Missing skill.json after version bump."
failures=$((failures + 1))
@@ -182,20 +182,6 @@ jobs:
continue
fi
skill_release_name="$(basename "${skill_dir}")"
release_tag="${skill_release_name}-v${head_json_version}"
if [ "${json_version_changed}" != "true" ] && [ "${md_version_changed}" != "true" ]; then
if git show-ref --verify --quiet "refs/tags/${release_tag}"; then
echo "::error file=${skill_dir}::Changed skill package has no version bump and release tag ${release_tag} already exists. Update skill.json and SKILL.md versions and add CHANGELOG.md release notes."
failures=$((failures + 1))
continue
fi
echo "No version bump detected for ${skill_dir}, but release tag ${release_tag} does not exist; treating ${head_json_version} as unreleased."
else
echo "Version bump detected for ${skill_dir} (skill.json changed: ${json_version_changed}, SKILL.md changed: ${md_version_changed})"
fi
echo "Version parity OK for ${skill_dir}: ${head_json_version}"
changelog_path="${skill_dir}/CHANGELOG.md"
@@ -237,17 +223,11 @@ jobs:
fi
if [ "${failures}" -gt 0 ]; then
echo "::error::Found ${failures} skill metadata/release-notes issue(s) across ${checked_skills} changed skill(s)."
echo "::error::Found ${failures} skill metadata/release-notes issue(s) across ${checked_skills} bumped skill(s)."
exit 1
fi
echo "Validated ${checked_skills} changed 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"
echo "Validated ${checked_skills} bumped skill(s): version parity and changelog release notes are present."
release:
if: github.event_name == 'pull_request'
@@ -261,21 +241,6 @@ 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
@@ -434,50 +399,6 @@ 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"
@@ -701,58 +622,6 @@ 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 "${inner_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
@@ -783,56 +652,6 @@ 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
@@ -845,7 +664,6 @@ jobs:
publishable: ${{ steps.publishable.outputs.publishable }}
openclaw_skill: ${{ steps.publishable.outputs.openclaw_skill }}
publish_clawhub: ${{ steps.publishable.outputs.publish_clawhub }}
clawhub_slug: ${{ steps.publishable.outputs.clawhub_slug }}
steps:
- name: Parse tag
id: parse
@@ -943,32 +761,16 @@ jobs:
PUBLISH_CLAWHUB=true
fi
CLAWHUB_SLUG=$(node scripts/ci/resolve_clawhub_slug.mjs "$SKILL_PATH")
echo "internal=${INTERNAL}" >> $GITHUB_OUTPUT
echo "openclaw_skill=${OPENCLAW_SKILL}" >> $GITHUB_OUTPUT
echo "publish_clawhub=${PUBLISH_CLAWHUB}" >> $GITHUB_OUTPUT
echo "publishable=${PUBLISHABLE}" >> $GITHUB_OUTPUT
echo "clawhub_slug=${CLAWHUB_SLUG}" >> $GITHUB_OUTPUT
- name: Setup Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
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
@@ -1068,49 +870,6 @@ 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"
@@ -1212,32 +971,6 @@ 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 "$INNER_DIR" "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
@@ -1322,7 +1055,6 @@ jobs:
run: |
set -euo pipefail
SKILL_NAME="${{ steps.parse.outputs.skill_name }}"
CLAWHUB_SLUG="${{ steps.publishable.outputs.clawhub_slug }}"
VERSION="${{ steps.parse.outputs.version }}"
REPO="${{ github.repository }}"
TAG="${{ github.ref_name }}"
@@ -1330,33 +1062,13 @@ jobs:
{
echo "quick_install<<INSTALL_EOF"
cat <<EOF
### Agent Skills CLI
**Codex global install:**
\`\`\`bash
npx skills add ${REPO} --skill ${SKILL_NAME} --agent codex --global --yes
\`\`\`
**OpenClaw global install:**
\`\`\`bash
npx skills add ${REPO} --skill ${SKILL_NAME} --agent openclaw --global --yes
\`\`\`
**Update an installed skill:**
\`\`\`bash
npx skills update ${SKILL_NAME}
\`\`\`
EOF
if [ "${{ steps.publishable.outputs.publish_clawhub }}" = "true" ] && [ "${{ steps.publishable.outputs.openclaw_skill }}" = "true" ]; then
cat <<EOF
### Quick Install
**Via ClawHub (recommended):**
\`\`\`bash
npx clawhub@latest install ${CLAWHUB_SLUG}
npx clawhub@latest install ${SKILL_NAME}
\`\`\`
**If you already have \`clawsec-suite\` installed:**
@@ -1403,63 +1115,38 @@ jobs:
echo "INSTALL_EOF"
} >> "$GITHUB_OUTPUT"
- name: Prepare GitHub release body
env:
SKILL_NAME: ${{ steps.parse.outputs.skill_name }}
VERSION: ${{ steps.parse.outputs.version }}
CHANGELOG: ${{ steps.changelog.outputs.changelog }}
QUICK_INSTALL: ${{ steps.install.outputs.quick_install }}
REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
node -e '
const { readFileSync, writeFileSync } = require("node:fs");
const bodyPath = `${process.env.RUNNER_TEMP}/skill-release-body.md`;
const report = readFileSync("release-assets/skillspector-report.md", "utf8").trimEnd();
const body = [
`## ${process.env.SKILL_NAME} ${process.env.VERSION}`,
"",
process.env.CHANGELOG || "",
"",
process.env.QUICK_INSTALL || "",
"",
"### SkillSpector Security Report",
"",
report,
"",
`Download the generated release-payload scan: [skillspector-report.md](https://github.com/${process.env.REPO}/releases/download/${process.env.TAG}/skillspector-report.md)`,
"",
"### Verification",
"",
"`checksums.json` is cryptographically signed (`checksums.sig`) using the ClawSec CI signing key.",
"Verify the signature first, then trust hashes from `checksums.json`:",
"```bash",
`curl -sLO https://github.com/${process.env.REPO}/releases/download/${process.env.TAG}/checksums.json`,
`curl -sLO https://github.com/${process.env.REPO}/releases/download/${process.env.TAG}/checksums.sig`,
`curl -sLO https://github.com/${process.env.REPO}/releases/download/${process.env.TAG}/signing-public.pem`,
"openssl base64 -d -A -in checksums.sig -out checksums.sig.bin",
"openssl pkeyutl -verify -rawin -pubin -inkey signing-public.pem -sigfile checksums.sig.bin -in checksums.json",
"```",
"",
"### Files",
"",
"See `checksums.json` for the complete file manifest with SHA256 hashes.",
"The zip archive preserves the full directory structure of the skill.",
"",
"---",
"*Released by ClawSec skill distribution pipeline*",
].join("\n");
writeFileSync(bodyPath, `${body}\n`);
'
- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
name: "${{ steps.parse.outputs.skill_name }} ${{ steps.parse.outputs.version }}"
tag_name: ${{ github.ref_name }}
files: release-assets/*
body_path: ${{ runner.temp }}/skill-release-body.md
body: |
## ${{ steps.parse.outputs.skill_name }} ${{ steps.parse.outputs.version }}
${{ steps.changelog.outputs.changelog }}
${{ steps.install.outputs.quick_install }}
### Verification
`checksums.json` is cryptographically signed (`checksums.sig`) using the ClawSec CI signing key.
Verify the signature first, then trust hashes from `checksums.json`:
```bash
curl -sLO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/checksums.json
curl -sLO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/checksums.sig
curl -sLO https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/signing-public.pem
openssl base64 -d -A -in checksums.sig -out checksums.sig.bin
openssl pkeyutl -verify -rawin -pubin -inkey signing-public.pem -sigfile checksums.sig.bin -in checksums.json
```
### Files
See `checksums.json` for the complete file manifest with SHA256 hashes.
The zip archive preserves the full directory structure of the skill.
---
*Released by ClawSec skill distribution pipeline*
draft: false
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') }}
env:
@@ -1597,24 +1284,23 @@ jobs:
SITE=${CLAWHUB_SITE:-https://clawhub.ai}
REGISTRY=${CLAWHUB_REGISTRY:-$SITE}
SKILL_NAME="${{ needs.release-tag.outputs.skill_name }}"
CLAWHUB_SLUG="${{ needs.release-tag.outputs.clawhub_slug }}"
VERSION="${{ needs.release-tag.outputs.version }}"
export CLAWHUB_CONFIG_PATH="$HOME/.clawhub-ci/config.json"
set +e
CLAWHUB_DISABLE_TELEMETRY=1 CLAWHUB_SITE="$SITE" CLAWHUB_REGISTRY="$REGISTRY" \
clawhub inspect "$CLAWHUB_SLUG" --version "$VERSION" --json \
clawhub inspect "$SKILL_NAME" --version "$VERSION" --json \
> /tmp/clawhub-existing-version.json 2> /tmp/clawhub-existing-version.err
STATUS=$?
set -e
if [ "$STATUS" -eq 0 ]; then
echo "::error::ClawHub already contains ${CLAWHUB_SLUG}@${VERSION}. Bump the version before tagging."
echo "::error::ClawHub already contains ${SKILL_NAME}@${VERSION}. Bump the version before tagging."
exit 1
fi
if grep -Eqi "Version not found|Skill not found" /tmp/clawhub-existing-version.err; then
echo "No existing ${CLAWHUB_SLUG}@${VERSION} detected in ClawHub. Proceeding."
echo "No existing ${SKILL_NAME}@${VERSION} detected in ClawHub. Proceeding."
else
echo "::error::Failed to verify ClawHub version precondition."
cat /tmp/clawhub-existing-version.err
@@ -1629,7 +1315,6 @@ jobs:
REGISTRY=${CLAWHUB_REGISTRY:-$SITE}
SKILL_PATH="${{ needs.release-tag.outputs.skill_path }}"
SKILL_NAME="${{ needs.release-tag.outputs.skill_name }}"
CLAWHUB_SLUG="${{ needs.release-tag.outputs.clawhub_slug }}"
VERSION="${{ needs.release-tag.outputs.version }}"
NAME=$(jq -r '.name' "$SKILL_PATH/skill.json")
CHANGELOG="Release ${VERSION} via CI"
@@ -1638,7 +1323,7 @@ jobs:
if ! CLAWHUB_DISABLE_TELEMETRY=1 CLAWHUB_SITE="$SITE" CLAWHUB_REGISTRY="$REGISTRY" \
clawhub publish "$SKILL_PATH" \
--slug "$CLAWHUB_SLUG" \
--slug "$SKILL_NAME" \
--name "$NAME" \
--version "$VERSION" \
--changelog "$CHANGELOG" \
@@ -1648,7 +1333,7 @@ jobs:
exit 1
fi
echo "✓ Successfully published $SKILL_NAME@$VERSION to ClawHub as $CLAWHUB_SLUG"
echo "✓ Successfully published $SKILL_NAME@$VERSION to ClawHub"
republish-clawhub:
# Manual workflow to republish a specific tag to ClawHub
@@ -1675,12 +1360,6 @@ jobs:
echo "Parsed tag: skill=${SKILL_NAME}, version=${VERSION}"
- name: Checkout workflow helpers
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Prepare ClawHub slug helper
run: cp scripts/ci/resolve_clawhub_slug.mjs "$RUNNER_TEMP/resolve_clawhub_slug.mjs"
- name: Checkout tag
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -1710,8 +1389,6 @@ jobs:
exit 1
fi
CLAWHUB_SLUG=$(node "$RUNNER_TEMP/resolve_clawhub_slug.mjs" "$SKILL_PATH")
echo "clawhub_slug=${CLAWHUB_SLUG}" >> $GITHUB_OUTPUT
echo "Skill is publishable to ClawHub"
- name: Setup Node
@@ -1719,9 +1396,6 @@ 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 clawhub CLI
run: npm install -g clawhub@${CLAWHUB_CLI_VERSION}
@@ -1790,19 +1464,18 @@ jobs:
REGISTRY=${CLAWHUB_REGISTRY:-$SITE}
SKILL_PATH="${{ steps.parse.outputs.skill_path }}"
SKILL_NAME="${{ steps.parse.outputs.skill_name }}"
CLAWHUB_SLUG="${{ steps.publishable.outputs.clawhub_slug }}"
VERSION="${{ steps.parse.outputs.version }}"
NAME=$(jq -r '.name' "$SKILL_PATH/skill.json")
CHANGELOG="Manual republish of ${VERSION} via workflow_dispatch"
export CLAWHUB_CONFIG_PATH="$HOME/.clawhub-ci/config.json"
echo "Publishing $SKILL_NAME@$VERSION to ClawHub as $CLAWHUB_SLUG..."
echo "Publishing $SKILL_NAME@$VERSION to ClawHub..."
# Publish with idempotent retry handling
if ! CLAWHUB_DISABLE_TELEMETRY=1 CLAWHUB_SITE="$SITE" CLAWHUB_REGISTRY="$REGISTRY" \
clawhub publish "$SKILL_PATH" \
--slug "$CLAWHUB_SLUG" \
--slug "$SKILL_NAME" \
--name "$NAME" \
--version "$VERSION" \
--changelog "$CHANGELOG" \
+1 -1
View File
@@ -58,7 +58,7 @@ ClawSec is a **complete security skill suite for AI agent platforms**. It provid
| clawsec-nanoclaw | NanoClaw | Yes | Yes | Yes | Yes | No |
| clawsec-scanner | OpenClaw | Yes | No | Yes | Yes | No |
| clawsec-suite | OpenClaw | Yes | Yes | No | Yes | No |
| clawtributor | All core platforms | No | No | No | No | No |
| clawtributor | OpenClaw | Yes | No | No | No | No |
| hermes-attestation-guardian | Hermes | Yes (signed advisory feed verification) | Yes | No | Limited (advisory preflight gating only; no artifact signature/provenance install verification) | No |
| hermes-traffic-guardian | Hermes | No | Planned posture export only | No | No | Spec baseline |
| nanoclaw-traffic-guardian | NanoClaw | No | No | No | No | Spec baseline |
+634 -516
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
1mDO2Dzr9LmliE5Gg9mthOpIqx+OuxifLV4BN167XvG9ATyyrBn9N4FwImv396EQpD83MZGO9VzxCPcYYwpuDg==
v+PiWmjIkY6zdIyI9xJX0l0aTy0Azp1+LoZR6qaiDZJnXFuSBX4Sw/x5tMdTb0xSbqdDTJOZwwWI8coPVepzBw==
+89 -117
View File
@@ -1,6 +1,6 @@
{
"version": "0.1.0",
"updated": "2026-06-12T07:34:36Z",
"updated": "2026-06-03T07:38:13Z",
"description": "Provisional ClawSec advisory feed for public GitHub Security Advisories that do not yet have CVE identifiers.",
"stale_after_days": 60,
"semantics": {
@@ -80,8 +80,8 @@
{
"id": "GHSA-rj6p-xmxr-qj4h",
"ghsa_id": "GHSA-rj6p-xmxr-qj4h",
"cve_id": "CVE-2026-53818",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -98,17 +98,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53818 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:40:09Z",
"updated": "2026-05-28T17:40:10Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-rj6p-xmxr-qj4h",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53818"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-rj6p-xmxr-qj4h"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-rj6p-xmxr-qj4h",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53818",
"nvd_url": null,
"cvss_score": 6.6,
"cvss_vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L",
"cwe_ids": [
@@ -120,8 +119,7 @@
"qclawer"
],
"aliases": [
"GHSA-rj6p-xmxr-qj4h",
"CVE-2026-53818"
"GHSA-rj6p-xmxr-qj4h"
]
},
{
@@ -211,8 +209,8 @@
{
"id": "GHSA-p39j-x9h5-q66m",
"ghsa_id": "GHSA-p39j-x9h5-q66m",
"cve_id": "CVE-2026-53809",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -229,17 +227,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53809 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:40:05Z",
"updated": "2026-05-28T17:40:05Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-p39j-x9h5-q66m",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53809"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-p39j-x9h5-q66m"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-p39j-x9h5-q66m",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53809",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [],
@@ -249,8 +246,7 @@
"qclawer"
],
"aliases": [
"GHSA-p39j-x9h5-q66m",
"CVE-2026-53809"
"GHSA-p39j-x9h5-q66m"
]
},
{
@@ -508,8 +504,8 @@
{
"id": "GHSA-v8cx-933x-r976",
"ghsa_id": "GHSA-v8cx-933x-r976",
"cve_id": "CVE-2026-53813",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -526,17 +522,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53813 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:56Z",
"updated": "2026-05-28T17:39:56Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-v8cx-933x-r976",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53813"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-v8cx-933x-r976"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-v8cx-933x-r976",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53813",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [],
@@ -544,8 +539,7 @@
"feynman-hou"
],
"aliases": [
"GHSA-v8cx-933x-r976",
"CVE-2026-53813"
"GHSA-v8cx-933x-r976"
]
},
{
@@ -633,8 +627,8 @@
{
"id": "GHSA-8wg3-5mcm-fjq8",
"ghsa_id": "GHSA-8wg3-5mcm-fjq8",
"cve_id": "CVE-2026-53819",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -651,17 +645,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53819 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:53Z",
"updated": "2026-05-28T17:39:53Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-8wg3-5mcm-fjq8",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53819"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-8wg3-5mcm-fjq8"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-8wg3-5mcm-fjq8",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53819",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [],
@@ -669,8 +662,7 @@
"feynman-hou"
],
"aliases": [
"GHSA-8wg3-5mcm-fjq8",
"CVE-2026-53819"
"GHSA-8wg3-5mcm-fjq8"
]
},
{
@@ -1191,8 +1183,8 @@
{
"id": "GHSA-cqwv-9qjx-vxw2",
"ghsa_id": "GHSA-cqwv-9qjx-vxw2",
"cve_id": "CVE-2026-53808",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -1209,17 +1201,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53808 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:35Z",
"updated": "2026-05-28T17:39:35Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-cqwv-9qjx-vxw2",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53808"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-cqwv-9qjx-vxw2"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-cqwv-9qjx-vxw2",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53808",
"nvd_url": null,
"cvss_score": 5.3,
"cvss_vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
"cwe_ids": [],
@@ -1229,8 +1220,7 @@
"qclawer"
],
"aliases": [
"GHSA-cqwv-9qjx-vxw2",
"CVE-2026-53808"
"GHSA-cqwv-9qjx-vxw2"
]
},
{
@@ -1322,8 +1312,8 @@
{
"id": "GHSA-w5ww-7chg-mxcq",
"ghsa_id": "GHSA-w5ww-7chg-mxcq",
"cve_id": "CVE-2026-53807",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -1340,17 +1330,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53807 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:32Z",
"updated": "2026-05-28T17:39:32Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-w5ww-7chg-mxcq",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53807"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-w5ww-7chg-mxcq"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-w5ww-7chg-mxcq",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53807",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [],
@@ -1360,8 +1349,7 @@
"qclawer"
],
"aliases": [
"GHSA-w5ww-7chg-mxcq",
"CVE-2026-53807"
"GHSA-w5ww-7chg-mxcq"
]
},
{
@@ -1410,8 +1398,8 @@
{
"id": "GHSA-7hxm-f538-3xp6",
"ghsa_id": "GHSA-7hxm-f538-3xp6",
"cve_id": "CVE-2026-53811",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -1428,17 +1416,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53811 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:30Z",
"updated": "2026-05-28T17:39:30Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-7hxm-f538-3xp6",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53811"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-7hxm-f538-3xp6"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-7hxm-f538-3xp6",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53811",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [
@@ -1448,8 +1435,7 @@
"PhilipPhil"
],
"aliases": [
"GHSA-7hxm-f538-3xp6",
"CVE-2026-53811"
"GHSA-7hxm-f538-3xp6"
]
},
{
@@ -1928,8 +1914,8 @@
{
"id": "GHSA-chr9-m4q2-76hw",
"ghsa_id": "GHSA-chr9-m4q2-76hw",
"cve_id": "CVE-2026-53817",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "high",
@@ -1946,17 +1932,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53817 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:12Z",
"updated": "2026-05-28T17:39:12Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-chr9-m4q2-76hw",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53817"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-chr9-m4q2-76hw"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-chr9-m4q2-76hw",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53817",
"nvd_url": null,
"cvss_score": 8,
"cvss_vector": "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"cwe_ids": [
@@ -1969,8 +1954,7 @@
"cantinagen"
],
"aliases": [
"GHSA-chr9-m4q2-76hw",
"CVE-2026-53817"
"GHSA-chr9-m4q2-76hw"
]
},
{
@@ -2024,8 +2008,8 @@
{
"id": "GHSA-6fvr-66p3-3qj4",
"ghsa_id": "GHSA-6fvr-66p3-3qj4",
"cve_id": "CVE-2026-53814",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "high",
@@ -2042,17 +2026,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53814 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:09Z",
"updated": "2026-05-28T17:39:09Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-6fvr-66p3-3qj4",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53814"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-6fvr-66p3-3qj4"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-6fvr-66p3-3qj4",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53814",
"nvd_url": null,
"cvss_score": 8.4,
"cvss_vector": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:L",
"cwe_ids": [
@@ -2064,8 +2047,7 @@
"Ellahinator"
],
"aliases": [
"GHSA-6fvr-66p3-3qj4",
"CVE-2026-53814"
"GHSA-6fvr-66p3-3qj4"
]
},
{
@@ -2116,8 +2098,8 @@
{
"id": "GHSA-3c6j-hq33-3jv4",
"ghsa_id": "GHSA-3c6j-hq33-3jv4",
"cve_id": "CVE-2026-53816",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "high",
@@ -2134,17 +2116,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53816 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:06Z",
"updated": "2026-05-28T17:39:06Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-3c6j-hq33-3jv4",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53816"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-3c6j-hq33-3jv4"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-3c6j-hq33-3jv4",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53816",
"nvd_url": null,
"cvss_score": 7.2,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"cwe_ids": [
@@ -2156,15 +2137,14 @@
"Ellahinator"
],
"aliases": [
"GHSA-3c6j-hq33-3jv4",
"CVE-2026-53816"
"GHSA-3c6j-hq33-3jv4"
]
},
{
"id": "GHSA-2hfg-4fh4-qp7f",
"ghsa_id": "GHSA-2hfg-4fh4-qp7f",
"cve_id": "CVE-2026-53812",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "high",
@@ -2181,17 +2161,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53812 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:04Z",
"updated": "2026-05-28T17:39:04Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-2hfg-4fh4-qp7f",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53812"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-2hfg-4fh4-qp7f"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-2hfg-4fh4-qp7f",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53812",
"nvd_url": null,
"cvss_score": 7.7,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
"cwe_ids": [
@@ -2203,15 +2182,14 @@
"Ellahinator"
],
"aliases": [
"GHSA-2hfg-4fh4-qp7f",
"CVE-2026-53812"
"GHSA-2hfg-4fh4-qp7f"
]
},
{
"id": "GHSA-v6r2-jh58-xx6w",
"ghsa_id": "GHSA-v6r2-jh58-xx6w",
"cve_id": "CVE-2026-53810",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -2228,17 +2206,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53810 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:39:03Z",
"updated": "2026-05-28T17:39:03Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-v6r2-jh58-xx6w",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53810"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-v6r2-jh58-xx6w"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-v6r2-jh58-xx6w",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53810",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [
@@ -2252,8 +2229,7 @@
"Ellahinator"
],
"aliases": [
"GHSA-v6r2-jh58-xx6w",
"CVE-2026-53810"
"GHSA-v6r2-jh58-xx6w"
]
},
{
@@ -2442,8 +2418,8 @@
{
"id": "GHSA-vxx3-6hc9-7cc3",
"ghsa_id": "GHSA-vxx3-6hc9-7cc3",
"cve_id": "CVE-2026-53806",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -2460,17 +2436,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53806 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:38:54Z",
"updated": "2026-05-28T17:38:54Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-vxx3-6hc9-7cc3",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53806"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-vxx3-6hc9-7cc3"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-vxx3-6hc9-7cc3",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53806",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [
@@ -2480,8 +2455,7 @@
"YLChen-007"
],
"aliases": [
"GHSA-vxx3-6hc9-7cc3",
"CVE-2026-53806"
"GHSA-vxx3-6hc9-7cc3"
]
},
{
@@ -2528,8 +2502,8 @@
{
"id": "GHSA-q7q8-3mgw-q67r",
"ghsa_id": "GHSA-q7q8-3mgw-q67r",
"cve_id": "CVE-2026-53815",
"status": "matured",
"cve_id": null,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "medium",
@@ -2547,17 +2521,16 @@
"platforms": [
"openclaw"
],
"action": "Track CVE-2026-53815 in the canonical CVE advisory feed and verify affected components.",
"action": "Review the GitHub Security Advisory and update affected components; no CVE is assigned yet.",
"published": "2026-05-28T17:38:50Z",
"updated": "2026-05-28T17:38:50Z",
"references": [
"https://github.com/openclaw/openclaw/security/advisories/GHSA-q7q8-3mgw-q67r",
"https://nvd.nist.gov/vuln/detail/CVE-2026-53815"
"https://github.com/openclaw/openclaw/security/advisories/GHSA-q7q8-3mgw-q67r"
],
"source": "GitHub Security Advisory",
"repository": "openclaw/openclaw",
"github_advisory_url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-q7q8-3mgw-q67r",
"nvd_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-53815",
"nvd_url": null,
"cvss_score": null,
"cvss_vector": null,
"cwe_ids": [
@@ -2568,8 +2541,7 @@
"samchodev"
],
"aliases": [
"GHSA-q7q8-3mgw-q67r",
"CVE-2026-53815"
"GHSA-q7q8-3mgw-q67r"
]
},
{
@@ -2838,8 +2810,8 @@
"id": "GHSA-jf56-mccx-5f3f",
"ghsa_id": "GHSA-jf56-mccx-5f3f",
"cve_id": null,
"status": "stale",
"stale": true,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "high",
"type": "github_security_advisory",
@@ -2881,8 +2853,8 @@
"id": "GHSA-gfmx-pph7-g46x",
"ghsa_id": "GHSA-gfmx-pph7-g46x",
"cve_id": null,
"status": "stale",
"stale": true,
"status": "active",
"stale": false,
"stale_after_days": 60,
"severity": "high",
"type": "github_security_advisory",
+1 -1
View File
@@ -1 +1 @@
0T+CmosXcbwSSDe75ATSwmuXTVT1jjCGQ/F6ZkmMgdp8OvJ2it87E96QHFO2iy6swvJdCsHUMXSFkcjb5Ij5DA==
SCkRaPMF6IYDwZuR7/JJXxpB7A7ebuMvLqK827uWX0yfEJr7l2gyLpxvHsEpWJDzE4gchxd5yqJx5qF/yqNwAg==
+2 -2
View File
@@ -7,7 +7,7 @@
ClawSec
Security skill suite for AI agents (integrity checks, drift detection, advisory feed).
Agent install:
Available via npx skills: npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y
Available via clawhub: npx clawhub@latest install clawsec-suite
OR
curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md
-->
@@ -141,7 +141,7 @@
ClawSec
Security skill suite for AI agents (integrity checks, drift detection, advisory feed).
Agent install:
Available via npx skills: npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y
Available via clawhub: npx clawhub@latest install clawsec-suite
OR
curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md
</noscript>
+2 -14
View File
@@ -13,7 +13,7 @@ export const Home: React.FC = () => {
const [currentFileIndex, setCurrentFileIndex] = useState(0);
const [currentPlatformIndex, setCurrentPlatformIndex] = useState(0);
const curlCommand = `npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y`;
const curlCommand = `npx clawhub@latest install clawsec-suite`;
// Rotate file names every 2-3 seconds
useEffect(() => {
@@ -44,7 +44,7 @@ export const Home: React.FC = () => {
};
}, []);
const humanInstruction = `Please install clawsec-suite with npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y`;
const humanInstruction = `Please install clawsec-suite from clawhubnpx clawhub@latest install clawsec-suite`;
const handleCopyCurl = () => {
navigator.clipboard.writeText(curlCommand);
@@ -285,18 +285,6 @@ export const Home: React.FC = () => {
</div>
</>
)}
<p className="mt-4 text-center text-xs leading-relaxed text-gray-500">
* For harnesses other than OpenClaw, consult the{' '}
<a
href="https://github.com/prompt-security/clawsec#skill-feature-matrix"
target="_blank"
rel="noreferrer"
className="text-clawd-accent hover:text-clawd-accent/80 underline underline-offset-2"
>
README Skill Feature Matrix
</a>
.
</p>
</div>
</div>
</section>
+1 -8
View File
@@ -321,14 +321,7 @@ const fetchJson = async ({ repo, token, pathname, fetchImpl }) => {
if (!response.ok) {
const body = await response.text().catch(() => '');
const suffix = body ? ` ${body.slice(0, 500)}` : '';
const lacksPushAccess = response.status === 403
&& /resource not accessible|must have push access/i.test(body);
const hint = lacksPushAccess
? ' Traffic endpoints require a token with push access to the repository; the Actions GITHUB_TOKEN is always rejected. Use a classic PAT with the repo scope or a fine-grained PAT with read access to Administration.'
: response.status === 401
? ' The token was rejected as invalid — it may be expired or revoked. Rotate the TRAFFIC_ARCHIVE_TOKEN secret.'
: '';
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}${hint}`);
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}`);
}
return response.json();
@@ -1,359 +0,0 @@
#!/usr/bin/env node
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { installAgentForSkill, PLATFORM_KEYS } from "./skill_platforms.mjs";
const KNOWN_AGENT_TYPES = new Set(["codex", "hermes-agent", "openclaw", "universal"]);
function usage() {
return [
"Usage: node scripts/ci/generate_skill_release_trust_packet.mjs <skill-dir> <output-dir> [options]",
"",
"Options:",
" --repository <owner/repo> Source repository used in install instructions",
" --tag <tag> Release tag for this skill",
" --source-ref <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 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, KNOWN_AGENT_TYPES);
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);
});
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env node
import { existsSync, readFileSync } from "node:fs";
import path from "node:path";
import { collectDeclaredPlatforms, PLATFORM_KEYS } from "./skill_platforms.mjs";
const EXPLICIT_SLUGS = new Map([
["openclaw-traffic-guardian", "clawsec-openclaw-traffic-guardian"],
["openclaw-audit-watchdog", "clawsec-openclaw-audit-watchdog"],
["soul-guardian", "clawsec-openclaw-soul-guardian"],
["hermes-attestation-guardian", "clawsec-hermes-attestation-guardian"],
["hermes-traffic-guardian", "clawsec-hermes-traffic-guardian"],
["nanoclaw-traffic-guardian", "clawsec-nanoclaw-traffic-guardian"],
["picoclaw-security-guardian", "clawsec-picoclaw-security-guardian"],
["picoclaw-self-pen-testing", "clawsec-picoclaw-self-pen-testing"],
["picoclaw-traffic-guardian", "clawsec-picoclaw-traffic-guardian"],
["clawtributor", "clawsec-clawtributor"],
]);
function usage() {
return [
"Usage: node scripts/ci/resolve_clawhub_slug.mjs <skill-dir-or-name>",
"",
"Prints the ClawHub slug for a skill without changing the GitHub release tag or skill package name.",
].join("\n");
}
function loadSkill(input) {
const skillJsonPath = existsSync(path.join(input, "skill.json")) ? path.join(input, "skill.json") : null;
if (!skillJsonPath) {
return { name: input, platforms: [] };
}
const skill = JSON.parse(readFileSync(skillJsonPath, "utf8"));
if (!skill.name || typeof skill.name !== "string") {
throw new Error(`${skillJsonPath} missing string field: name`);
}
return { name: skill.name, platforms: collectDeclaredPlatforms(skill) };
}
export function resolveClawHubSlug({ name, platforms = [] }) {
if (!/^[a-z0-9-]+$/.test(name)) {
throw new Error(`Invalid skill name for ClawHub slug mapping: ${name}`);
}
if (name.startsWith("clawsec-")) {
return name;
}
if (EXPLICIT_SLUGS.has(name)) {
return EXPLICIT_SLUGS.get(name);
}
if (PLATFORM_KEYS.some((platform) => name.startsWith(`${platform}-`))) {
return `clawsec-${name}`;
}
const declaredPlatforms = collectDeclaredPlatforms({ platforms });
if (declaredPlatforms.length === 1 && PLATFORM_KEYS.includes(declaredPlatforms[0])) {
return `clawsec-${declaredPlatforms[0]}-${name}`;
}
return `clawsec-${name}`;
}
if (import.meta.url === `file://${process.argv[1]}`) {
const input = process.argv[2];
if (!input || input === "--help" || input === "-h") {
console.log(usage());
process.exit(input ? 0 : 1);
}
try {
console.log(resolveClawHubSlug(loadSkill(input)));
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
-520
View File
@@ -1,520 +0,0 @@
#!/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 <skill-dir> <output-dir> [options]",
"",
"Options:",
" --repository <owner/repo> Source repository used in release metadata",
" --source-ref <ref> Source ref used in npx skills examples",
" --skillspector-bin <path> 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: innerDir,
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);
});
-52
View File
@@ -1,52 +0,0 @@
export const PLATFORM_KEYS = Object.freeze(["openclaw", "nanoclaw", "hermes", "picoclaw"]);
const PLATFORM_AGENT_ALIASES = new Map([["hermes", "hermes-agent"]]);
function asStringArray(value) {
if (Array.isArray(value)) {
return value.filter((item) => typeof item === "string" && item.trim()).map((item) => item.trim());
}
if (typeof value === "string" && value.trim()) {
return [value.trim()];
}
return [];
}
export function collectDeclaredPlatforms(skill) {
const platforms = new Set([
...asStringArray(skill.platform),
...asStringArray(skill.platforms),
]);
for (const key of PLATFORM_KEYS) {
if (skill[key] && typeof skill[key] === "object") {
platforms.add(key);
}
}
return [...platforms];
}
export function installAgentForSkill(skill, agentTypes, fallback = "openclaw") {
const platforms = collectDeclaredPlatforms(skill);
if (platforms.length === 0) {
return fallback;
}
const matchedAgents = new Set();
let allPlatformsMatched = true;
for (const platform of platforms) {
const candidate = PLATFORM_AGENT_ALIASES.get(platform) || platform;
if (agentTypes.has(candidate)) {
matchedAgents.add(candidate);
} else {
allPlatformsMatched = false;
}
}
if (allPlatformsMatched && matchedAgents.size === 1) {
return [...matchedAgents][0];
}
return fallback;
}
-266
View File
@@ -1,266 +0,0 @@
#!/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";
import { installAgentForSkill } from "./skill_platforms.mjs";
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"];
function usage() {
return [
"Usage: node scripts/ci/validate_skill_install_docs.mjs [options]",
"",
"Options:",
" --root <dir> Repository root. Defaults to current working directory.",
" --repository <owner/repo> Expected npx skills source. Defaults to prompt-security/clawsec.",
" --base <sha> Base ref for changed-skill detection.",
" --head <sha> Head ref for changed-skill detection.",
" --skills <dir[,dir...]> Skill directories to validate.",
" --all Validate every skill directory with skill.json.",
" --agent-types-file <path> Read Vercel AgentType source from a local file.",
" --agent-types-url <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 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 = installAgentForSkill(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);
});
+1 -36
View File
@@ -76,40 +76,6 @@ test('fetchGitHubTraffic requests the daily GitHub traffic endpoints with auth',
assert.deepEqual(snapshot.clones.clones, responses[`/repos/${TEST_REPOSITORY}/traffic/clones?per=day`].clones);
});
test('fetchGitHubTraffic explains traffic token requirements on 403', async () => {
const fetchImpl = async () => new globalThis.Response(
JSON.stringify({ message: 'Resource not accessible by integration' }),
{ status: 403 },
);
await assert.rejects(
fetchGitHubTraffic({
repo: TEST_REPOSITORY,
token: 'installation-token',
capturedAt,
fetchImpl,
}),
/returned 403\..*push access/,
);
});
test('fetchGitHubTraffic flags invalid tokens on 401', async () => {
const fetchImpl = async () => new globalThis.Response(
JSON.stringify({ message: 'Bad credentials' }),
{ status: 401 },
);
await assert.rejects(
fetchGitHubTraffic({
repo: TEST_REPOSITORY,
token: 'expired-token',
capturedAt,
fetchImpl,
}),
/returned 401\..*expired or revoked/,
);
});
test('mergeTrafficArchive upserts daily views and clones without double-counting overlapping windows', () => {
const archive = mergeTrafficArchive(
{
@@ -266,8 +232,7 @@ test('traffic archive workflow uses a daily schedule and a dedicated archive bra
assert.match(workflow, /cron:\s+'17 3 \* \* \*'/);
assert.match(workflow, /TRAFFIC_ARCHIVE_BRANCH:\s+traffic-archive/);
assert.match(workflow, /GH_TRAFFIC_TOKEN:\s*\$\{\{\s*secrets\.TRAFFIC_ARCHIVE_TOKEN\b/);
assert.doesNotMatch(workflow, /GH_TRAFFIC_TOKEN:[^\n]*github\.token/);
assert.match(workflow, /TRAFFIC_ARCHIVE_TOKEN/);
assert.match(workflow, /node scripts\/archive-github-traffic\.mjs/);
assert.match(workflow, /git add traffic\/archive\.json traffic\/summary\.json/);
assert.match(workflow, /git rm --ignore-unmatch traffic\/README\.md/);
@@ -47,16 +47,6 @@ assert.match(
/git add "\$FEED_PATH" "\$FEED_SIG_PATH" "\$GHSA_FEED_PATH" "\$GHSA_FEED_SIG_PATH" "\$SKILL_FEED_PATH" "\$SKILL_FEED_SIG_PATH"/,
'NVD workflow PR must include both NVD and GHSA feed artifacts',
);
assert.doesNotMatch(
workflow,
/gh run list[\s\S]*--jq --arg/,
'CodeQL run lookup must not pass jq CLI flags through gh --jq',
);
assert.match(
workflow,
/gh run list[\s\S]*--json databaseId,createdAt,headSha \\\s*\n\s+\| jq -r --arg since "\$DISPATCHED_AT" --arg sha "\$EXPECTED_HEAD_SHA"/,
'CodeQL run lookup must filter the gh JSON output with jq variables',
);
assert.match(
ciWorkflow,
/name: NVD \+ GHSA Pipeline Dry Run[\s\S]*node scripts\/test-nvd-ghsa-pipeline-dry-run\.mjs/,
-45
View File
@@ -1,45 +0,0 @@
import assert from "node:assert/strict";
import { resolveClawHubSlug } from "./ci/resolve_clawhub_slug.mjs";
import { collectDeclaredPlatforms, installAgentForSkill } from "./ci/skill_platforms.mjs";
const cases = [
["openclaw-traffic-guardian", ["openclaw"], "clawsec-openclaw-traffic-guardian"],
["openclaw-audit-watchdog", ["openclaw"], "clawsec-openclaw-audit-watchdog"],
["soul-guardian", ["openclaw"], "clawsec-openclaw-soul-guardian"],
["hermes-attestation-guardian", ["hermes"], "clawsec-hermes-attestation-guardian"],
["hermes-traffic-guardian", ["hermes"], "clawsec-hermes-traffic-guardian"],
["nanoclaw-traffic-guardian", ["nanoclaw"], "clawsec-nanoclaw-traffic-guardian"],
["picoclaw-security-guardian", ["picoclaw"], "clawsec-picoclaw-security-guardian"],
["picoclaw-self-pen-testing", ["picoclaw"], "clawsec-picoclaw-self-pen-testing"],
["picoclaw-traffic-guardian", ["picoclaw"], "clawsec-picoclaw-traffic-guardian"],
["clawtributor", ["openclaw", "nanoclaw", "hermes", "picoclaw"], "clawsec-clawtributor"],
["clawsec-feed", ["openclaw"], "clawsec-feed"],
["clawsec-suite", ["openclaw"], "clawsec-suite"],
];
for (const [name, platforms, expected] of cases) {
assert.equal(resolveClawHubSlug({ name, platforms }), expected, `${name} should map to ${expected}`);
assert.equal(resolveClawHubSlug({ name }), expected, `${name} should map to ${expected} without metadata`);
}
assert.throws(
() => resolveClawHubSlug({ name: "../openclaw-traffic-guardian", platforms: ["openclaw"] }),
/Invalid skill name/,
"unsafe skill names must be rejected",
);
assert.deepEqual(
collectDeclaredPlatforms({
platform: "openclaw",
platforms: ["hermes", "openclaw", ""],
picoclaw: { requires: {} },
}),
["openclaw", "hermes", "picoclaw"],
"declared platform parsing should combine legacy fields, arrays, and platform metadata keys",
);
assert.equal(
installAgentForSkill({ platform: "hermes" }, new Set(["codex", "hermes-agent", "openclaw"])),
"hermes-agent",
"install agent selection should reuse platform aliases",
);
-137
View File
@@ -1,137 +0,0 @@
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 });
}
+3 -233
View File
@@ -2,9 +2,7 @@ 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,
@@ -12,22 +10,6 @@ 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\/\*\*'/,
@@ -37,223 +19,11 @@ assert.match(
assert.doesNotMatch(
workflow,
/No version bump detected for \$\{skill_dir\}; skipping\./,
'Changed skill directories without a version bump must not be skipped without release-tag validation',
'Changed skill directories without a version bump must fail validation instead of being skipped',
);
assert.match(
workflow,
/skill_release_name="\$\(basename "\$\{skill_dir\}"\)"/,
'Skill release validation must derive the release tag prefix from the skill package directory',
);
assert.match(
workflow,
/release_tag="\$\{skill_release_name\}-v\$\{head_json_version\}"/,
'Skill release validation must use the skill package directory name for release tag checks',
);
assert.doesNotMatch(
workflow,
/release_tag="\$\{head_skill_name\}-v\$\{head_json_version\}"/,
'Skill release validation must not use skill.json name for release tag checks because release tags resolve to skill directories',
);
assert.match(
workflow,
/git show-ref --verify --quiet "refs\/tags\/\$\{release_tag\}"/,
'Skill release validation must check whether the current skill version has already been tagged',
);
assert.match(
workflow,
/No version bump detected for \$\{skill_dir\}, but release tag \$\{release_tag\} does not exist; treating \$\{head_json_version\} as unreleased\./,
'Skill release validation must allow edits to an unchanged version when that release tag does not exist yet',
);
assert.match(
workflow,
/::error file=\$\{skill_dir\}::Changed skill package has no version bump and release tag \$\{release_tag\} already exists\./,
'Skill release validation must still fail unchanged versions after their release tag exists',
);
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,
/### SkillSpector Security Report[\s\S]*\[skillspector-report\.md\]\(https:\/\/github\.com\/\$\{process\.env\.REPO\}\/releases\/download\/\$\{process\.env\.TAG\}\/skillspector-report\.md\)/,
'GitHub release notes must include a direct SkillSpector report link',
);
assert.match(
workflow,
/readFileSync\("release-assets\/skillspector-report\.md", "utf8"\)/,
'GitHub release notes must load the generated SkillSpector report content into the release body file',
);
assert.match(
workflow,
/body_path: \$\{\{ runner\.temp \}\}\/skill-release-body\.md/,
'GitHub release creation must use body_path for the generated release body file',
);
assert.doesNotMatch(
workflow,
/SKILLSPECTOR_REPORT_EOF|\$\{\{ steps\.skillspector_report\.outputs\.body \}\}|cat release-assets\/skillspector-report\.md[\s\S]*>> "\$GITHUB_OUTPUT"/,
'SkillSpector report content must not be sent through GitHub Actions step outputs',
);
assert.match(
workflow,
/generate_skillspector_report "\$\{inner_dir\}" "\$\{out_assets\}\/skillspector-report\.md"/,
'PR dry-run SkillSpector scan must target the staged release payload, not the source skill directory',
);
assert.doesNotMatch(
workflow,
/generate_skillspector_report "\$\{skill_dir\}" "\$\{out_assets\}\/skillspector-report\.md"/,
'PR dry-run SkillSpector scan must not include source-only test directories',
);
assert.match(
workflow,
/generate_skillspector_report "\$INNER_DIR" "release-assets\/skillspector-report\.md"/,
'Tag release SkillSpector scan must target the staged release payload, not the source skill directory',
);
assert.doesNotMatch(
workflow,
/generate_skillspector_report "\$SKILL_PATH" "release-assets\/skillspector-report\.md"/,
'Tag release SkillSpector scan must not include source-only test directories',
);
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',
);
assert.match(
workflow,
/clawhub_slug: \$\{\{ steps\.publishable\.outputs\.clawhub_slug \}\}/,
'Skill release workflow must expose the resolved ClawHub slug from release-tag outputs',
);
assert.match(
workflow,
/CLAWHUB_SLUG=\$\(node scripts\/ci\/resolve_clawhub_slug\.mjs "\$SKILL_PATH"\)/,
'Skill release workflow must resolve the ClawHub slug from the skill package path',
);
assert.match(
workflow,
/cp scripts\/ci\/resolve_clawhub_slug\.mjs "\$RUNNER_TEMP\/resolve_clawhub_slug\.mjs"/,
'Manual ClawHub republish must preserve the current slug helper before checking out an older release tag',
);
assert.match(
workflow,
/CLAWHUB_SLUG=\$\(node "\$RUNNER_TEMP\/resolve_clawhub_slug\.mjs" "\$SKILL_PATH"\)/,
'Manual ClawHub republish must resolve slugs with the preserved helper against the checked-out tag metadata',
);
assert.match(
workflow,
/npx clawhub@latest install \$\{CLAWHUB_SLUG\}/,
'GitHub release quick install instructions must use the resolved ClawHub slug',
);
assert.match(
workflow,
/clawhub inspect "\$CLAWHUB_SLUG" --version "\$VERSION" --json/,
'Duplicate ClawHub version guard must inspect the resolved ClawHub slug',
);
assert.match(
workflow,
/--slug "\$CLAWHUB_SLUG"/,
'ClawHub publish must use the resolved ClawHub slug',
);
assert.doesNotMatch(
workflow,
/clawhub inspect "\$SKILL_NAME" --version "\$VERSION" --json/,
'Duplicate ClawHub version guard must not inspect the raw skill package name',
);
assert.doesNotMatch(
workflow,
/--slug "\$SKILL_NAME"/,
'ClawHub publish must not use the raw skill package name as the ClawHub slug',
/::error file=\$\{skill_dir\}::Changed skill package has no version bump\./,
'Skill release validation must emit an explicit missing-version-bump error',
);
@@ -1,184 +0,0 @@
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 { readdirSync, writeFileSync } from "node:fs";
import path from "node:path";
const scanIndex = process.argv.indexOf("scan");
if (scanIndex === -1 || !process.argv[scanIndex + 1]) {
console.error("missing scan target");
process.exit(2);
}
function containsTestDirectory(dir) {
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (!entry.isDirectory()) {
continue;
}
const lowerName = entry.name.toLowerCase();
if (lowerName === "test" || lowerName === "tests") {
return true;
}
if (containsTestDirectory(path.join(dir, entry.name))) {
return true;
}
}
return false;
}
const scanTarget = process.argv[scanIndex + 1];
if (containsTestDirectory(scanTarget)) {
console.error("SkillSpector test fixture must scan the staged release payload, not source test directories.");
process.exit(42);
}
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 });
}
-79
View File
@@ -1,79 +0,0 @@
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 });
}
-7
View File
@@ -1,12 +1,5 @@
# 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
-11
View File
@@ -1,11 +0,0 @@
# 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
```
+4 -16
View File
@@ -1,14 +1,9 @@
---
name: claw-release
version: 0.0.4
version: 0.0.3
description: Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.
homepage: https://clawsec.prompt.security
metadata:
internal: true
openclaw:
emoji: "🚀"
category: "utility"
internal: true
metadata: {"openclaw":{"emoji":"🚀","category":"utility","internal":true}}
clawdis:
emoji: "🚀"
requires:
@@ -23,14 +18,6 @@ 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.
@@ -39,6 +26,7 @@ npx skills add prompt-security/clawsec --skill claw-release -a openclaw -y
- 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.
@@ -47,7 +35,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="claw-release"
VERSION="0.0.4"
VERSION="0.0.3"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claw-release",
"version": "0.0.4",
"version": "0.0.3",
"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",
@@ -1,11 +1,5 @@
# 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
-8
View File
@@ -2,14 +2,6 @@
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`
+2 -9
View File
@@ -1,6 +1,6 @@
---
name: clawsec-clawhub-checker
version: 0.0.6
version: 0.0.5
description: ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.
homepage: https://clawsec.prompt.security
clawdis:
@@ -14,14 +14,6 @@ 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`
@@ -53,6 +45,7 @@ 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.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "clawsec-clawhub-checker",
"version": "0.0.6",
"version": "0.0.5",
"description": "ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.",
"author": "abutbul",
"license": "AGPL-3.0-or-later",
-6
View File
@@ -1,11 +1,5 @@
# 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
-8
View File
@@ -2,14 +2,6 @@
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`
+4 -10
View File
@@ -1,6 +1,6 @@
---
name: clawsec-feed
version: 0.0.9
version: 0.0.8
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,14 +16,6 @@ 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`
@@ -74,6 +66,7 @@ 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 | \
@@ -88,6 +81,7 @@ 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.
@@ -96,7 +90,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="clawsec-feed"
VERSION="0.0.9"
VERSION="0.0.8"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
1mDO2Dzr9LmliE5Gg9mthOpIqx+OuxifLV4BN167XvG9ATyyrBn9N4FwImv396EQpD83MZGO9VzxCPcYYwpuDg==
v+PiWmjIkY6zdIyI9xJX0l0aTy0Azp1+LoZR6qaiDZJnXFuSBX4Sw/x5tMdTb0xSbqdDTJOZwwWI8coPVepzBw==
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "clawsec-feed",
"version": "0.0.9",
"version": "0.0.8",
"description": "Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence.",
"author": "prompt-security",
"license": "AGPL-3.0-or-later",
-6
View File
@@ -1,11 +1,5 @@
# 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
-8
View File
@@ -2,14 +2,6 @@
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
+2 -9
View File
@@ -1,6 +1,6 @@
---
name: clawsec-nanoclaw
version: 0.0.8
version: 0.0.7
description: Use when checking for security vulnerabilities in NanoClaw skills, before installing new skills, or when asked about security advisories affecting the bot
---
@@ -8,14 +8,6 @@ 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.
@@ -209,6 +201,7 @@ 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.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "clawsec-nanoclaw",
"version": "0.0.8",
"version": "0.0.7",
"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",
-6
View File
@@ -1,11 +1,5 @@
# 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
-11
View File
@@ -1,11 +0,0 @@
# 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
```
+1 -9
View File
@@ -1,6 +1,6 @@
---
name: clawsec-scanner
version: 0.0.5
version: 0.0.4
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,14 +20,6 @@ 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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "clawsec-scanner",
"version": "0.0.5",
"version": "0.0.4",
"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",
-6
View File
@@ -1,11 +1,5 @@
# 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/),
-11
View File
@@ -1,11 +0,0 @@
# 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
```
+1 -9
View File
@@ -1,6 +1,6 @@
---
name: clawsec-suite
version: 0.1.10
version: 0.1.9
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,14 +11,6 @@ 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`
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "clawsec-suite",
"version": "0.1.10",
"version": "0.1.9",
"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",
-10
View File
@@ -1,15 +1,5 @@
# 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
-22
View File
@@ -2,20 +2,6 @@
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
@@ -31,14 +17,6 @@ npx skills add prompt-security/clawsec --skill clawtributor -a codex -y
## 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
```
+12 -38
View File
@@ -1,44 +1,23 @@
---
name: clawtributor
version: 0.0.7
description: Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.
version: 0.0.6
description: Community incident reporting for AI agents. Contribute to collective security by reporting threats.
homepage: https://clawsec.prompt.security
platforms:
- openclaw
- nanoclaw
- hermes
- picoclaw
metadata:
global: true
openclaw:
emoji: "🤝"
category: "security"
metadata: {"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: 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/`
- Recommended install path: ClawHub registry (`npx clawhub@latest install clawtributor`)
- Side effects: creates local report/state files under `~/.openclaw/`
- 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
@@ -48,13 +27,7 @@ npx skills add prompt-security/clawsec --skill clawtributor -a codex -y
## Installation
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:
Install from the registry:
```bash
npx clawhub@latest install clawtributor
@@ -71,6 +44,7 @@ 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.
@@ -79,7 +53,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="clawtributor"
VERSION="0.0.7"
VERSION="0.0.6"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -259,7 +233,7 @@ See [reporting.md](./reporting.md) for the full report format and submission gui
### Step 1: Prepare report locally
- Save the report JSON under `~/.clawsec/clawtributor/reports/`
- Save the report JSON under `~/.openclaw/clawtributor-reports/`
- Keep file permissions private (`chmod 600`)
- Confirm the report is sanitized before sharing
@@ -310,7 +284,7 @@ DO NOT include:
## State Tracking
Track submitted reports in `~/.clawsec/clawtributor/state.json`.
Track submitted reports in `~/.openclaw/clawtributor-state.json`.
Example:
+7 -10
View File
@@ -1,24 +1,16 @@
{
"name": "clawtributor",
"version": "0.0.7",
"description": "Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.",
"version": "0.0.6",
"description": "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"
],
@@ -44,6 +36,11 @@
"openclaw": {
"emoji": "🤝",
"category": "security",
"requires": {
"bins": [
"openclaw"
]
},
"execution": {
"always": false,
"persistence": "Stores local report/state files only; no recurring automation is created by default.",
@@ -1,11 +1,5 @@
# 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
@@ -4,14 +4,6 @@ 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:
+2 -9
View File
@@ -1,6 +1,6 @@
---
name: hermes-attestation-guardian
version: 0.1.4
version: 0.1.3
description: Hermes-only runtime security attestation and drift detection skill for operator-managed Hermes infrastructure.
homepage: https://clawsec.prompt.security
hermes:
@@ -15,13 +15,6 @@ 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
@@ -31,7 +24,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="hermes-attestation-guardian"
VERSION="0.1.4"
VERSION="0.1.3"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -1,6 +1,6 @@
{
"name": "hermes-attestation-guardian",
"version": "0.1.4",
"version": "0.1.3",
"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",
@@ -1,11 +1,5 @@
# 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
+1 -8
View File
@@ -4,14 +4,6 @@ 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
@@ -23,3 +15,4 @@ npx skills add prompt-security/clawsec --skill hermes-traffic-guardian -a hermes
## Builder Notes
Keep runtime ownership in this skill. `hermes-attestation-guardian` should only attest this skill's state, config, and output fingerprints.
+3 -9
View File
@@ -1,6 +1,6 @@
---
name: hermes-traffic-guardian
version: 0.0.1-beta3
version: 0.0.1-beta2
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,13 +15,6 @@ 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
@@ -31,7 +24,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="hermes-traffic-guardian"
VERSION="0.0.1-beta3"
VERSION="0.0.1-beta2"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -152,3 +145,4 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking
- sending traffic to external services
- collecting full request/response bodies
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hermes-traffic-guardian",
"version": "0.0.1-beta3",
"version": "0.0.1-beta2",
"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",
@@ -1,11 +1,5 @@
# 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
+1 -8
View File
@@ -4,14 +4,6 @@ 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
@@ -23,3 +15,4 @@ npx skills add prompt-security/clawsec --skill nanoclaw-traffic-guardian -a open
## Builder Notes
Follow the existing `clawsec-nanoclaw` pattern: host services own privileged operations, while MCP tools expose bounded requests and redacted responses.
+3 -9
View File
@@ -1,6 +1,6 @@
---
name: nanoclaw-traffic-guardian
version: 0.0.1-beta3
version: 0.0.1-beta2
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,13 +14,6 @@ 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
@@ -30,7 +23,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="nanoclaw-traffic-guardian"
VERSION="0.0.1-beta3"
VERSION="0.0.1-beta2"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -153,3 +146,4 @@ 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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "nanoclaw-traffic-guardian",
"version": "0.0.1-beta3",
"version": "0.0.1-beta2",
"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",
@@ -1,11 +1,5 @@
# 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
-8
View File
@@ -2,14 +2,6 @@
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:
+3 -10
View File
@@ -1,6 +1,6 @@
---
name: openclaw-audit-watchdog
version: 0.1.7
version: 0.1.6
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,14 +29,6 @@ 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:
@@ -73,6 +65,7 @@ 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.
@@ -81,7 +74,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="openclaw-audit-watchdog"
VERSION="0.1.7"
VERSION="0.1.6"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "openclaw-audit-watchdog",
"version": "0.1.7",
"version": "0.1.6",
"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",
@@ -1,15 +1,5 @@
# Changelog
## [0.0.1-beta3] - 2026-06-10
### Security
- Added the `POLICY_REVIEW` scope for approval-sensitive social-account mutation requests, contributed by @kriptoburak.
- Defined required JSONL metadata for social-account mutation findings, including source type, mutation category, approval-marker presence, and execution context.
### Changed
- Clarified that persistent social monitor and webhook configuration changes are review findings, while read-only social research should remain covered by no-false-positive tests.
- 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
+1 -9
View File
@@ -4,19 +4,10 @@ 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
- detect inbound command-injection and tool-abuse payloads
- record operator-review findings for approval-sensitive social-account mutations
- write redacted local JSONL findings
- provide explicit start, stop, status, and log-query commands
- integrate with `clawsec-suite` as an optional add-on
@@ -24,3 +15,4 @@ npx skills add prompt-security/clawsec --skill openclaw-traffic-guardian -a open
## Builder Notes
Use `SPEC.md` as the implementation contract. Keep runtime changes opt-in and scoped to the OpenClaw process being monitored.
+6 -15
View File
@@ -1,7 +1,7 @@
---
name: openclaw-traffic-guardian
version: 0.0.1-beta3
description: OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, inbound injection detection, and social-account policy review.
version: 0.0.1-beta2
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
license: AGPL-3.0-or-later
@@ -15,13 +15,6 @@ 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
@@ -31,7 +24,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="openclaw-traffic-guardian"
VERSION="0.0.1-beta3"
VERSION="0.0.1-beta2"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -110,7 +103,6 @@ Builders should use this skill as the OpenClaw landing zone for runtime traffic
- optional HTTPS inspection with per-process CA trust
- outbound exfiltration detection
- inbound injection detection
- approval-sensitive social-account mutation review
- redacted local threat logs
- optional OpenClaw hook/status integration
@@ -144,10 +136,8 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
3. Scope proxy environment variables to the target OpenClaw process.
4. Inspect HTTP request/response text up to a bounded byte limit.
5. Support optional HTTPS MITM only when the operator supplies per-process trust configuration.
6. Flag requests matching `SPEC.md`'s Outbound POLICY_REVIEW cases as operator-review findings, including TweetClaw or other X/Twitter automation writes and scheduler/background-runner repeats without a fresh operator-approval marker.
7. Detect repeat/background-runner context from bounded request metadata such as paths, headers, user-agent, client context, tool invocation metadata, or scheduler identifiers.
8. Emit JSONL findings with redacted snippets plus source type, mutation category, approval-marker presence, and direct-operator versus background-runner context.
9. Provide a `status` command that reports mode, listener, CA fingerprint if present, and last findings.
6. Emit JSONL findings with redacted snippets.
7. Provide a `status` command that reports mode, listener, CA fingerprint if present, and last findings.
## Out of Scope for v0.0.1 Implementation
@@ -156,3 +146,4 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking
- sending traffic to external services
- collecting full request/response bodies
+1 -26
View File
@@ -45,24 +45,6 @@ Findings must be JSON objects with these fields:
}
```
`POLICY_REVIEW` findings must keep the same base schema and add these fields:
```json
{
"threat_type": "POLICY_REVIEW",
"pattern": "social_account_mutation",
"source_type": "openclaw_tool_request",
"mutation_category": "post",
"approval_marker_present": false,
"execution_context": "background_runner"
}
```
- `source_type`: `http_request`, `openclaw_tool_request`, or `unknown`.
- `mutation_category`: `post`, `reply`, `repost`, `like`, `follow`, `unfollow`, `dm`, `media_upload`, `persistent_monitor`, `webhook_config`, `giveaway_draw`, or `other_social_account_mutation`.
- `approval_marker_present`: boolean; do not persist marker secrets or full approval tokens.
- `execution_context`: `direct_operator`, `scheduler`, `background_runner`, or `unknown`.
## Minimum Detection Set
Outbound EXFIL:
@@ -82,12 +64,6 @@ Inbound INJECTION:
- destructive remove commands
- SSH authorized-key injection shapes
Outbound POLICY_REVIEW:
- social-account write requests such as post, reply, repost, like, follow, unfollow, DM, media upload, persistent monitor creation/update, webhook configuration changes, or giveaway draw actions
- OpenClaw plugin/tool requests that invoke TweetClaw or another X/Twitter automation plugin for account mutation
- scheduler or background-runner requests that would repeat social-account mutations without a fresh operator approval
## Safety Requirements
- Default mode is detect-and-log.
@@ -96,7 +72,6 @@ Outbound POLICY_REVIEW:
- Maximum scan bytes must be configurable and bounded.
- CA trust must be per-process by default.
- System trust-store instructions must require explicit operator confirmation and must never run automatically.
- POLICY_REVIEW findings must create an operator-review record only; they must not auto-block, auto-approve, or rewrite the requested action.
## Tests Required Before Release
@@ -104,7 +79,7 @@ Outbound POLICY_REVIEW:
- redaction tests proving secrets are not persisted
- proxy fixture tests for HTTP request and response inspection
- no-false-positive tests for common benign traffic
- policy-review fixture tests for TweetClaw/social-account mutation examples and benign read-only social research requests
- lifecycle tests for stale PID/state cleanup
- status output tests
- OpenClaw hook integration tests if hook files are added
+4 -11
View File
@@ -1,7 +1,7 @@
{
"name": "openclaw-traffic-guardian",
"version": "0.0.1-beta3",
"description": "OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, inbound injection detection, and social-account policy review.",
"version": "0.0.1-beta2",
"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",
"homepage": "https://clawsec.prompt.security/",
@@ -15,10 +15,7 @@
"injection",
"proxy",
"mitm",
"runtime",
"policy-review",
"operator-review",
"social-account-mutation"
"runtime"
],
"sbom": {
"files": [
@@ -87,7 +84,6 @@
"https_mitm_inspection": "planned_optional",
"egress_exfiltration_detection": "planned",
"inbound_injection_detection": "planned",
"social_account_policy_review": "planned",
"blocking": "future_version"
},
"execution": {
@@ -100,7 +96,6 @@
"Default to detect-and-log mode; blocking is out of scope for v0.0.1 implementation.",
"Scope HTTP_PROXY/HTTPS_PROXY to the OpenClaw process being monitored.",
"Redact secret snippets before writing logs or sending conversation alerts.",
"Record POLICY_REVIEW findings for approval-sensitive social-account mutations without auto-blocking, auto-approving, or rewriting requests.",
"Integrate with clawsec-suite as an optional add-on, not a default install."
],
"triggers": [
@@ -108,9 +103,7 @@
"openclaw traffic monitoring",
"monitor openclaw egress",
"inspect openclaw http traffic",
"detect openclaw exfiltration",
"review social account mutations",
"detect tweetclaw write actions"
"detect openclaw exfiltration"
]
}
}
@@ -1,11 +1,5 @@
# 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
+1 -8
View File
@@ -6,14 +6,6 @@ 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 |
@@ -56,3 +48,4 @@ 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.
+2 -9
View File
@@ -1,6 +1,6 @@
---
name: picoclaw-security-guardian
version: 0.0.4
version: 0.0.3
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,13 +18,6 @@ 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
@@ -34,7 +27,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="picoclaw-security-guardian"
VERSION="0.0.4"
VERSION="0.0.3"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "picoclaw-security-guardian",
"version": "0.0.4",
"version": "0.0.3",
"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",
@@ -1,11 +1,5 @@
# 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
@@ -4,14 +4,6 @@ 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.
+2 -9
View File
@@ -1,6 +1,6 @@
---
name: picoclaw-self-pen-testing
version: 0.0.3
version: 0.0.2
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,13 +18,6 @@ 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
@@ -34,7 +27,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="picoclaw-self-pen-testing"
VERSION="0.0.3"
VERSION="0.0.2"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "picoclaw-self-pen-testing",
"version": "0.0.3",
"version": "0.0.2",
"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",
@@ -1,11 +1,5 @@
# 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
+1 -8
View File
@@ -4,14 +4,6 @@ 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
@@ -23,3 +15,4 @@ npx skills add prompt-security/clawsec --skill picoclaw-traffic-guardian -a open
## 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.
+3 -9
View File
@@ -1,6 +1,6 @@
---
name: picoclaw-traffic-guardian
version: 0.0.1-beta3
version: 0.0.1-beta2
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,13 +15,6 @@ 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
@@ -31,7 +24,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="picoclaw-traffic-guardian"
VERSION="0.0.1-beta3"
VERSION="0.0.1-beta2"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -152,3 +145,4 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking
- sending traffic to external services
- collecting full request/response bodies
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "picoclaw-traffic-guardian",
"version": "0.0.1-beta3",
"version": "0.0.1-beta2",
"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",
-6
View File
@@ -1,11 +1,5 @@
# 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
-8
View File
@@ -2,14 +2,6 @@
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`
+3 -10
View File
@@ -1,6 +1,6 @@
---
name: soul-guardian
version: 0.0.7
version: 0.0.6
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,14 +14,6 @@ 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`
@@ -30,6 +22,7 @@ npx skills add prompt-security/clawsec --skill soul-guardian -a openclaw -y
- 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.
@@ -38,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail
SKILL_NAME="soul-guardian"
VERSION="0.0.7"
VERSION="0.0.6"
REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "soul-guardian",
"version": "0.0.7",
"version": "0.0.6",
"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",