Compare commits

..

16 Commits

Author SHA1 Message Date
David Abutbul bd6403073e fix(release): resolve metadata review comments 2026-06-23 00:24:51 +03:00
David Abutbul 6d155d747b fix(release): keep skillspector PR comments non-blocking 2026-06-23 00:17:44 +03:00
David Abutbul 0f79f45405 chore(release): bump skill metadata for republish 2026-06-23 00:12:24 +03:00
davida-ps de28dadd39 fix(release): update ClawHub slug pipeline deps (#277)
* fix(release): update clawhub slug pipeline deps

* fix(release): bump clawhub cli undici lock

* fix(release): guard clawhub publish slug

* fix(release): keep suite slug releasable

* fix(release): harden clawhub slug guard

* fix(release): pass clawhub registry to slug guard
2026-06-22 23:45:24 +03:00
github-actions[bot] f937384104 chore: update NVD/GHSA advisories - 0 NVD new, 27 NVD updated (#276)
Automated update from NVD CVE and GHSA advisory feeds.
Keywords: openclaw, nanoclaw, hermes, picoclaw
Poll window: 2026-06-17T07:45:48Z to 2026-06-21T07:34:32.000Z

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-21 14:32:23 +03:00
github-actions[bot] 8648aad6d7 chore: update NVD/GHSA advisories - 27 NVD new, 20 NVD updated (#274)
* chore: update NVD/GHSA advisories - 27 NVD new, 20 NVD updated

Automated update from NVD CVE and GHSA advisory feeds.
Keywords: openclaw, nanoclaw, hermes, picoclaw
Poll window: 2026-06-14T07:33:37Z to 2026-06-17T07:44:37.000Z

* fix(skill-release): ignore generated advisory mirror updates

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: David Abutbul <David.a@prompt.security>
2026-06-17 17:24:25 +03:00
davida-ps 4a4b547b92 ci(skills): pin clawhub CLI by hash via committed lockfile (#268)
* ci(skills): pin clawhub CLI by hash via committed lockfile

Scorecard flags the skill-release workflow's npm install of the clawhub
CLI (code-scanning alerts #25/#26): version pinning alone carries no
integrity guarantee. Install it with npm ci from a committed
package-lock.json instead, so every package (clawhub + 35 transitive
deps) is verified against its sha512 hash at install time.

The publish-payload patch step now resolves the module from the local
node_modules instead of npm root -g.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skill-release): authenticate pinned clawhub install

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-15 18:12:36 +03:00
davida-ps 6f51e53cdd fix(skill-release): report SkillSpector scans on PRs (#267)
* fix(skill-release): report skillspector scans on PRs

* fix(skill-release): address PR report review comments

* fix(deps): update vite audit chain

* docs(wiki): document skillspector release evidence

* docs(wiki): centralize skillspector release details
2026-06-14 19:28:11 +03:00
github-actions[bot] d8dec965a8 chore: update NVD/GHSA advisories - 34 NVD new, 0 NVD updated (#270)
Automated update from NVD CVE and GHSA advisory feeds.
Keywords: openclaw, nanoclaw, hermes, picoclaw
Poll window: 2026-06-10T08:30:16Z to 2026-06-14T07:32:26.000Z

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-14 13:23:30 +03:00
davida-ps 9fd3059271 fix(traffic): require a traffic-capable PAT for the archive workflow (#265)
* fix(traffic): use a traffic-capable PAT for the archive workflow

The daily Archive GitHub Traffic run has failed since creation: the
TRAFFIC_ARCHIVE_TOKEN secret was never provisioned, so the workflow fell
back to github.token, which GitHub categorically rejects on traffic
endpoints (403 "Resource not accessible by integration").

- Fall back to the existing POLL_NVD_CVES_PAT automation token instead
  of github.token, keeping TRAFFIC_ARCHIVE_TOKEN as the preferred
  override once provisioned.
- Fail fast with an actionable error when no traffic-capable token is
  configured.
- Explain token requirements in the script's 401/403 errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(traffic): require dedicated TRAFFIC_ARCHIVE_TOKEN, drop expired PAT fallback

A live dispatch confirmed POLL_NVD_CVES_PAT is expired (401 Bad
credentials), so falling back to it only trades one daily failure for
another. Require the dedicated secret and fail fast with setup
instructions instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:25:56 +03:00
davida-ps 1b676fd42c fix(skills): scan staged payload with SkillSpector (#264)
* fix(skills): scan staged payload with skillspector

* fix(skills): embed skillspector report in releases

* fix(skills): use body path for release notes
2026-06-10 17:18:54 +03:00
davida-ps 59d54ed778 fix(skills): namespace ClawHub skill slugs (#263)
* fix(release): map ClawHub publish slugs

* fix(release): share skill platform parsing
2026-06-10 16:39:19 +03:00
Burak Bayır d99f324f72 feat(openclaw-traffic-guardian): add social action review scope (#261)
* feat(openclaw-traffic-guardian): add social action review scope

* fix(openclaw-traffic-guardian): cover background repeats

* fix(openclaw-traffic-guardian): address policy review release gates

* docs(openclaw-traffic-guardian): credit policy review contributor

* docs(openclaw-traffic-guardian): inline contributor credit

* docs(openclaw-traffic-guardian): reference policy review spec

* ci(skills): allow unreleased version edits

* ci(skills): use directory name for release tag checks

---------

Co-authored-by: kriptoburak <kriptoburak@users.noreply.github.com>
Co-authored-by: David Abutbul <David.a@prompt.security>
2026-06-10 14:46:52 +03:00
davida-ps c1d1824f86 ci(skills): publish release trust packets + expand skill installer awareness (vercel) (#262)
* ci(skills): publish release trust packets

* ci(skills): simulate beta tag releases

* ci(skills): match release version bump rules

* chore(skills): group agent skills for installer

* chore(skills): make clawtributor global

* chore(skills): bump all skills for trust release

* ci(skills): require npx install docs

* fix(skills): simulate prerelease tag versions

* fix(skills): aggregate trust artifact checksum failures

* fix(frontend): advertise npx skills suite install

* chore(frontend): drop ad hoc homepage copy test

* fix(ci): run skill release tooling tests
2026-06-10 13:22:22 +03:00
github-actions[bot] d7312d7429 chore: update NVD/GHSA advisories - 1 NVD new, 0 NVD updated (#257)
Automated update from NVD CVE and GHSA advisory feeds.
Keywords: openclaw, nanoclaw, hermes, picoclaw
Poll window: 2026-06-03T07:38:12Z to 2026-06-10T08:29:07.000Z

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-10 11:35:17 +03:00
davida-ps cb58e588c5 fix(workflow): filter dispatched codeql runs with jq (#260) 2026-06-10 11:23:30 +03:00
107 changed files with 9736 additions and 7084 deletions
+56
View File
@@ -0,0 +1,56 @@
{
"plugins": [
{
"name": "global-skills",
"source": "./",
"skills": [
"./skills/clawtributor"
]
},
{
"name": "hermes-skills",
"source": "./",
"skills": [
"./skills/hermes-attestation-guardian",
"./skills/hermes-traffic-guardian"
]
},
{
"name": "nano-claw-skills",
"source": "./",
"skills": [
"./skills/clawsec-nanoclaw",
"./skills/nanoclaw-traffic-guardian"
]
},
{
"name": "open-claw-skills",
"source": "./",
"skills": [
"./skills/clawsec-clawhub-checker",
"./skills/clawsec-feed",
"./skills/clawsec-scanner",
"./skills/clawsec-suite",
"./skills/openclaw-audit-watchdog",
"./skills/openclaw-traffic-guardian",
"./skills/soul-guardian"
]
},
{
"name": "pico-claw-skills",
"source": "./",
"skills": [
"./skills/picoclaw-security-guardian",
"./skills/picoclaw-self-pen-testing",
"./skills/picoclaw-traffic-guardian"
]
},
{
"name": "repo-internal-skills",
"source": "./",
"skills": [
"./skills/claw-release"
]
}
]
}
+406
View File
@@ -0,0 +1,406 @@
{
"name": "clawhub-cli-pin",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "clawhub-cli-pin",
"dependencies": {
"clawhub": "0.7.0"
}
},
"node_modules/@ark/schema": {
"version": "0.56.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@ark/schema/-/schema-0.56.0.tgz",
"integrity": "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==",
"dependencies": {
"@ark/util": "0.56.0"
}
},
"node_modules/@ark/util": {
"version": "0.56.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@ark/util/-/util-0.56.0.tgz",
"integrity": "sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA=="
},
"node_modules/@clack/core": {
"version": "0.5.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@clack/core/-/core-0.5.0.tgz",
"integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==",
"dependencies": {
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
}
},
"node_modules/@clack/prompts": {
"version": "0.11.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/@clack/prompts/-/prompts-0.11.0.tgz",
"integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==",
"dependencies": {
"@clack/core": "0.5.0",
"picocolors": "^1.0.0",
"sisteransi": "^1.0.5"
}
},
"node_modules/ansi-regex": {
"version": "6.2.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/arkregex": {
"version": "0.0.5",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/arkregex/-/arkregex-0.0.5.tgz",
"integrity": "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==",
"dependencies": {
"@ark/util": "0.56.0"
}
},
"node_modules/arktype": {
"version": "2.2.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/arktype/-/arktype-2.2.0.tgz",
"integrity": "sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==",
"dependencies": {
"@ark/schema": "0.56.0",
"@ark/util": "0.56.0",
"arkregex": "0.0.5"
}
},
"node_modules/chalk": {
"version": "5.6.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/clawhub": {
"version": "0.7.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/clawhub/-/clawhub-0.7.0.tgz",
"integrity": "sha512-volW6SbX8PawlnRxxCoUTKv5Pi+N3MrBi3hlO5/m9bVaO43UFciEeYti9+01c2U5n/SKhUkw7ASvnleyNmcoSA==",
"dependencies": {
"@clack/prompts": "^0.11.0",
"arktype": "^2.1.29",
"commander": "^14.0.2",
"fflate": "^0.8.2",
"ignore": "^7.0.5",
"json5": "^2.2.3",
"mime": "^4.1.0",
"ora": "^9.0.0",
"p-retry": "^7.1.1",
"semver": "^7.7.3",
"undici": "^7.16.0"
},
"bin": {
"clawdhub": "bin/clawdhub.js",
"clawhub": "bin/clawdhub.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/cli-cursor": {
"version": "5.0.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/cli-cursor/-/cli-cursor-5.0.0.tgz",
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"dependencies": {
"restore-cursor": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-spinners": {
"version": "3.4.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/cli-spinners/-/cli-spinners-3.4.0.tgz",
"integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
"engines": {
"node": ">=18.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/commander": {
"version": "14.0.3",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"engines": {
"node": ">=20"
}
},
"node_modules/fflate": {
"version": "0.8.3",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/fflate/-/fflate-0.8.3.tgz",
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="
},
"node_modules/get-east-asian-width": {
"version": "1.6.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"engines": {
"node": ">= 4"
}
},
"node_modules/is-interactive": {
"version": "2.0.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/is-interactive/-/is-interactive-2.0.0.tgz",
"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-network-error": {
"version": "1.3.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/is-network-error/-/is-network-error-1.3.2.tgz",
"integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-unicode-supported": {
"version": "2.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
"integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/log-symbols": {
"version": "7.0.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/log-symbols/-/log-symbols-7.0.1.tgz",
"integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
"dependencies": {
"is-unicode-supported": "^2.0.0",
"yoctocolors": "^2.1.1"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mime": {
"version": "4.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/mime/-/mime-4.1.0.tgz",
"integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
"funding": [
"https://github.com/sponsors/broofa"
],
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/mimic-function": {
"version": "5.0.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/mimic-function/-/mimic-function-5.0.1.tgz",
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/onetime/-/onetime-7.0.0.tgz",
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dependencies": {
"mimic-function": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora": {
"version": "9.4.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/ora/-/ora-9.4.0.tgz",
"integrity": "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==",
"dependencies": {
"chalk": "^5.6.2",
"cli-cursor": "^5.0.0",
"cli-spinners": "^3.2.0",
"is-interactive": "^2.0.0",
"is-unicode-supported": "^2.1.0",
"log-symbols": "^7.0.1",
"stdin-discarder": "^0.3.2",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-retry": {
"version": "7.1.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/p-retry/-/p-retry-7.1.1.tgz",
"integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==",
"dependencies": {
"is-network-error": "^1.1.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/restore-cursor/-/restore-cursor-5.1.0.tgz",
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dependencies": {
"onetime": "^7.0.0",
"signal-exit": "^4.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": {
"version": "7.8.4",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/semver/-/semver-7.8.4.tgz",
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
},
"node_modules/stdin-discarder": {
"version": "0.3.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/stdin-discarder/-/stdin-discarder-0.3.2.tgz",
"integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width": {
"version": "8.2.1",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/string-width/-/string-width-8.2.1.tgz",
"integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
"dependencies": {
"get-east-asian-width": "^1.5.0",
"strip-ansi": "^7.1.2"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-ansi": {
"version": "7.2.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"dependencies": {
"ansi-regex": "^6.2.2"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/undici": {
"version": "7.28.0",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/undici/-/undici-7.28.0.tgz",
"integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/yoctocolors": {
"version": "2.1.2",
"resolved": "https://prompt-security-443370709039.d.codeartifact.eu-north-1.amazonaws.com/npm/npm-proxy/yoctocolors/-/yoctocolors-2.1.2.tgz",
"integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"name": "clawhub-cli-pin",
"private": true,
"description": "Pins the clawhub CLI used by skill-release.yml; package-lock.json provides the integrity hashes. Bump the version here and regenerate the lockfile with: npm install --package-lock-only",
"dependencies": {
"clawhub": "0.7.0"
}
}
+14 -2
View File
@@ -53,9 +53,21 @@ jobs:
- name: Collect traffic - name: Collect traffic
env: env:
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN || github.token }} # 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 }}
GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_REPOSITORY: ${{ github.repository }}
run: node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}" 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}"
- name: Commit archive - name: Commit archive
run: | run: |
+6 -2
View File
@@ -111,8 +111,12 @@ jobs:
run: node scripts/test-nvd-ghsa-consolidation-workflow.mjs run: node scripts/test-nvd-ghsa-consolidation-workflow.mjs
- name: NVD + GHSA Pipeline Dry Run - name: NVD + GHSA Pipeline Dry Run
run: node scripts/test-nvd-ghsa-pipeline-dry-run.mjs run: node scripts/test-nvd-ghsa-pipeline-dry-run.mjs
- name: Skill Release Workflow Tests - name: Skill Release Tooling Tests
run: node scripts/test-skill-release-workflow.mjs run: |
set -euo pipefail
for test_file in scripts/test-skill-*.mjs; do
node "$test_file"
done
- name: Deploy Pages Advisory Checksums Tests - name: Deploy Pages Advisory Checksums Tests
run: node scripts/test-deploy-pages-checksums.mjs run: node scripts/test-deploy-pages-checksums.mjs
- name: GitHub Traffic Archive Tests - name: GitHub Traffic Archive Tests
+2 -2
View File
@@ -27,7 +27,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml config-file: ./.github/codeql/codeql-config.yml
@@ -38,4 +38,4 @@ jobs:
- name: Build project - name: Build project
run: npm run build run: npm run build
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
+1 -1
View File
@@ -1069,7 +1069,7 @@ jobs:
--event workflow_dispatch \ --event workflow_dispatch \
--limit 50 \ --limit 50 \
--json databaseId,createdAt,headSha \ --json databaseId,createdAt,headSha \
--jq --arg since "$DISPATCHED_AT" --arg sha "$EXPECTED_HEAD_SHA" ' | jq -r --arg since "$DISPATCHED_AT" --arg sha "$EXPECTED_HEAD_SHA" '
map(select(.createdAt >= $since and .headSha == $sha)) map(select(.createdAt >= $since and .headSha == $sha))
| sort_by(.createdAt) | sort_by(.createdAt)
| last | last
+1 -1
View File
@@ -84,6 +84,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional). # Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard # Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning" - name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
with: with:
sarif_file: results.sarif sarif_file: results.sarif
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -58,7 +58,7 @@ ClawSec is a **complete security skill suite for AI agent platforms**. It provid
| clawsec-nanoclaw | NanoClaw | Yes | Yes | Yes | Yes | No | | clawsec-nanoclaw | NanoClaw | Yes | Yes | Yes | Yes | No |
| clawsec-scanner | OpenClaw | Yes | No | Yes | Yes | No | | clawsec-scanner | OpenClaw | Yes | No | Yes | Yes | No |
| clawsec-suite | OpenClaw | Yes | Yes | No | Yes | No | | clawsec-suite | OpenClaw | Yes | Yes | No | Yes | No |
| clawtributor | OpenClaw | Yes | No | No | No | No | | clawtributor | All core platforms | No | 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-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 | | hermes-traffic-guardian | Hermes | No | Planned posture export only | No | No | Spec baseline |
| nanoclaw-traffic-guardian | NanoClaw | No | No | No | No | Spec baseline | | nanoclaw-traffic-guardian | NanoClaw | No | No | No | No | Spec baseline |
+2233 -2712
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
v+PiWmjIkY6zdIyI9xJX0l0aTy0Azp1+LoZR6qaiDZJnXFuSBX4Sw/x5tMdTb0xSbqdDTJOZwwWI8coPVepzBw== K19pfVfv7qB1cqFPFTu69+sKLHIMIrmS7GeK4BZIlHzRvrLfRUuq/KftC8/CIWwvixVlBBm/iZlyfJ5sutoDDw==
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
SCkRaPMF6IYDwZuR7/JJXxpB7A7ebuMvLqK827uWX0yfEJr7l2gyLpxvHsEpWJDzE4gchxd5yqJx5qF/yqNwAg== pmw3QutYARGuNH2evzHY/slVqxsrIGU+JrtS1hr1kOSqo1Md1aVBEA0tsNoQ+SkVjNohwGVk/61CcUxeW6WAAA==
+2 -2
View File
@@ -7,7 +7,7 @@
ClawSec ClawSec
Security skill suite for AI agents (integrity checks, drift detection, advisory feed). Security skill suite for AI agents (integrity checks, drift detection, advisory feed).
Agent install: Agent install:
Available via clawhub: npx clawhub@latest install clawsec-suite Available via npx skills: npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y
OR OR
curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md
--> -->
@@ -141,7 +141,7 @@
ClawSec ClawSec
Security skill suite for AI agents (integrity checks, drift detection, advisory feed). Security skill suite for AI agents (integrity checks, drift detection, advisory feed).
Agent install: Agent install:
Available via clawhub: npx clawhub@latest install clawsec-suite Available via npx skills: npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y
OR OR
curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md curl -sL https://clawsec.prompt.security/releases/latest/download/SKILL.md
</noscript> </noscript>
+573 -1019
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -31,13 +31,13 @@
"@types/node": "^25.8.0", "@types/node": "^25.8.0",
"@typescript-eslint/eslint-plugin": "^8.55.0", "@typescript-eslint/eslint-plugin": "^8.55.0",
"@typescript-eslint/parser": "^8.58.1", "@typescript-eslint/parser": "^8.58.1",
"@vitejs/plugin-react": "^5.1.4", "@vitejs/plugin-react": "^6.0.2",
"eslint": "^9.39.4", "eslint": "^9.39.4",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"fast-check": "^4.7.0", "fast-check": "^4.7.0",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.3.2" "vite": "^8.0.16"
}, },
"overrides": { "overrides": {
"ajv": "6.14.0", "ajv": "6.14.0",
+14 -2
View File
@@ -13,7 +13,7 @@ export const Home: React.FC = () => {
const [currentFileIndex, setCurrentFileIndex] = useState(0); const [currentFileIndex, setCurrentFileIndex] = useState(0);
const [currentPlatformIndex, setCurrentPlatformIndex] = useState(0); const [currentPlatformIndex, setCurrentPlatformIndex] = useState(0);
const curlCommand = `npx clawhub@latest install clawsec-suite`; const curlCommand = `npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y`;
// Rotate file names every 2-3 seconds // Rotate file names every 2-3 seconds
useEffect(() => { useEffect(() => {
@@ -44,7 +44,7 @@ export const Home: React.FC = () => {
}; };
}, []); }, []);
const humanInstruction = `Please install clawsec-suite from clawhubnpx clawhub@latest install clawsec-suite`; const humanInstruction = `Please install clawsec-suite with npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y`;
const handleCopyCurl = () => { const handleCopyCurl = () => {
navigator.clipboard.writeText(curlCommand); navigator.clipboard.writeText(curlCommand);
@@ -285,6 +285,18 @@ export const Home: React.FC = () => {
</div> </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>
</div> </div>
</section> </section>
+8 -1
View File
@@ -321,7 +321,14 @@ const fetchJson = async ({ repo, token, pathname, fetchImpl }) => {
if (!response.ok) { if (!response.ok) {
const body = await response.text().catch(() => ''); const body = await response.text().catch(() => '');
const suffix = body ? ` ${body.slice(0, 500)}` : ''; const suffix = body ? ` ${body.slice(0, 500)}` : '';
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}`); 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}`);
} }
return response.json(); return response.json();
@@ -0,0 +1,359 @@
#!/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);
});
+104
View File
@@ -0,0 +1,104 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "Usage: $0 <target-clawhub-slug>" >&2
}
if [ "$#" -ne 1 ]; then
usage
exit 2
fi
TARGET_SLUG="$1"
SITE="${CLAWHUB_SITE:-https://clawhub.ai}"
REGISTRY="${CLAWHUB_REGISTRY:-$SITE}"
CONFIG_PATH="${CLAWHUB_CONFIG_PATH:-$HOME/.clawhub-ci/config.json}"
if [[ ! "$TARGET_SLUG" =~ ^[a-z0-9-]+$ ]]; then
echo "::error::Invalid ClawHub slug for ownership guard: ${TARGET_SLUG}"
exit 1
fi
if [ ! -f "$CONFIG_PATH" ]; then
echo "::error::ClawHub config not found at ${CONFIG_PATH}. Run clawhub login before ownership guard."
exit 1
fi
TOKEN="$(jq -r '.token // empty' "$CONFIG_PATH")"
if [ -z "$TOKEN" ]; then
echo "::error::ClawHub token missing from ${CONFIG_PATH}. Run clawhub login before ownership guard."
exit 1
fi
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
api_get() {
local path="$1"
local output_path="$2"
local url="${REGISTRY%/}${path}"
local http_status
local curl_status
set +e
http_status="$(
curl --silent --show-error --location --max-time 15 \
--header "Accept: application/json" \
--header "Authorization: Bearer ${TOKEN}" \
--output "$output_path" \
--write-out "%{http_code}" \
"$url"
)"
curl_status=$?
set -e
if [ "$curl_status" -ne 0 ]; then
echo "::error::Failed to call ClawHub API: ${url}"
return 1
fi
printf '%s\n' "$http_status"
}
whoami_json="$TMP_DIR/whoami.json"
whoami_status="$(api_get "/api/v1/whoami" "$whoami_json")"
if [ "$whoami_status" != "200" ]; then
echo "::error::Failed to verify authenticated ClawHub publisher. HTTP ${whoami_status}."
cat "$whoami_json"
exit 1
fi
publisher_handle="$(jq -r '.user.handle // empty' "$whoami_json")"
if [ -z "$publisher_handle" ]; then
echo "::error::Could not determine authenticated ClawHub publisher handle."
cat "$whoami_json"
exit 1
fi
target_json="$TMP_DIR/target.json"
target_status="$(api_get "/api/v1/skills/${TARGET_SLUG}" "$target_json")"
if [ "$target_status" = "404" ]; then
echo "Target ClawHub slug ${TARGET_SLUG} is not currently published; authenticated publisher ${publisher_handle} may create it."
exit 0
fi
if [ "$target_status" != "200" ]; then
echo "::error::Failed to inspect target ClawHub slug ${TARGET_SLUG}. HTTP ${target_status}."
cat "$target_json"
exit 1
fi
target_owner="$(jq -r '.owner.handle // .owner.displayName // empty' "$target_json")"
if [ -z "$target_owner" ]; then
echo "::error::Could not determine owner for existing ClawHub slug ${TARGET_SLUG}."
echo "target owner: ${target_owner:-unknown}"
exit 1
fi
if [ "$target_owner" != "$publisher_handle" ]; then
echo "::error::Resolved ClawHub slug ${TARGET_SLUG} is already owned by ${target_owner}, but the authenticated publisher is ${publisher_handle}. Transfer or alias the registry slug before publishing."
exit 1
fi
echo "ClawHub slug ownership guard passed: ${TARGET_SLUG} owned by authenticated publisher ${publisher_handle}."
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
CLI_PREFIX="${CLAWHUB_CLI_PREFIX:-.github/clawhub-cli}"
CODEARTIFACT_DOMAIN="${CODEARTIFACT_DOMAIN:-prompt-security}"
CODEARTIFACT_DOMAIN_OWNER="${CODEARTIFACT_DOMAIN_OWNER:-443370709039}"
CODEARTIFACT_REPOSITORY="${CODEARTIFACT_REPOSITORY:-npm-proxy}"
AWS_REGION="${AWS_REGION:-${AWS_DEFAULT_REGION:-eu-north-1}}"
if ! command -v aws >/dev/null 2>&1; then
echo "::error::aws CLI is required to authenticate npm against CodeArtifact"
exit 1
fi
if ! aws sts get-caller-identity >/dev/null 2>&1; then
echo "::error::AWS credentials are required before installing the CodeArtifact-pinned clawhub CLI"
exit 1
fi
aws codeartifact login \
--tool npm \
--domain "$CODEARTIFACT_DOMAIN" \
--domain-owner "$CODEARTIFACT_DOMAIN_OWNER" \
--repository "$CODEARTIFACT_REPOSITORY" \
--region "$AWS_REGION"
npm ci --prefix "$CLI_PREFIX"
if [ -n "${GITHUB_PATH:-}" ]; then
workspace="${GITHUB_WORKSPACE:-$(pwd)}"
echo "${workspace}/${CLI_PREFIX}/node_modules/.bin" >> "$GITHUB_PATH"
fi
@@ -0,0 +1,35 @@
import fs from "node:fs";
import path from "node:path";
const workspace = process.env.GITHUB_WORKSPACE || process.cwd();
const npmRoot = path.join(workspace, ".github", "clawhub-cli", "node_modules");
const publishScriptPath = path.join(
npmRoot,
"clawhub",
"dist",
"cli",
"commands",
"publish.js",
);
if (!fs.existsSync(publishScriptPath)) {
throw new Error(`clawhub publish script not found: ${publishScriptPath}`);
}
const original = fs.readFileSync(publishScriptPath, "utf8");
if (original.includes("acceptLicenseTerms: true")) {
console.log(`[patch-clawhub] Already patched: ${publishScriptPath}`);
process.exit(0);
}
const payloadPattern = /changelog,\r?\n(\s*)tags,/;
if (!payloadPattern.test(original)) {
throw new Error(`[patch-clawhub] Could not find expected publish payload pattern in ${publishScriptPath}`);
}
const patched = original.replace(
payloadPattern,
(_, indent) => `changelog,\n${indent}acceptLicenseTerms: true,\n${indent}tags,`,
);
fs.writeFileSync(publishScriptPath, patched, "utf8");
console.log(`[patch-clawhub] Patched: ${publishScriptPath}`);
+79
View File
@@ -0,0 +1,79 @@
#!/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 (EXPLICIT_SLUGS.has(name)) {
return EXPLICIT_SLUGS.get(name);
}
if (name.startsWith("clawsec-")) {
return name;
}
if (PLATFORM_KEYS.some((platform) => name.startsWith(`${platform}-`))) {
return `clawsec-${name}`;
}
const declaredPlatforms = collectDeclaredPlatforms({ platforms });
if (declaredPlatforms.length === 1 && PLATFORM_KEYS.includes(declaredPlatforms[0])) {
return `clawsec-${declaredPlatforms[0]}-${name}`;
}
return `clawsec-${name}`;
}
if (import.meta.url === `file://${process.argv[1]}`) {
const input = process.argv[2];
if (!input || input === "--help" || input === "-h") {
console.log(usage());
process.exit(input ? 0 : 1);
}
try {
console.log(resolveClawHubSlug(loadSkill(input)));
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
+520
View File
@@ -0,0 +1,520 @@
#!/usr/bin/env node
import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
import {
cp,
mkdir,
mkdtemp,
readFile,
rm,
stat,
writeFile,
} from "node:fs/promises";
import { existsSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
const TRUST_ARTIFACTS = [
"skill-card.md",
"permissions.json",
"install.md",
"skillspector-report.md",
];
function usage() {
return [
"Usage: node scripts/ci/simulate_skill_tag_release.mjs <skill-dir> <output-dir> [options]",
"",
"Options:",
" --repository <owner/repo> Source repository used in release metadata",
" --source-ref <ref> Source ref used in npx skills examples",
" --skillspector-bin <path> SkillSpector executable to run",
].join("\n");
}
function parseArgs(argv) {
const positional = [];
const options = {
repository: "prompt-security/clawsec",
sourceRef: "main",
skillspectorBin: "skillspector",
};
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i];
if (token === "--repository") {
options.repository = argv[++i];
} else if (token === "--source-ref") {
options.sourceRef = argv[++i];
} else if (token === "--skillspector-bin") {
options.skillspectorBin = argv[++i];
} else if (token === "--help" || token === "-h") {
console.log(usage());
process.exit(0);
} else if (token.startsWith("--")) {
throw new Error(`Unknown option: ${token}`);
} else {
positional.push(token);
}
}
if (positional.length !== 2) {
throw new Error(usage());
}
return {
skillDir: positional[0],
outputDir: positional[1],
...options,
};
}
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
encoding: "utf8",
...options,
});
if (result.status !== 0) {
throw new Error(
[
`Command failed: ${command} ${args.join(" ")}`,
result.stdout ? `stdout:\n${result.stdout}` : "",
result.stderr ? `stderr:\n${result.stderr}` : "",
].filter(Boolean).join("\n"),
);
}
return result.stdout;
}
function runAllowFailure(command, args, options = {}) {
return spawnSync(command, args, {
encoding: "utf8",
...options,
});
}
function nextSimulatedReleaseVersion(version) {
const versionMatch = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9]+))?$/);
if (!versionMatch) {
throw new Error(`Cannot derive simulated release version from unsupported version: ${version}`);
}
const [, major, minor, patch, prerelease] = versionMatch;
if (!prerelease) {
return `${major}.${minor}.${Number(patch) + 1}`;
}
const prereleaseMatch = prerelease.match(/^(.*?)(\d+)$/);
if (prereleaseMatch) {
const [, label, number] = prereleaseMatch;
return `${major}.${minor}.${patch}-${label}${Number(number) + 1}`;
}
return `${major}.${minor}.${patch}-${prerelease}1`;
}
function normalizeReleasePath(rawPath) {
let releasePath = rawPath.replaceAll("\\", "/");
while (releasePath.startsWith("./")) {
releasePath = releasePath.slice(2);
}
while (releasePath.includes("//")) {
releasePath = releasePath.replaceAll("//", "/");
}
if (
releasePath === "" ||
releasePath.startsWith("/") ||
/^[A-Za-z]:/.test(releasePath) ||
releasePath === ".." ||
releasePath.startsWith("../") ||
releasePath.endsWith("/..") ||
releasePath.includes("/../")
) {
throw new Error(`Unsafe release path: ${rawPath}`);
}
return releasePath;
}
function isTestReleasePath(releasePath) {
const lower = releasePath.toLowerCase();
return lower === "test" ||
lower === "tests" ||
lower.startsWith("test/") ||
lower.startsWith("tests/") ||
lower.includes("/test/") ||
lower.includes("/tests/");
}
async function sha256File(filePath) {
const buffer = await readFile(filePath);
return createHash("sha256").update(buffer).digest("hex");
}
async function fileSize(filePath) {
return (await stat(filePath)).size;
}
async function checksumEntry(filePath, releasePath) {
return {
sha256: await sha256File(filePath),
size: await fileSize(filePath),
path: releasePath,
};
}
function replaceSkillMarkdownVersion(markdown, version) {
if (!markdown.startsWith("---\n")) {
throw new Error("SKILL.md is missing YAML frontmatter");
}
const end = markdown.indexOf("\n---", 4);
if (end === -1) {
throw new Error("SKILL.md frontmatter is not closed");
}
const frontmatter = markdown.slice(0, end);
if (!/^version:\s*.+$/m.test(frontmatter)) {
throw new Error("SKILL.md frontmatter is missing a version field");
}
return markdown.replace(/^version:\s*.+$/m, `version: ${version}`);
}
async function addSimulatedChangelogEntry(skillDir, version) {
const changelogPath = path.join(skillDir, "CHANGELOG.md");
if (!existsSync(changelogPath)) {
return;
}
const today = new Date().toISOString().slice(0, 10);
const original = await readFile(changelogPath, "utf8");
if (original.includes(`## [${version}] -`)) {
return;
}
const entry = [
`## [${version}] - ${today}`,
"",
"- Simulated prerelease build for release-pipeline validation.",
"",
"---",
"",
].join("\n");
await writeFile(changelogPath, `${entry}${original}`);
}
async function writeJson(filePath, value) {
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
}
async function signFileBase64({ keyPath, inputPath, outputPath, tempRoot }) {
const sigBin = path.join(tempRoot, `${path.basename(outputPath)}.bin`);
run("openssl", ["pkeyutl", "-sign", "-rawin", "-inkey", keyPath, "-in", inputPath, "-out", sigBin]);
run("openssl", ["base64", "-A", "-in", sigBin, "-out", outputPath]);
await rm(sigBin, { force: true });
}
async function verifyFileBase64Signature({ publicKeyPath, inputPath, signaturePath, tempRoot }) {
const sigBin = path.join(tempRoot, `${path.basename(signaturePath)}.verify.bin`);
run("openssl", ["base64", "-d", "-A", "-in", signaturePath, "-out", sigBin]);
run("openssl", [
"pkeyutl",
"-verify",
"-rawin",
"-pubin",
"-inkey",
publicKeyPath,
"-sigfile",
sigBin,
"-in",
inputPath,
]);
await rm(sigBin, { force: true });
}
async function createSigningKeyPair(tempRoot) {
const keyDir = await mkdtemp(path.join(tempRoot, "signing-"));
const privateKeyPath = path.join(keyDir, "private.pem");
const publicKeyPath = path.join(keyDir, "public.pem");
run("openssl", ["genpkey", "-algorithm", "ED25519", "-out", privateKeyPath]);
run("openssl", ["pkey", "-in", privateKeyPath, "-pubout", "-out", publicKeyPath]);
return { privateKeyPath, publicKeyPath };
}
async function signAdvisoryArtifacts(skillDir, tempRoot) {
const advisoryDir = path.join(skillDir, "advisories");
const feedPath = path.join(advisoryDir, "feed.json");
if (!existsSync(feedPath)) {
return;
}
const { privateKeyPath, publicKeyPath } = await createSigningKeyPair(tempRoot);
const feedSignaturePath = path.join(advisoryDir, "feed.json.sig");
const checksumsPath = path.join(advisoryDir, "checksums.json");
const checksumsSignaturePath = path.join(advisoryDir, "checksums.json.sig");
const publicKeyOutputPath = path.join(advisoryDir, "feed-signing-public.pem");
await signFileBase64({
keyPath: privateKeyPath,
inputPath: feedPath,
outputPath: feedSignaturePath,
tempRoot,
});
await verifyFileBase64Signature({
publicKeyPath,
inputPath: feedPath,
signaturePath: feedSignaturePath,
tempRoot,
});
await writeJson(checksumsPath, {
schema_version: "1",
algorithm: "sha256",
version: "simulation",
generated_at: new Date().toISOString(),
files: {
"advisories/feed.json": await checksumEntry(feedPath, "advisories/feed.json"),
"advisories/feed.json.sig": await checksumEntry(feedSignaturePath, "advisories/feed.json.sig"),
},
});
await signFileBase64({
keyPath: privateKeyPath,
inputPath: checksumsPath,
outputPath: checksumsSignaturePath,
tempRoot,
});
await verifyFileBase64Signature({
publicKeyPath,
inputPath: checksumsPath,
signaturePath: checksumsSignaturePath,
tempRoot,
});
await cp(publicKeyPath, publicKeyOutputPath);
}
async function addReleaseAssetChecksum({ releaseAssetsDir, manifest, asset }) {
const filePath = path.join(releaseAssetsDir, asset);
if (!existsSync(filePath) || (await fileSize(filePath)) === 0) {
throw new Error(`Required release trust artifact is missing or empty: ${filePath}`);
}
manifest.files[asset] = await checksumEntry(filePath, asset);
}
async function stageSbomFiles({ skillDir, innerDir, sbomFiles }) {
for (const entry of sbomFiles) {
const releasePath = normalizeReleasePath(entry.path);
if (isTestReleasePath(releasePath)) {
continue;
}
const fullPath = path.join(skillDir, releasePath);
if (!existsSync(fullPath)) {
throw new Error(`SBOM references missing file: ${releasePath}`);
}
const destination = path.join(innerDir, releasePath);
await mkdir(path.dirname(destination), { recursive: true });
await cp(fullPath, destination);
}
}
async function buildFilesManifest({ skillDir, skillJsonPath, sbomFiles }) {
const files = {};
for (const entry of sbomFiles) {
const releasePath = normalizeReleasePath(entry.path);
if (isTestReleasePath(releasePath)) {
continue;
}
const fullPath = path.join(skillDir, releasePath);
if (existsSync(fullPath)) {
files[releasePath] = await checksumEntry(fullPath, releasePath);
}
}
files["skill.json"] = {
sha256: await sha256File(skillJsonPath),
size: await fileSize(skillJsonPath),
};
return files;
}
async function runSkillSpector({ skillspectorBin, skillDir, reportPath }) {
const result = runAllowFailure(skillspectorBin, [
"scan",
skillDir,
"--no-llm",
"--format",
"markdown",
"--output",
reportPath,
]);
if (!existsSync(reportPath) || (await fileSize(reportPath)) === 0) {
throw new Error(
[
"SkillSpector did not produce a report.",
result.stdout ? `stdout:\n${result.stdout}` : "",
result.stderr ? `stderr:\n${result.stderr}` : "",
].filter(Boolean).join("\n"),
);
}
if (result.status !== 0) {
console.warn(`SkillSpector returned exit code ${result.status}; report is included for review.`);
}
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const sourceSkillDir = path.resolve(args.skillDir);
const outputDir = path.resolve(args.outputDir);
const releaseAssetsDir = path.join(outputDir, "release-assets");
const tempRoot = await mkdtemp(path.join(tmpdir(), "clawsec-release-sim-"));
try {
const skillName = path.basename(sourceSkillDir);
const tempSkillDir = path.join(tempRoot, skillName);
await cp(sourceSkillDir, tempSkillDir, { recursive: true });
const skillJsonPath = path.join(tempSkillDir, "skill.json");
const skillMdPath = path.join(tempSkillDir, "SKILL.md");
const skill = JSON.parse(await readFile(skillJsonPath, "utf8"));
const originalVersion = skill.version;
const simulatedVersion = nextSimulatedReleaseVersion(originalVersion);
const tag = `${skillName}-v${simulatedVersion}`;
const zipName = `${tag}.zip`;
skill.version = simulatedVersion;
await writeJson(skillJsonPath, skill);
await writeFile(
skillMdPath,
replaceSkillMarkdownVersion(await readFile(skillMdPath, "utf8"), simulatedVersion),
);
await addSimulatedChangelogEntry(tempSkillDir, simulatedVersion);
await signAdvisoryArtifacts(tempSkillDir, tempRoot);
if (!skill.sbom || !Array.isArray(skill.sbom.files)) {
throw new Error(`skill.json missing required release field: sbom.files`);
}
await mkdir(releaseAssetsDir, { recursive: true });
const stagingDir = await mkdtemp(path.join(tempRoot, "staging-"));
const innerDir = path.join(stagingDir, skillName);
await mkdir(innerDir, { recursive: true });
await stageSbomFiles({
skillDir: tempSkillDir,
innerDir,
sbomFiles: skill.sbom.files,
});
await cp(skillJsonPath, path.join(innerDir, "skill.json"));
run("python3", ["scripts/ci/verify_skill_release_import_closure.py", innerDir], {
cwd: process.cwd(),
});
run("zip", ["-qr", path.join(releaseAssetsDir, zipName), "."], {
cwd: stagingDir,
});
const zipContents = run("unzip", ["-Z1", path.join(releaseAssetsDir, zipName)]);
if (zipContents.split("\n").some((entry) => /(^|\/)(test|tests)\//i.test(entry))) {
throw new Error(`Simulated release archive contains test-only files: ${zipName}`);
}
const manifest = {
skill: skillName,
version: simulatedVersion,
generated_at: new Date().toISOString(),
repository: args.repository,
tag,
archive: {
filename: zipName,
sha256: await sha256File(path.join(releaseAssetsDir, zipName)),
size: await fileSize(path.join(releaseAssetsDir, zipName)),
url: `https://github.com/${args.repository}/releases/download/${tag}/${zipName}`,
},
files: await buildFilesManifest({
skillDir: tempSkillDir,
skillJsonPath,
sbomFiles: skill.sbom.files,
}),
};
await writeJson(path.join(releaseAssetsDir, "checksums.json"), manifest);
run(process.execPath, [
"scripts/ci/generate_skill_release_trust_packet.mjs",
tempSkillDir,
releaseAssetsDir,
"--repository",
args.repository,
"--tag",
tag,
"--source-ref",
args.sourceRef,
]);
await runSkillSpector({
skillspectorBin: args.skillspectorBin,
skillDir: innerDir,
reportPath: path.join(releaseAssetsDir, "skillspector-report.md"),
});
for (const artifact of TRUST_ARTIFACTS) {
await addReleaseAssetChecksum({ releaseAssetsDir, manifest, asset: artifact });
}
await writeJson(path.join(releaseAssetsDir, "checksums.json"), manifest);
await cp(skillJsonPath, path.join(releaseAssetsDir, "skill.json"));
await cp(skillMdPath, path.join(releaseAssetsDir, "SKILL.md"));
if (existsSync(path.join(tempSkillDir, "README.md"))) {
await cp(path.join(tempSkillDir, "README.md"), path.join(releaseAssetsDir, "README.md"));
}
const { privateKeyPath, publicKeyPath } = await createSigningKeyPair(tempRoot);
await signFileBase64({
keyPath: privateKeyPath,
inputPath: path.join(releaseAssetsDir, "checksums.json"),
outputPath: path.join(releaseAssetsDir, "checksums.sig"),
tempRoot,
});
await verifyFileBase64Signature({
publicKeyPath,
inputPath: path.join(releaseAssetsDir, "checksums.json"),
signaturePath: path.join(releaseAssetsDir, "checksums.sig"),
tempRoot,
});
await cp(publicKeyPath, path.join(releaseAssetsDir, "signing-public.pem"));
await writeJson(path.join(outputDir, "simulation-summary.json"), {
skill: skillName,
original_version: originalVersion,
simulated_version: simulatedVersion,
tag,
release_assets: path.relative(outputDir, releaseAssetsDir),
archive: `release-assets/${zipName}`,
});
console.log(`Simulated tag release build for ${skillName}: ${tag}`);
} finally {
await rm(tempRoot, { recursive: true, force: true });
}
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});
+52
View File
@@ -0,0 +1,52 @@
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;
}
+268
View File
@@ -0,0 +1,268 @@
#!/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/clawsec-feed/advisories/feed.json",
":(exclude)skills/clawsec-feed/advisories/feed.json.sig",
":(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);
});
+36 -1
View File
@@ -76,6 +76,40 @@ test('fetchGitHubTraffic requests the daily GitHub traffic endpoints with auth',
assert.deepEqual(snapshot.clones.clones, responses[`/repos/${TEST_REPOSITORY}/traffic/clones?per=day`].clones); 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', () => { test('mergeTrafficArchive upserts daily views and clones without double-counting overlapping windows', () => {
const archive = mergeTrafficArchive( const archive = mergeTrafficArchive(
{ {
@@ -232,7 +266,8 @@ test('traffic archive workflow uses a daily schedule and a dedicated archive bra
assert.match(workflow, /cron:\s+'17 3 \* \* \*'/); assert.match(workflow, /cron:\s+'17 3 \* \* \*'/);
assert.match(workflow, /TRAFFIC_ARCHIVE_BRANCH:\s+traffic-archive/); assert.match(workflow, /TRAFFIC_ARCHIVE_BRANCH:\s+traffic-archive/);
assert.match(workflow, /TRAFFIC_ARCHIVE_TOKEN/); 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, /node scripts\/archive-github-traffic\.mjs/); assert.match(workflow, /node scripts\/archive-github-traffic\.mjs/);
assert.match(workflow, /git add traffic\/archive\.json traffic\/summary\.json/); assert.match(workflow, /git add traffic\/archive\.json traffic\/summary\.json/);
assert.match(workflow, /git rm --ignore-unmatch traffic\/README\.md/); assert.match(workflow, /git rm --ignore-unmatch traffic\/README\.md/);
@@ -47,6 +47,16 @@ assert.match(
/git add "\$FEED_PATH" "\$FEED_SIG_PATH" "\$GHSA_FEED_PATH" "\$GHSA_FEED_SIG_PATH" "\$SKILL_FEED_PATH" "\$SKILL_FEED_SIG_PATH"/, /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', '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( assert.match(
ciWorkflow, ciWorkflow,
/name: NVD \+ GHSA Pipeline Dry Run[\s\S]*node scripts\/test-nvd-ghsa-pipeline-dry-run\.mjs/, /name: NVD \+ GHSA Pipeline Dry Run[\s\S]*node scripts\/test-nvd-ghsa-pipeline-dry-run\.mjs/,
+45
View File
@@ -0,0 +1,45 @@
import assert from "node:assert/strict";
import { resolveClawHubSlug } from "./ci/resolve_clawhub_slug.mjs";
import { collectDeclaredPlatforms, installAgentForSkill } from "./ci/skill_platforms.mjs";
const cases = [
["openclaw-traffic-guardian", ["openclaw"], "clawsec-openclaw-traffic-guardian"],
["openclaw-audit-watchdog", ["openclaw"], "clawsec-openclaw-audit-watchdog"],
["soul-guardian", ["openclaw"], "clawsec-openclaw-soul-guardian"],
["hermes-attestation-guardian", ["hermes"], "clawsec-hermes-attestation-guardian"],
["hermes-traffic-guardian", ["hermes"], "clawsec-hermes-traffic-guardian"],
["nanoclaw-traffic-guardian", ["nanoclaw"], "clawsec-nanoclaw-traffic-guardian"],
["picoclaw-security-guardian", ["picoclaw"], "clawsec-picoclaw-security-guardian"],
["picoclaw-self-pen-testing", ["picoclaw"], "clawsec-picoclaw-self-pen-testing"],
["picoclaw-traffic-guardian", ["picoclaw"], "clawsec-picoclaw-traffic-guardian"],
["clawtributor", ["openclaw", "nanoclaw", "hermes", "picoclaw"], "clawsec-clawtributor"],
["clawsec-feed", ["openclaw"], "clawsec-feed"],
["clawsec-suite", ["openclaw"], "clawsec-suite"],
];
for (const [name, platforms, expected] of cases) {
assert.equal(resolveClawHubSlug({ name, platforms }), expected, `${name} should map to ${expected}`);
assert.equal(resolveClawHubSlug({ name }), expected, `${name} should map to ${expected} without metadata`);
}
assert.throws(
() => resolveClawHubSlug({ name: "../openclaw-traffic-guardian", platforms: ["openclaw"] }),
/Invalid skill name/,
"unsafe skill names must be rejected",
);
assert.deepEqual(
collectDeclaredPlatforms({
platform: "openclaw",
platforms: ["hermes", "openclaw", ""],
picoclaw: { requires: {} },
}),
["openclaw", "hermes", "picoclaw"],
"declared platform parsing should combine legacy fields, arrays, and platform metadata keys",
);
assert.equal(
installAgentForSkill({ platform: "hermes" }, new Set(["codex", "hermes-agent", "openclaw"])),
"hermes-agent",
"install agent selection should reuse platform aliases",
);
+137
View File
@@ -0,0 +1,137 @@
import assert from "node:assert/strict";
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
const validator = "scripts/ci/validate_skill_install_docs.mjs";
const workflow = await readFile(".github/workflows/skill-release.yml", "utf8");
const tempRoot = await mkdtemp(path.join(tmpdir(), "clawsec-install-docs-"));
const agentTypesPath = path.join(tempRoot, "vercel-types.ts");
function runValidator(args) {
return spawnSync(
process.execPath,
[validator, "--root", tempRoot, "--agent-types-file", agentTypesPath, ...args],
{
encoding: "utf8",
},
);
}
async function writeSkill({ name, metadata, readme, skillMd }) {
const skillDir = path.join(tempRoot, "skills", name);
await mkdir(skillDir, { recursive: true });
await writeFile(
path.join(skillDir, "skill.json"),
JSON.stringify(
{
name,
version: "1.0.0",
description: `${name} test skill`,
license: "AGPL-3.0-or-later",
...metadata,
},
null,
2,
),
);
await writeFile(path.join(skillDir, "README.md"), readme);
await writeFile(path.join(skillDir, "SKILL.md"), skillMd);
}
try {
await writeFile(
agentTypesPath,
"export type AgentType = | 'codex' | 'hermes-agent' | 'openclaw' | 'universal';\n",
);
await writeSkill({
name: "hermes-example",
metadata: { hermes: { category: "security" } },
readme: "# Hermes Example\n\n## Installation\n\nMissing the Skills CLI command.\n",
skillMd: "---\nname: hermes-example\nversion: 1.0.0\n---\n\n## Installation\n\nMissing the Skills CLI command.\n",
});
const missingHermes = runValidator(["--skills", "skills/hermes-example"]);
assert.equal(missingHermes.status, 1, "missing Hermes install docs must fail validation");
assert.match(
missingHermes.stderr,
/npx skills add prompt-security\/clawsec --skill hermes-example -a hermes-agent -y/,
"Hermes skills must require the hermes-agent installer target",
);
await writeSkill({
name: "hermes-example",
metadata: { hermes: { category: "security" } },
readme:
"# Hermes Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill hermes-example -a hermes-agent -y\n```\n",
skillMd:
"---\nname: hermes-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill hermes-example -a hermes-agent -y\n```\n",
});
const validHermes = runValidator(["--skills", "skills/hermes-example"]);
assert.equal(
validHermes.status,
0,
`valid Hermes install docs should pass\nstdout:\n${validHermes.stdout}\nstderr:\n${validHermes.stderr}`,
);
await writeSkill({
name: "codex-example",
metadata: { platform: "codex" },
readme:
"# Codex Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill codex-example -a openclaw -y\n```\n",
skillMd:
"---\nname: codex-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill codex-example -a openclaw -y\n```\n",
});
const wrongExactTarget = runValidator(["--skills", "skills/codex-example"]);
assert.equal(wrongExactTarget.status, 1, "exact AgentType matches must use their matched target");
assert.match(
wrongExactTarget.stderr,
/npx skills add prompt-security\/clawsec --skill codex-example -a codex -y/,
"Exact AgentType matches must not fall back to openclaw",
);
await writeSkill({
name: "nanoclaw-example",
metadata: { platform: "nanoclaw", nanoclaw: { category: "security" } },
readme:
"# NanoClaw Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a hermes-agent -y\n```\n",
skillMd:
"---\nname: nanoclaw-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a hermes-agent -y\n```\n",
});
const wrongNanoTarget = runValidator(["--skills", "skills/nanoclaw-example"]);
assert.equal(wrongNanoTarget.status, 1, "NanoClaw docs must fail when they use the Hermes target");
assert.match(
wrongNanoTarget.stderr,
/npx skills add prompt-security\/clawsec --skill nanoclaw-example -a openclaw -y/,
"NanoClaw skills must install through the openclaw target",
);
await writeSkill({
name: "nanoclaw-example",
metadata: { platform: "nanoclaw", nanoclaw: { category: "security" } },
readme:
"# NanoClaw Example\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a openclaw -y\n```\n",
skillMd:
"---\nname: nanoclaw-example\nversion: 1.0.0\n---\n\n## Vercel Skills Installation\n\n```bash\nnpx skills add prompt-security/clawsec --skill nanoclaw-example -a openclaw -y\n```\n",
});
const validNano = runValidator(["--skills", "skills/nanoclaw-example"]);
assert.equal(
validNano.status,
0,
`valid NanoClaw install docs should pass\nstdout:\n${validNano.stdout}\nstderr:\n${validNano.stderr}`,
);
assert.match(
workflow,
/Validate npx skills install docs/,
"Skill release workflow must run the install-doc validator",
);
} finally {
await rm(tempRoot, { recursive: true, force: true });
}
+467 -5
View File
@@ -2,7 +2,17 @@ import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises'; import { readFile } from 'node:fs/promises';
const workflowPath = new URL('../.github/workflows/skill-release.yml', import.meta.url); const workflowPath = new URL('../.github/workflows/skill-release.yml', import.meta.url);
const ciWorkflowPath = new URL('../.github/workflows/ci.yml', import.meta.url);
const validateSkillInstallDocsPath = new URL('./ci/validate_skill_install_docs.mjs', import.meta.url);
const installClawhubCliPath = new URL('./ci/install_clawhub_cli.sh', import.meta.url);
const patchClawhubPayloadPath = new URL('./ci/patch_clawhub_publish_payload.mjs', import.meta.url);
const guardClawhubSlugOwnerPath = new URL('./ci/guard_clawhub_slug_owner.sh', import.meta.url);
const workflow = await readFile(workflowPath, 'utf8'); const workflow = await readFile(workflowPath, 'utf8');
const ciWorkflow = await readFile(ciWorkflowPath, 'utf8');
const validateSkillInstallDocs = await readFile(validateSkillInstallDocsPath, 'utf8');
const installClawhubCli = await readFile(installClawhubCliPath, 'utf8');
const patchClawhubPayload = await readFile(patchClawhubPayloadPath, 'utf8');
const guardClawhubSlugOwner = await readFile(guardClawhubSlugOwnerPath, 'utf8');
assert.match( assert.match(
workflow, workflow,
@@ -10,20 +20,472 @@ assert.match(
'Skill release workflow must run when any skill package file changes', 'Skill release workflow must run when any skill package file changes',
); );
for (const generatedFeedPath of [
'skills/clawsec-feed/advisories/feed.json',
'skills/clawsec-feed/advisories/feed.json.sig',
]) {
assert.ok(
workflow.includes(` - '!${generatedFeedPath}'`),
`Skill release workflow must not run for generated advisory mirror-only changes to ${generatedFeedPath}`,
);
}
assert.match( assert.match(
workflow, workflow,
/git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/, /pull_request:[\s\S]*paths:[\s\S]*- '\.github\/workflows\/skill-release\.yml'[\s\S]*- 'scripts\/ci\/\*\*'/,
'Skill release validation must ignore test-only skill changes while inspecting release-relevant skill files', '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\/clawsec-feed\/advisories\/feed\.json'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json\.sig'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/,
'Skill release validation must ignore generated clawsec-feed advisory mirror and test-only changes while inspecting release-relevant skill files',
);
for (const generatedFeedPath of [
':(exclude)skills/clawsec-feed/advisories/feed.json',
':(exclude)skills/clawsec-feed/advisories/feed.json.sig',
]) {
assert.ok(
validateSkillInstallDocs.includes(`"${generatedFeedPath}"`),
`Install-doc validation changed-skill detection must ignore generated advisory mirror-only changes to ${generatedFeedPath}`,
);
}
assert.ok(
workflow.includes('name = tolower($NF)')
&& workflow.includes('name ~ /^(test|spec)[_-]/')
&& workflow.includes('name ~ /\\.(test|spec)\\./'),
'Skill release validation must filter test-named skill files such as scripts/test_*.py before selecting dry-run skill directories',
); );
assert.doesNotMatch( assert.doesNotMatch(
workflow, workflow,
/No version bump detected for \$\{skill_dir\}; skipping\./, /No version bump detected for \$\{skill_dir\}; skipping\./,
'Changed skill directories without a version bump must fail validation instead of being skipped', 'Changed skill directories without a version bump must not be skipped without release-tag validation',
); );
assert.match( assert.match(
workflow, workflow,
/::error file=\$\{skill_dir\}::Changed skill package has no version bump\./, /skill_release_name="\$\(basename "\$\{skill_dir\}"\)"/,
'Skill release validation must emit an explicit missing-version-bump error', '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.doesNotMatch(
workflow,
/"### SkillSpector Security Report"/,
'GitHub release notes must not add a duplicate SkillSpector heading before the generated report',
);
assert.match(
workflow,
/readFileSync\("release-assets\/skillspector-report\.md", "utf8"\)[\s\S]*report,[\s\S]*\[skillspector-report\.md\]\(https:\/\/github\.com\/\$\{process\.env\.REPO\}\/releases\/download\/\$\{process\.env\.TAG\}\/skillspector-report\.md\)/,
'GitHub release notes must embed the generated SkillSpector report and include a direct 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.match(
workflow,
/Run release dry-run for changed skills[\s\S]*git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json\.sig'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/,
'PR dry-run SkillSpector scan must run when any release-relevant skill package file changes except generated advisory mirror files',
);
assert.ok(
workflow.includes('local name="${lower##*/}"')
&& workflow.includes('"$name" == test_*')
&& workflow.includes('"$name" == *.test.*')
&& workflow.includes('(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\\.(test|spec)\\.'),
'Skill release archives must exclude test directories and test-named files from staged release payloads',
);
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,
/Upload SkillSpector PR reports[\s\S]*actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7\.0\.1[\s\S]*name: skillspector-pr-reports/,
'PR dry-run must upload generated SkillSpector reports as workflow artifacts',
);
assert.match(
workflow,
/comment-skillspector-report:[\s\S]*needs: release[\s\S]*issues: write[\s\S]*actions\/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8\.0\.1/,
'Skill release workflow must download generated SkillSpector reports in a separate PR comment job with comment permissions',
);
const commentJob = workflow.match(/[ ]{2}comment-skillspector-report:[\s\S]*?\n[ ]{2}[a-z][^:\n]*:/)?.[0] || "";
assert.doesNotMatch(
commentJob,
/pull-requests: write/,
'SkillSpector PR comment publishing should not request redundant pull-requests write permissions',
);
assert.match(
workflow,
/comment-skillspector-report:[\s\S]*if: always\(\) && github\.event_name == 'pull_request' && needs\.release\.result != 'cancelled'[\s\S]*Download SkillSpector reports[\s\S]*continue-on-error: true/,
'SkillSpector PR comments must still run when the release dry-run produced reports but the release job failed later',
);
assert.match(
workflow,
/Comment SkillSpector reports[\s\S]*continue-on-error: true[\s\S]*actions\/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9\.0\.0/,
'SkillSpector PR comment publishing must not fail the release dry-run check',
);
assert.match(
workflow,
/function sanitizeReportForComment\(report\)[\s\S]*code block omitted from PR comment[\s\S]*inline snippet omitted[\s\S]*redacted-email[\s\S]*redacted-token/,
'SkillSpector PR comments must sanitize raw report content before posting to the PR',
);
assert.match(
workflow,
/const sanitizedReport = sanitizeReportForComment\(report\);[\s\S]*`\$\{marker\}\\n\$\{sanitizedReport\}/,
'SkillSpector PR comments must use the sanitized report body, not the raw artifact text',
);
assert.doesNotMatch(
workflow,
/`\$\{marker\}\\n\$\{report\.trimEnd\(\)\}/,
'SkillSpector PR comments must not post report.trimEnd() verbatim',
);
assert.match(
workflow,
/clawsec-skillspector-report:\$\{tag\}[\s\S]*github\.rest\.issues\.updateComment[\s\S]*github\.rest\.issues\.createComment/,
'SkillSpector PR comments must use stable per-skill markers and update existing comments before creating new ones',
);
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.match(
workflow,
/clawhub publish "\$SKILL_PATH"[\s\S]*--slug "\$CLAWHUB_SLUG"/,
'ClawHub publish must use the resolved ClawHub slug',
);
assert.equal(
workflow.match(/bash scripts\/ci\/install_clawhub_cli\.sh/g)?.length,
2,
'ClawHub publish and republish jobs must share the same pinned CLI installer',
);
assert.equal(
workflow.match(/node scripts\/ci\/patch_clawhub_publish_payload\.mjs/g)?.length,
2,
'ClawHub publish and republish jobs must share the same payload patch helper',
);
assert.equal(
workflow.match(/bash scripts\/ci\/guard_clawhub_slug_owner\.sh/g)?.length,
2,
'ClawHub publish and republish jobs must guard mapped slug ownership before publishing',
);
assert.doesNotMatch(
workflow,
/npm ci --prefix \.github\/clawhub-cli/,
'ClawHub CLI installation must not be duplicated inline in the workflow',
);
assert.doesNotMatch(
workflow,
/node <<'NODE'[\s\S]*acceptLicenseTerms: true/,
'ClawHub payload patching must not be duplicated inline in the workflow',
);
for (const secret of ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN']) {
assert.match(
workflow,
new RegExp(`${secret}: \\$\\{\\{ secrets\\.${secret} \\}\\}`),
`ClawHub jobs must expose ${secret} for CodeArtifact npm authentication`,
);
}
assert.match(
installClawhubCli,
/aws codeartifact login[\s\S]*--domain "\$CODEARTIFACT_DOMAIN"[\s\S]*--domain-owner "\$CODEARTIFACT_DOMAIN_OWNER"[\s\S]*--repository "\$CODEARTIFACT_REPOSITORY"[\s\S]*--region "\$AWS_REGION"/,
'ClawHub CLI installer must authenticate npm against CodeArtifact before npm ci',
);
assert.match(
installClawhubCli,
/npm ci --prefix "\$CLI_PREFIX"/,
'ClawHub CLI installer must install from the committed lockfile prefix',
);
assert.match(
installClawhubCli,
/"\$\{workspace\}\/\$\{CLI_PREFIX\}\/node_modules\/\.bin" >> "\$GITHUB_PATH"/,
'ClawHub CLI installer must expose the pinned clawhub binary on GITHUB_PATH',
);
assert.match(
patchClawhubPayload,
/const payloadPattern = \/changelog,\\r\?\\n\(\\s\*\)tags,\/;/,
'ClawHub payload patch helper must target the expected publish payload shape',
);
assert.match(
patchClawhubPayload,
/acceptLicenseTerms: true/,
'ClawHub payload patch helper must preserve the acceptLicenseTerms workaround',
);
assert.match(
patchClawhubPayload,
/Already patched/,
'ClawHub payload patch helper must stay idempotent when the pinned CLI already includes acceptLicenseTerms',
);
assert.match(
guardClawhubSlugOwner,
/api_get "\/api\/v1\/whoami" "\$whoami_json"/,
'ClawHub slug ownership guard must verify the authenticated publisher through the ClawHub API',
);
assert.match(
guardClawhubSlugOwner,
/api_get "\/api\/v1\/skills\/\$\{TARGET_SLUG\}" "\$target_json"/,
'ClawHub slug ownership guard must inspect the resolved publish slug through the ClawHub API',
);
assert.match(
guardClawhubSlugOwner,
/\[ "\$target_status" = "404" \]/,
'ClawHub slug ownership guard must treat HTTP 404 as the structured unpublished-slug signal',
);
assert.match(
guardClawhubSlugOwner,
/\[ "\$target_owner" != "\$publisher_handle" \]/,
'ClawHub slug ownership guard must reject slugs owned by a different authenticated registry publisher',
);
assert.doesNotMatch(
guardClawhubSlugOwner,
/SOURCE_SLUG|source_owner|grep -Eqi[\s\S]*Skill not found/,
'ClawHub slug ownership guard must not inspect raw source names or depend on stderr wording',
);
assert.match(
workflow,
/SITE=\$\{CLAWHUB_SITE:-https:\/\/clawhub\.ai\}[\s\S]*REGISTRY=\$\{CLAWHUB_REGISTRY:-\$SITE\}[\s\S]*export CLAWHUB_CONFIG_PATH="\$HOME\/\.clawhub-ci\/config\.json"[\s\S]*export CLAWHUB_SITE="\$SITE"[\s\S]*export CLAWHUB_REGISTRY="\$REGISTRY"[\s\S]*bash scripts\/ci\/guard_clawhub_slug_owner\.sh[\s\S]*\$\{\{ needs\.release-tag\.outputs\.clawhub_slug \}\}/,
'ClawHub publish job must guard the resolved publish slug with the authenticated ClawHub config path',
);
assert.match(
workflow,
/SITE=\$\{CLAWHUB_SITE:-https:\/\/clawhub\.ai\}[\s\S]*REGISTRY=\$\{CLAWHUB_REGISTRY:-\$SITE\}[\s\S]*export CLAWHUB_CONFIG_PATH="\$HOME\/\.clawhub-ci\/config\.json"[\s\S]*export CLAWHUB_SITE="\$SITE"[\s\S]*export CLAWHUB_REGISTRY="\$REGISTRY"[\s\S]*bash scripts\/ci\/guard_clawhub_slug_owner\.sh[\s\S]*\$\{\{ steps\.publishable\.outputs\.clawhub_slug \}\}/,
'ClawHub republish job must guard the resolved publish slug with the authenticated ClawHub config path',
);
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',
); );
@@ -0,0 +1,184 @@
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.11",
expectedSimulated: "0.1.12",
expectedAgent: "openclaw",
});
await runSimulation({
skillDir: "skills/hermes-traffic-guardian",
outputDir: path.join(tempRoot, "beta"),
expectedOriginal: "0.0.1-beta4",
expectedSimulated: "0.0.1-beta5",
expectedAgent: "hermes-agent",
});
const alphaSkillDir = await prereleaseFixture("skills/picoclaw-self-pen-testing", "0.0.3-alpha1", "alpha-fixture");
await runSimulation({
skillDir: alphaSkillDir,
outputDir: path.join(tempRoot, "alpha"),
expectedOriginal: "0.0.3-alpha1",
expectedSimulated: "0.0.3-alpha2",
expectedAgent: "openclaw",
});
const rcSkillDir = await prereleaseFixture("skills/picoclaw-security-guardian", "0.0.4-rc1", "rc-fixture");
await runSimulation({
skillDir: rcSkillDir,
outputDir: path.join(tempRoot, "rc"),
expectedOriginal: "0.0.4-rc1",
expectedSimulated: "0.0.4-rc2",
expectedAgent: "openclaw",
});
const previewSkillDir = await prereleaseFixture("skills/openclaw-traffic-guardian", "0.0.1-preview", "preview-fixture");
await runSimulation({
skillDir: previewSkillDir,
outputDir: path.join(tempRoot, "preview"),
expectedOriginal: "0.0.1-preview",
expectedSimulated: "0.0.1-preview1",
expectedAgent: "openclaw",
});
} finally {
await rm(tempRoot, { recursive: true, force: true });
}
+79
View File
@@ -0,0 +1,79 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
const outputDir = await mkdtemp(path.join(tmpdir(), "clawsec-trust-packet-"));
function runTrustPacket(skillDir, targetDir, tag) {
return spawnSync(
process.execPath,
[
"scripts/ci/generate_skill_release_trust_packet.mjs",
skillDir,
targetDir,
"--repository",
"prompt-security/clawsec",
"--tag",
tag,
"--source-ref",
"main",
],
{ encoding: "utf8" },
);
}
try {
const result = runTrustPacket("skills/clawsec-suite", outputDir, "clawsec-suite-v0.1.11");
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\.11/);
assert.equal(permissions.skill, "clawsec-suite");
assert.equal(permissions.version, "0.1.11");
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.5",
);
assert.equal(
hermesResult.status,
0,
`Hermes trust packet generator failed\nstdout:\n${hermesResult.stdout}\nstderr:\n${hermesResult.stderr}`,
);
const hermesInstall = await readFile(path.join(hermesOutputDir, "install.md"), "utf8");
assert.match(
hermesInstall,
/npx skills add prompt-security\/clawsec --skill hermes-attestation-guardian --agent hermes-agent --global --yes/,
);
} finally {
await rm(outputDir, { recursive: true, force: true });
}
+7
View File
@@ -1,5 +1,12 @@
# Changelog # 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 ## [0.0.3] - 2026-05-14
### Security ### Security
+11
View File
@@ -0,0 +1,11 @@
# Claw Release
Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.
## Vercel Skills Installation
Install with the Vercel Skills CLI for this harness:
```bash
npx skills add prompt-security/clawsec --skill claw-release -a openclaw -y
```
+16 -4
View File
@@ -1,9 +1,14 @@
--- ---
name: claw-release name: claw-release
version: 0.0.3 version: 0.0.4
description: Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification. description: Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
metadata: {"openclaw":{"emoji":"🚀","category":"utility","internal":true}} metadata:
internal: true
openclaw:
emoji: "🚀"
category: "utility"
internal: true
clawdis: clawdis:
emoji: "🚀" emoji: "🚀"
requires: requires:
@@ -18,6 +23,14 @@ Internal tool for releasing skills and managing the ClawSec catalog.
--- ---
## Vercel Skills Installation
Install with the Vercel Skills CLI for this harness:
```bash
npx skills add prompt-security/clawsec --skill claw-release -a openclaw -y
```
## Operational Notes ## Operational Notes
- Internal maintainer workflow only. - Internal maintainer workflow only.
@@ -26,7 +39,6 @@ Internal tool for releasing skills and managing the ClawSec catalog.
- Side effects: creates commits, tags, pushes to remote, and publishes GitHub Releases - 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 - Trust model: run only from a trusted checkout with a clean working tree and maintainer approval
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
@@ -35,7 +47,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="claw-release" SKILL_NAME="claw-release"
VERSION="0.0.3" VERSION="0.0.4"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "claw-release", "name": "claw-release",
"version": "0.0.3", "version": "0.0.4",
"description": "Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.", "description": "Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.7] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.5] - 2026-06-07
### Security ### Security
+8
View File
@@ -2,6 +2,14 @@
A `clawsec-suite` companion skill that adds a standalone reputation gate before guarded installs. 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 ## Operational Notes
- Required runtime: `node`, `clawhub`, `openclaw` - Required runtime: `node`, `clawhub`, `openclaw`
+9 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: clawsec-clawhub-checker name: clawsec-clawhub-checker
version: 0.0.5 version: 0.0.7
description: ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation. description: ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
clawdis: clawdis:
@@ -14,6 +14,14 @@ clawdis:
Adds a reputation gate on top of the `clawsec-suite` guarded installer. 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 ## Operational Notes
- Required runtime: `node`, `clawhub`, `openclaw` - Required runtime: `node`, `clawhub`, `openclaw`
@@ -45,7 +53,6 @@ Optional preflight check (validates local paths and prints recommended command):
node ~/.openclaw/skills/clawsec-clawhub-checker/scripts/setup_reputation_hook.mjs node ~/.openclaw/skills/clawsec-clawhub-checker/scripts/setup_reputation_hook.mjs
``` ```
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "clawsec-clawhub-checker", "name": "clawsec-clawhub-checker",
"version": "0.0.5", "version": "0.0.7",
"description": "ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.", "description": "ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.",
"author": "abutbul", "author": "abutbul",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
+12
View File
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.10] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.8] - 2026-05-24
### Changed ### Changed
+8
View File
@@ -2,6 +2,14 @@
Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence and stay informed about emerging threats. 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 ## Operational Notes
- Required runtime for standalone installation: `bash`, `curl`, `jq`, `shasum`, `unzip` - Required runtime for standalone installation: `bash`, `curl`, `jq`, `shasum`, `unzip`
+10 -4
View File
@@ -1,6 +1,6 @@
--- ---
name: clawsec-feed name: clawsec-feed
version: 0.0.8 version: 0.0.10
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. 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 homepage: https://clawsec.prompt.security
metadata: {"openclaw":{"emoji":"📡","category":"security"}} metadata: {"openclaw":{"emoji":"📡","category":"security"}}
@@ -16,6 +16,14 @@ Security advisory feed monitoring for AI agents. Subscribe to community-driven t
The default `feed.json` is the consolidated agent feed. It includes NVD CVEs, approved community advisories, and provisional GitHub Security Advisories that do not have CVE IDs yet. 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 ## Operational Notes
- Required runtime for standalone installation: `bash`, `curl`, `jq`, `shasum`, `unzip` - Required runtime for standalone installation: `bash`, `curl`, `jq`, `shasum`, `unzip`
@@ -66,7 +74,6 @@ Standalone installation is a network download workflow. Verify the release sourc
Installation Steps: Installation Steps:
Agents should review scripts before execution! Agents should review scripts before execution!
```bash ```bash
# Get latest clawsec-feed release tag # Get latest clawsec-feed release tag
LATEST_TAG=$(curl -sSL https://api.github.com/repos/prompt-security/ClawSec/releases | \ LATEST_TAG=$(curl -sSL https://api.github.com/repos/prompt-security/ClawSec/releases | \
@@ -81,7 +88,6 @@ Once you have this skill file, proceed to **[Deploy ClawSec Feed](#deploy-clawse
--- ---
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
@@ -90,7 +96,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="clawsec-feed" SKILL_NAME="clawsec-feed"
VERSION="0.0.8" VERSION="0.0.9"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
v+PiWmjIkY6zdIyI9xJX0l0aTy0Azp1+LoZR6qaiDZJnXFuSBX4Sw/x5tMdTb0xSbqdDTJOZwwWI8coPVepzBw== K19pfVfv7qB1cqFPFTu69+sKLHIMIrmS7GeK4BZIlHzRvrLfRUuq/KftC8/CIWwvixVlBBm/iZlyfJ5sutoDDw==
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "clawsec-feed", "name": "clawsec-feed",
"version": "0.0.8", "version": "0.0.10",
"description": "Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence.", "description": "Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
+12
View File
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.9] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.7] - 2026-06-07
### Security ### Security
+8
View File
@@ -2,6 +2,14 @@
ClawSec now supports NanoClaw, a containerized WhatsApp bot powered by Claude agents. 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 ## What Changed
### Advisory Feed Monitoring ### Advisory Feed Monitoring
+9 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: clawsec-nanoclaw name: clawsec-nanoclaw
version: 0.0.7 version: 0.0.9
description: Use when checking for security vulnerabilities in NanoClaw skills, before installing new skills, or when asked about security advisories affecting the bot description: Use when checking for security vulnerabilities in NanoClaw skills, before installing new skills, or when asked about security advisories affecting the bot
--- ---
@@ -8,6 +8,14 @@ description: Use when checking for security vulnerabilities in NanoClaw skills,
Security advisory monitoring that protects your WhatsApp bot from known vulnerabilities in skills and dependencies. 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 ## 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. ClawSec provides MCP tools that check installed skills against a curated feed of security advisories. It prevents installation of vulnerable skills, includes exploitability context for triage, and alerts you to issues in existing ones.
@@ -201,7 +209,6 @@ See [INSTALL.md](./INSTALL.md) for setup and [docs/](./docs/) for advanced usage
- Provides actionable remediation steps - Provides actionable remediation steps
- Zero false positives (curated feed only) - Zero false positives (curated feed only)
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "clawsec-nanoclaw", "name": "clawsec-nanoclaw",
"version": "0.0.7", "version": "0.0.9",
"description": "ClawSec security suite for NanoClaw - Advisory feed monitoring, MCP tools for vulnerability checking, and Ed25519 signature verification for containerized WhatsApp bot agents", "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", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
+12
View File
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.6] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.4] - 2026-06-07
### Security ### Security
+11
View File
@@ -0,0 +1,11 @@
# Clawsec Scanner
Automated vulnerability scanner for agent platforms. Performs dependency scanning (npm audit, pip-audit), multi-database CVE lookup (OSV, NVD, GitHub Advisory), SAST analysis (Semgrep, Bandit), and agent-specific static hook inspection for OpenClaw hooks.
## Vercel Skills Installation
Install with the Vercel Skills CLI for this harness:
```bash
npx skills add prompt-security/clawsec --skill clawsec-scanner -a openclaw -y
```
+9 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: clawsec-scanner name: clawsec-scanner
version: 0.0.4 version: 0.0.6
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. 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 homepage: https://clawsec.prompt.security
clawdis: clawdis:
@@ -20,6 +20,14 @@ Comprehensive security scanner for agent platforms that automates vulnerability
- **Unified Reporting**: Consolidated vulnerability reports with severity classification and remediation guidance - **Unified Reporting**: Consolidated vulnerability reports with severity classification and remediation guidance
- **Continuous Monitoring**: OpenClaw hook integration for automated periodic scanning - **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 ## Features
### Multi-Engine Scanning ### Multi-Engine Scanning
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "clawsec-scanner", "name": "clawsec-scanner",
"version": "0.0.4", "version": "0.0.6",
"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.", "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", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
+12
View File
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.1.11] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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. 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/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+11
View File
@@ -0,0 +1,11 @@
# Clawsec Suite
ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills.
## Vercel Skills Installation
Install with the Vercel Skills CLI for this harness:
```bash
npx skills add prompt-security/clawsec --skill clawsec-suite -a openclaw -y
```
+9 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: clawsec-suite name: clawsec-suite
version: 0.1.9 version: 0.1.11
description: ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills. 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 homepage: https://clawsec.prompt.security
clawdis: clawdis:
@@ -11,6 +11,14 @@ clawdis:
# ClawSec Suite # 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 ## Operational Notes
- Required runtime: `node`, `npx`, `openclaw`, `curl`, `jq`, `shasum`, `openssl`, `unzip` - Required runtime: `node`, `npx`, `openclaw`, `curl`, `jq`, `shasum`, `openssl`, `unzip`
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "clawsec-suite", "name": "clawsec-suite",
"version": "0.1.9", "version": "0.1.11",
"description": "ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills.", "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", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
+16
View File
@@ -1,5 +1,21 @@
# Changelog # Changelog
## [0.0.8] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.6] - 2026-05-14
### Security ### Security
+22
View File
@@ -2,6 +2,20 @@
Community incident reporting for AI agents. 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 ## Operational Notes
- Reporting is opt-in for every submission - Reporting is opt-in for every submission
@@ -17,6 +31,14 @@ Community incident reporting for AI agents.
## Quick Install ## Quick Install
Vercel skills installer:
```bash
npx skills add prompt-security/clawsec --skill clawtributor -a codex -y
```
OpenClaw/ClawHub:
```bash ```bash
npx clawhub@latest install clawtributor npx clawhub@latest install clawtributor
``` ```
+38 -12
View File
@@ -1,23 +1,44 @@
--- ---
name: clawtributor name: clawtributor
version: 0.0.6 version: 0.0.8
description: Community incident reporting for AI agents. Contribute to collective security by reporting threats. description: Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
metadata: {"openclaw":{"emoji":"🤝","category":"security"}} platforms:
- openclaw
- nanoclaw
- hermes
- picoclaw
metadata:
global: true
openclaw:
emoji: "🤝"
category: "security"
clawdis: clawdis:
emoji: "🤝" emoji: "🤝"
requires:
bins: [openclaw]
--- ---
# Clawtributor 🤝 # Clawtributor 🤝
Community incident reporting for AI agents. Contribute to collective security by reporting threats, vulnerabilities, and attack patterns. 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 ## Operational Notes
- Recommended install path: ClawHub registry (`npx clawhub@latest install clawtributor`) - 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 `~/.openclaw/` - Side effects: creates local report/state files under `~/.clawsec/clawtributor/`
- Network behavior: none unless the user explicitly approves manual submission - 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 - Trust model: reporting is opt-in for every submission; sanitize evidence before it leaves the host
@@ -27,7 +48,13 @@ Community incident reporting for AI agents. Contribute to collective security by
## Installation ## Installation
Install from the registry: Install with your harness-native skills installer. For the Vercel skills installer:
```bash
npx skills add prompt-security/clawsec --skill clawtributor -a codex -y
```
For OpenClaw/ClawHub environments, install from the registry:
```bash ```bash
npx clawhub@latest install clawtributor npx clawhub@latest install clawtributor
@@ -44,7 +71,6 @@ I will keep reports local unless you explicitly approve submission.
--- ---
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
@@ -53,7 +79,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="clawtributor" SKILL_NAME="clawtributor"
VERSION="0.0.6" VERSION="0.0.7"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -233,7 +259,7 @@ See [reporting.md](./reporting.md) for the full report format and submission gui
### Step 1: Prepare report locally ### Step 1: Prepare report locally
- Save the report JSON under `~/.openclaw/clawtributor-reports/` - Save the report JSON under `~/.clawsec/clawtributor/reports/`
- Keep file permissions private (`chmod 600`) - Keep file permissions private (`chmod 600`)
- Confirm the report is sanitized before sharing - Confirm the report is sanitized before sharing
@@ -284,7 +310,7 @@ DO NOT include:
## State Tracking ## State Tracking
Track submitted reports in `~/.openclaw/clawtributor-state.json`. Track submitted reports in `~/.clawsec/clawtributor/state.json`.
Example: Example:
+10 -7
View File
@@ -1,16 +1,24 @@
{ {
"name": "clawtributor", "name": "clawtributor",
"version": "0.0.6", "version": "0.0.8",
"description": "Community incident reporting for AI agents. Contribute to collective security by reporting threats.", "description": "Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"homepage": "https://clawsec.prompt.security", "homepage": "https://clawsec.prompt.security",
"platforms": [
"openclaw",
"nanoclaw",
"hermes",
"picoclaw"
],
"keywords": [ "keywords": [
"security", "security",
"reporting", "reporting",
"community", "community",
"agents", "agents",
"ai", "ai",
"global",
"harness-neutral",
"vulnerability", "vulnerability",
"contribution" "contribution"
], ],
@@ -36,11 +44,6 @@
"openclaw": { "openclaw": {
"emoji": "🤝", "emoji": "🤝",
"category": "security", "category": "security",
"requires": {
"bins": [
"openclaw"
]
},
"execution": { "execution": {
"always": false, "always": false,
"persistence": "Stores local report/state files only; no recurring automation is created by default.", "persistence": "Stores local report/state files only; no recurring automation is created by default.",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.1.5] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.1.3] - 2026-05-24
### Changed ### Changed
@@ -4,6 +4,14 @@ Hermes-only attestation, advisory verification, and guarded verification workflo
Status: implemented (v0.1.0), Hermes-only. 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 ## Capabilities
This skill now covers the full Hermes-side capability set expected from the clawsec-suite parity workstream: This skill now covers the full Hermes-side capability set expected from the clawsec-suite parity workstream:
+9 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: hermes-attestation-guardian name: hermes-attestation-guardian
version: 0.1.3 version: 0.1.5
description: Hermes-only runtime security attestation and drift detection skill for operator-managed Hermes infrastructure. description: Hermes-only runtime security attestation and drift detection skill for operator-managed Hermes infrastructure.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
hermes: hermes:
@@ -15,6 +15,13 @@ IMPORTANT SCOPE:
- This skill targets Hermes infrastructure only (CLI/Gateway/profile-managed deployments). - This skill targets Hermes infrastructure only (CLI/Gateway/profile-managed deployments).
- This skill is not an OpenClaw runtime hook package. - 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 ## Release Artifact Verification
@@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="hermes-attestation-guardian" SKILL_NAME="hermes-attestation-guardian"
VERSION="0.1.3" VERSION="0.1.5"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -1,6 +1,6 @@
{ {
"name": "hermes-attestation-guardian", "name": "hermes-attestation-guardian",
"version": "0.1.3", "version": "0.1.5",
"description": "Hermes-only runtime security attestation and drift detection skill. Generates deterministic posture artifacts, verifies integrity fail-closed, and classifies baseline drift severity.", "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", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.1-beta4] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.1-beta2] - 2026-05-13
### Security ### Security
+8 -1
View File
@@ -4,6 +4,14 @@ Baseline skill for Hermes runtime traffic monitoring.
This package is intentionally a spec scaffold. Builders should add the Hermes-specific monitor implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. 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 ## Intended Capability
- detect outbound secret exfiltration in Hermes HTTP/HTTPS traffic - detect outbound secret exfiltration in Hermes HTTP/HTTPS traffic
@@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the Hermes-sp
## Builder Notes ## Builder Notes
Keep runtime ownership in this skill. `hermes-attestation-guardian` should only attest this skill's state, config, and output fingerprints. Keep runtime ownership in this skill. `hermes-attestation-guardian` should only attest this skill's state, config, and output fingerprints.
+9 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: hermes-traffic-guardian name: hermes-traffic-guardian
version: 0.0.1-beta2 version: 0.0.1-beta4
description: Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture. description: Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
author: prompt-security author: prompt-security
@@ -15,6 +15,13 @@ hermes:
This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. 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 ## Release Artifact Verification
@@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="hermes-traffic-guardian" SKILL_NAME="hermes-traffic-guardian"
VERSION="0.0.1-beta2" VERSION="0.0.1-beta3"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -145,4 +152,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking - default blocking
- sending traffic to external services - sending traffic to external services
- collecting full request/response bodies - collecting full request/response bodies
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hermes-traffic-guardian", "name": "hermes-traffic-guardian",
"version": "0.0.1-beta2", "version": "0.0.1-beta4",
"description": "Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture.", "description": "Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.1-beta4] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.1-beta2] - 2026-05-13
### Security ### Security
+8 -1
View File
@@ -4,6 +4,14 @@ Baseline skill for NanoClaw runtime traffic monitoring.
This package is intentionally a spec scaffold. Builders should add the NanoClaw-specific host-service, IPC, and MCP implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. 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 ## Intended Capability
- detect outbound secret exfiltration in NanoClaw host-managed traffic - detect outbound secret exfiltration in NanoClaw host-managed traffic
@@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the NanoClaw-
## Builder Notes ## Builder Notes
Follow the existing `clawsec-nanoclaw` pattern: host services own privileged operations, while MCP tools expose bounded requests and redacted responses. Follow the existing `clawsec-nanoclaw` pattern: host services own privileged operations, while MCP tools expose bounded requests and redacted responses.
+9 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: nanoclaw-traffic-guardian name: nanoclaw-traffic-guardian
version: 0.0.1-beta2 version: 0.0.1-beta4
description: NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces. description: NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
author: prompt-security author: prompt-security
@@ -14,6 +14,13 @@ nanoclaw:
This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. 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 ## Release Artifact Verification
@@ -23,7 +30,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="nanoclaw-traffic-guardian" SKILL_NAME="nanoclaw-traffic-guardian"
VERSION="0.0.1-beta2" VERSION="0.0.1-beta3"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -146,4 +153,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking - default blocking
- sending traffic to external services - sending traffic to external services
- exposing raw request/response bodies to the container - exposing raw request/response bodies to the container
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "nanoclaw-traffic-guardian", "name": "nanoclaw-traffic-guardian",
"version": "0.0.1-beta2", "version": "0.0.1-beta4",
"description": "NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces.", "description": "NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.1.8] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.1.6] - 2026-05-16
### Fixed ### Fixed
+8
View File
@@ -2,6 +2,14 @@
Automated daily security audits for OpenClaw agents with DM delivery and optional email reporting. 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 ## Overview
The Audit Watchdog provides automated security monitoring for your OpenClaw agent deployments: The Audit Watchdog provides automated security monitoring for your OpenClaw agent deployments:
+10 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: openclaw-audit-watchdog name: openclaw-audit-watchdog
version: 0.1.6 version: 0.1.8
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. 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 homepage: https://clawsec.prompt.security
metadata: metadata:
@@ -29,6 +29,14 @@ clawdis:
# Prompt Security Audit (openclaw) # 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 ## Installation Options
You can get openclaw-audit-watchdog in two ways: You can get openclaw-audit-watchdog in two ways:
@@ -65,7 +73,6 @@ Continue below for standalone installation instructions.
--- ---
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
@@ -74,7 +81,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="openclaw-audit-watchdog" SKILL_NAME="openclaw-audit-watchdog"
VERSION="0.1.6" VERSION="0.1.7"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "openclaw-audit-watchdog", "name": "openclaw-audit-watchdog",
"version": "0.1.6", "version": "0.1.8",
"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.", "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", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,21 @@
# Changelog # Changelog
## [0.0.1-beta4] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.1-beta2] - 2026-05-13
### Security ### Security
+9 -1
View File
@@ -4,10 +4,19 @@ 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`. 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 ## Intended Capability
- detect outbound secret exfiltration in agent HTTP/HTTPS traffic - detect outbound secret exfiltration in agent HTTP/HTTPS traffic
- detect inbound command-injection and tool-abuse payloads - detect inbound command-injection and tool-abuse payloads
- record operator-review findings for approval-sensitive social-account mutations
- write redacted local JSONL findings - write redacted local JSONL findings
- provide explicit start, stop, status, and log-query commands - provide explicit start, stop, status, and log-query commands
- integrate with `clawsec-suite` as an optional add-on - integrate with `clawsec-suite` as an optional add-on
@@ -15,4 +24,3 @@ This package is intentionally a spec scaffold. Builders should add the OpenClaw-
## Builder Notes ## Builder Notes
Use `SPEC.md` as the implementation contract. Keep runtime changes opt-in and scoped to the OpenClaw process being monitored. Use `SPEC.md` as the implementation contract. Keep runtime changes opt-in and scoped to the OpenClaw process being monitored.
+15 -6
View File
@@ -1,7 +1,7 @@
--- ---
name: openclaw-traffic-guardian name: openclaw-traffic-guardian
version: 0.0.1-beta2 version: 0.0.1-beta4
description: OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, and inbound injection detection. description: OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, inbound injection detection, and social-account policy review.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
author: prompt-security author: prompt-security
license: AGPL-3.0-or-later license: AGPL-3.0-or-later
@@ -15,6 +15,13 @@ clawdis:
This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. 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 ## Release Artifact Verification
@@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="openclaw-traffic-guardian" SKILL_NAME="openclaw-traffic-guardian"
VERSION="0.0.1-beta2" VERSION="0.0.1-beta3"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -103,6 +110,7 @@ Builders should use this skill as the OpenClaw landing zone for runtime traffic
- optional HTTPS inspection with per-process CA trust - optional HTTPS inspection with per-process CA trust
- outbound exfiltration detection - outbound exfiltration detection
- inbound injection detection - inbound injection detection
- approval-sensitive social-account mutation review
- redacted local threat logs - redacted local threat logs
- optional OpenClaw hook/status integration - optional OpenClaw hook/status integration
@@ -136,8 +144,10 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
3. Scope proxy environment variables to the target OpenClaw process. 3. Scope proxy environment variables to the target OpenClaw process.
4. Inspect HTTP request/response text up to a bounded byte limit. 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. 5. Support optional HTTPS MITM only when the operator supplies per-process trust configuration.
6. Emit JSONL findings with redacted snippets. 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. Provide a `status` command that reports mode, listener, CA fingerprint if present, and last findings. 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.
## Out of Scope for v0.0.1 Implementation ## Out of Scope for v0.0.1 Implementation
@@ -146,4 +156,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking - default blocking
- sending traffic to external services - sending traffic to external services
- collecting full request/response bodies - collecting full request/response bodies
+26 -1
View File
@@ -45,6 +45,24 @@ 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 ## Minimum Detection Set
Outbound EXFIL: Outbound EXFIL:
@@ -64,6 +82,12 @@ Inbound INJECTION:
- destructive remove commands - destructive remove commands
- SSH authorized-key injection shapes - 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 ## Safety Requirements
- Default mode is detect-and-log. - Default mode is detect-and-log.
@@ -72,6 +96,7 @@ Inbound INJECTION:
- Maximum scan bytes must be configurable and bounded. - Maximum scan bytes must be configurable and bounded.
- CA trust must be per-process by default. - CA trust must be per-process by default.
- System trust-store instructions must require explicit operator confirmation and must never run automatically. - 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 ## Tests Required Before Release
@@ -79,7 +104,7 @@ Inbound INJECTION:
- redaction tests proving secrets are not persisted - redaction tests proving secrets are not persisted
- proxy fixture tests for HTTP request and response inspection - proxy fixture tests for HTTP request and response inspection
- no-false-positive tests for common benign traffic - 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 - lifecycle tests for stale PID/state cleanup
- status output tests - status output tests
- OpenClaw hook integration tests if hook files are added - OpenClaw hook integration tests if hook files are added
+11 -4
View File
@@ -1,7 +1,7 @@
{ {
"name": "openclaw-traffic-guardian", "name": "openclaw-traffic-guardian",
"version": "0.0.1-beta2", "version": "0.0.1-beta4",
"description": "OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, and inbound injection detection.", "description": "OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, inbound injection detection, and social-account policy review.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"homepage": "https://clawsec.prompt.security/", "homepage": "https://clawsec.prompt.security/",
@@ -15,7 +15,10 @@
"injection", "injection",
"proxy", "proxy",
"mitm", "mitm",
"runtime" "runtime",
"policy-review",
"operator-review",
"social-account-mutation"
], ],
"sbom": { "sbom": {
"files": [ "files": [
@@ -84,6 +87,7 @@
"https_mitm_inspection": "planned_optional", "https_mitm_inspection": "planned_optional",
"egress_exfiltration_detection": "planned", "egress_exfiltration_detection": "planned",
"inbound_injection_detection": "planned", "inbound_injection_detection": "planned",
"social_account_policy_review": "planned",
"blocking": "future_version" "blocking": "future_version"
}, },
"execution": { "execution": {
@@ -96,6 +100,7 @@
"Default to detect-and-log mode; blocking is out of scope for v0.0.1 implementation.", "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.", "Scope HTTP_PROXY/HTTPS_PROXY to the OpenClaw process being monitored.",
"Redact secret snippets before writing logs or sending conversation alerts.", "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." "Integrate with clawsec-suite as an optional add-on, not a default install."
], ],
"triggers": [ "triggers": [
@@ -103,7 +108,9 @@
"openclaw traffic monitoring", "openclaw traffic monitoring",
"monitor openclaw egress", "monitor openclaw egress",
"inspect openclaw http traffic", "inspect openclaw http traffic",
"detect openclaw exfiltration" "detect openclaw exfiltration",
"review social account mutations",
"detect tweetclaw write actions"
] ]
} }
} }
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.5] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.3] - 2026-05-24
### Changed ### Changed
+8 -1
View File
@@ -6,6 +6,14 @@ Status: implemented (v0.0.1), Picoclaw-specific.
Detailed architecture/operator docs: `wiki/modules/picoclaw-security-guardian.md`. 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 ## Support matrix mapping
| Skill name | supported platform | security feed | config drift | agent posture-review lane | chain of supply verification | | Skill name | supported platform | security feed | config drift | agent posture-review lane | chain of supply verification |
@@ -48,4 +56,3 @@ test/picoclaw_security_guardian_sandbox_regression.sh
``` ```
It uses Docker to publish the skill through a local ClawHub-compatible registry, installs it with Picoclaw's own `find_skills` / `install_skill` flow into an isolated Picoclaw workspace, confirms Picoclaw's skill loader can list/load it, then verifies the installed copy's profile, drift, advisory, and supply-chain paths. 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.
+9 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: picoclaw-security-guardian name: picoclaw-security-guardian
version: 0.0.3 version: 0.0.5
description: Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance. description: Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
author: prompt-security author: prompt-security
@@ -18,6 +18,13 @@ picoclaw:
Detailed architecture/operator docs: `wiki/modules/picoclaw-security-guardian.md`. 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 ## Release Artifact Verification
@@ -27,7 +34,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="picoclaw-security-guardian" SKILL_NAME="picoclaw-security-guardian"
VERSION="0.0.3" VERSION="0.0.4"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "picoclaw-security-guardian", "name": "picoclaw-security-guardian",
"version": "0.0.3", "version": "0.0.5",
"description": "Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance.", "description": "Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.4] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.2] - 2026-05-13
### Security ### Security
@@ -4,6 +4,14 @@ Picoclaw-only local posture-review findings package for ClawSec.
Status: implemented (v0.0.1), Picoclaw-specific. 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 ## What it does
Given a generated Picoclaw posture profile, it emits severity-ranked findings and a summary count for local operator review. Given a generated Picoclaw posture profile, it emits severity-ranked findings and a summary count for local operator review.
+9 -2
View File
@@ -1,6 +1,6 @@
--- ---
name: picoclaw-self-pen-testing name: picoclaw-self-pen-testing
version: 0.0.2 version: 0.0.4
description: Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance. description: Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
author: prompt-security author: prompt-security
@@ -18,6 +18,13 @@ picoclaw:
Purpose: keep Picoclaw posture-review checks isolated from the broader guardian package so moderation-sensitive checks can be versioned/published independently. 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 ## Release Artifact Verification
@@ -27,7 +34,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="picoclaw-self-pen-testing" SKILL_NAME="picoclaw-self-pen-testing"
VERSION="0.0.2" VERSION="0.0.3"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "picoclaw-self-pen-testing", "name": "picoclaw-self-pen-testing",
"version": "0.0.2", "version": "0.0.4",
"description": "Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance.", "description": "Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.1-beta4] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.1-beta2] - 2026-05-13
### Security ### Security
+8 -1
View File
@@ -4,6 +4,14 @@ Baseline skill for Picoclaw runtime traffic monitoring.
This package is intentionally a spec scaffold. Builders should add the Picoclaw-specific monitor implementation here while preserving the safety contract in `SKILL.md` and `SPEC.md`. 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 ## Intended Capability
- detect outbound secret exfiltration in Picoclaw gateway HTTP/HTTPS traffic - detect outbound secret exfiltration in Picoclaw gateway HTTP/HTTPS traffic
@@ -15,4 +23,3 @@ This package is intentionally a spec scaffold. Builders should add the Picoclaw-
## Builder Notes ## 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. Keep runtime ownership in this skill. `picoclaw-security-guardian` should only profile and drift-check this skill's state, config, and output fingerprints.
+9 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: picoclaw-traffic-guardian name: picoclaw-traffic-guardian
version: 0.0.1-beta2 version: 0.0.1-beta4
description: Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration. description: Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration.
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
author: prompt-security author: prompt-security
@@ -15,6 +15,13 @@ picoclaw:
This is a baseline specification skill. It intentionally does not ship a proxy or runtime implementation yet. 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 ## Release Artifact Verification
@@ -24,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="picoclaw-traffic-guardian" SKILL_NAME="picoclaw-traffic-guardian"
VERSION="0.0.1-beta2" VERSION="0.0.1-beta3"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"
@@ -145,4 +152,3 @@ Read `SPEC.md` before implementing. Use the placeholder folders as follows:
- default blocking - default blocking
- sending traffic to external services - sending traffic to external services
- collecting full request/response bodies - collecting full request/response bodies
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "picoclaw-traffic-guardian", "name": "picoclaw-traffic-guardian",
"version": "0.0.1-beta2", "version": "0.0.1-beta4",
"description": "Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration.", "description": "Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration.",
"author": "prompt-security", "author": "prompt-security",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
+12
View File
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.0.8] - 2026-06-22
### Changed
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
## [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 ## [0.0.6] - 2026-05-14
### Security ### Security
+8
View File
@@ -2,6 +2,14 @@
A small, dependency-free integrity guard for OpenClaw agent workspaces. 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 ## Operational Notes
- Required runtime: `python3` - Required runtime: `python3`
+10 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: soul-guardian name: soul-guardian
version: 0.0.6 version: 0.0.8
description: Drift detection + baseline integrity guard for agent workspace files with automatic alerting support description: Drift detection + baseline integrity guard for agent workspace files with automatic alerting support
homepage: https://clawsec.prompt.security homepage: https://clawsec.prompt.security
metadata: {"openclaw":{"emoji":"👻","category":"security"}} metadata: {"openclaw":{"emoji":"👻","category":"security"}}
@@ -14,6 +14,14 @@ clawdis:
Protects your agent's core files (SOUL.md, AGENTS.md, etc.) from unauthorized changes with automatic detection, restoration, and **user alerting**. 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 ## Operational Notes
- Required runtime: `python3` - Required runtime: `python3`
@@ -22,7 +30,6 @@ Protects your agent's core files (SOUL.md, AGENTS.md, etc.) from unauthorized ch
- Network behavior: none by default - Network behavior: none by default
- Trust model: any scheduling is opt-in, but restore mode intentionally overwrites drifted files - Trust model: any scheduling is opt-in, but restore mode intentionally overwrites drifted files
## Release Artifact Verification ## 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. For standalone installs, verify the signed release manifest before trusting `SKILL.md`, `skill.json`, or the archive. The `skill.json` file is the package metadata/SBOM source, and the release pipeline signs `checksums.json` with the ClawSec release key.
@@ -31,7 +38,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
set -euo pipefail set -euo pipefail
SKILL_NAME="soul-guardian" SKILL_NAME="soul-guardian"
VERSION="0.0.6" VERSION="0.0.7"
REPO="prompt-security/clawsec" REPO="prompt-security/clawsec"
TAG="${SKILL_NAME}-v${VERSION}" TAG="${SKILL_NAME}-v${VERSION}"
BASE="https://github.com/${REPO}/releases/download/${TAG}" BASE="https://github.com/${REPO}/releases/download/${TAG}"

Some files were not shown because too many files have changed in this diff Show More