mirror of
https://github.com/prompt-security/clawsec.git
synced 2026-06-13 05:28:02 +03:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 543b256901 |
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
1mDO2Dzr9LmliE5Gg9mthOpIqx+OuxifLV4BN167XvG9ATyyrBn9N4FwImv396EQpD83MZGO9VzxCPcYYwpuDg==
|
||||
v+PiWmjIkY6zdIyI9xJX0l0aTy0Azp1+LoZR6qaiDZJnXFuSBX4Sw/x5tMdTb0xSbqdDTJOZwwWI8coPVepzBw==
|
||||
@@ -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 @@
|
||||
0T+CmosXcbwSSDe75ATSwmuXTVT1jjCGQ/F6ZkmMgdp8OvJ2it87E96QHFO2iy6swvJdCsHUMXSFkcjb5Ij5DA==
|
||||
SCkRaPMF6IYDwZuR7/JJXxpB7A7ebuMvLqK827uWX0yfEJr7l2gyLpxvHsEpWJDzE4gchxd5yqJx5qF/yqNwAg==
|
||||
+2
-2
@@ -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
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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/,
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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,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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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 @@
|
||||
1mDO2Dzr9LmliE5Gg9mthOpIqx+OuxifLV4BN167XvG9ATyyrBn9N4FwImv396EQpD83MZGO9VzxCPcYYwpuDg==
|
||||
v+PiWmjIkY6zdIyI9xJX0l0aTy0Azp1+LoZR6qaiDZJnXFuSBX4Sw/x5tMdTb0xSbqdDTJOZwwWI8coPVepzBw==
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,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",
|
||||
|
||||
@@ -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/),
|
||||
|
||||
@@ -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,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,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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
Reference in New Issue
Block a user