mirror of
https://github.com/prompt-security/clawsec.git
synced 2026-06-14 14:01:20 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5fe8ca54c | |||
| 5da8fb808d |
@@ -53,21 +53,9 @@ jobs:
|
||||
|
||||
- name: Collect traffic
|
||||
env:
|
||||
# Traffic endpoints reject the Actions GITHUB_TOKEN ("Resource not
|
||||
# accessible by integration") — a PAT from a user with push access
|
||||
# is required: classic with repo scope, or fine-grained with read
|
||||
# access to Administration on this repository.
|
||||
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN }}
|
||||
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN || github.token }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "${GH_TRAFFIC_TOKEN}" ]; then
|
||||
echo "::error::No traffic-capable token configured. Set the TRAFFIC_ARCHIVE_TOKEN secret to a PAT with push access (classic: repo scope; fine-grained: Administration read)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}"
|
||||
run: node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}"
|
||||
|
||||
- name: Commit archive
|
||||
run: |
|
||||
|
||||
+1515
-1172
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
jPrlTYwicRwoQgTs5Rk3Y3g6Lz78jNRs9ZNf0R09M4jkJokZENxfvhvHphI9MH4u+7wv0sFZ+yZbQtJ42y+hCQ==
|
||||
agiAAFvzM1vNHxH2+bGtyeKqFScLWJHnNreBcPpTODUqD0xqFi0cnyP/ZaZX+Rsw1Y9uZ7pGdFdA93pD4lh2BQ==
|
||||
+206
-274
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
M1Jm4YHXsm0msygmd+XCJBRWMrXIjQfv1Y5v7XS8RCachLQwEzUJ1nhhic6CXxItNLmvgmDjVCMPVdHpnOMqDA==
|
||||
q1EyZ75QcdG2X6FVDkUoAyBtQE3ONA+7k9cmNFmXFgOOuGRPOpSDFUtbSvy86HPqnii26DMoeFJ1hatWJ0lBCQ==
|
||||
@@ -321,14 +321,7 @@ const fetchJson = async ({ repo, token, pathname, fetchImpl }) => {
|
||||
if (!response.ok) {
|
||||
const body = await response.text().catch(() => '');
|
||||
const suffix = body ? ` ${body.slice(0, 500)}` : '';
|
||||
const lacksPushAccess = response.status === 403
|
||||
&& /resource not accessible|must have push access/i.test(body);
|
||||
const hint = lacksPushAccess
|
||||
? ' Traffic endpoints require a token with push access to the repository; the Actions GITHUB_TOKEN is always rejected. Use a classic PAT with the repo scope or a fine-grained PAT with read access to Administration.'
|
||||
: response.status === 401
|
||||
? ' The token was rejected as invalid — it may be expired or revoked. Rotate the TRAFFIC_ARCHIVE_TOKEN secret.'
|
||||
: '';
|
||||
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}${hint}`);
|
||||
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
|
||||
@@ -76,40 +76,6 @@ test('fetchGitHubTraffic requests the daily GitHub traffic endpoints with auth',
|
||||
assert.deepEqual(snapshot.clones.clones, responses[`/repos/${TEST_REPOSITORY}/traffic/clones?per=day`].clones);
|
||||
});
|
||||
|
||||
test('fetchGitHubTraffic explains traffic token requirements on 403', async () => {
|
||||
const fetchImpl = async () => new globalThis.Response(
|
||||
JSON.stringify({ message: 'Resource not accessible by integration' }),
|
||||
{ status: 403 },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
fetchGitHubTraffic({
|
||||
repo: TEST_REPOSITORY,
|
||||
token: 'installation-token',
|
||||
capturedAt,
|
||||
fetchImpl,
|
||||
}),
|
||||
/returned 403\..*push access/,
|
||||
);
|
||||
});
|
||||
|
||||
test('fetchGitHubTraffic flags invalid tokens on 401', async () => {
|
||||
const fetchImpl = async () => new globalThis.Response(
|
||||
JSON.stringify({ message: 'Bad credentials' }),
|
||||
{ status: 401 },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
fetchGitHubTraffic({
|
||||
repo: TEST_REPOSITORY,
|
||||
token: 'expired-token',
|
||||
capturedAt,
|
||||
fetchImpl,
|
||||
}),
|
||||
/returned 401\..*expired or revoked/,
|
||||
);
|
||||
});
|
||||
|
||||
test('mergeTrafficArchive upserts daily views and clones without double-counting overlapping windows', () => {
|
||||
const archive = mergeTrafficArchive(
|
||||
{
|
||||
@@ -266,8 +232,7 @@ test('traffic archive workflow uses a daily schedule and a dedicated archive bra
|
||||
|
||||
assert.match(workflow, /cron:\s+'17 3 \* \* \*'/);
|
||||
assert.match(workflow, /TRAFFIC_ARCHIVE_BRANCH:\s+traffic-archive/);
|
||||
assert.match(workflow, /GH_TRAFFIC_TOKEN:\s*\$\{\{\s*secrets\.TRAFFIC_ARCHIVE_TOKEN\b/);
|
||||
assert.doesNotMatch(workflow, /GH_TRAFFIC_TOKEN:[^\n]*github\.token/);
|
||||
assert.match(workflow, /TRAFFIC_ARCHIVE_TOKEN/);
|
||||
assert.match(workflow, /node scripts\/archive-github-traffic\.mjs/);
|
||||
assert.match(workflow, /git add traffic\/archive\.json traffic\/summary\.json/);
|
||||
assert.match(workflow, /git rm --ignore-unmatch traffic\/README\.md/);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: claw-release
|
||||
version: 0.0.4
|
||||
version: 0.0.5
|
||||
description: Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.
|
||||
homepage: https://clawsec.prompt.security
|
||||
metadata:
|
||||
@@ -240,13 +240,17 @@ This skill (`claw-release`) is an internal skill.
|
||||
|
||||
## Existing Skills
|
||||
|
||||
| Skill | Category | Internal |
|
||||
|-------|----------|----------|
|
||||
| clawsec-feed | security | No |
|
||||
| clawtributor | security | No |
|
||||
| openclaw-audit-watchdog | security | No |
|
||||
| soul-guardian | security | No |
|
||||
| claw-release | utility | Yes |
|
||||
Do not rely on a hardcoded list here — it drifts out of date as skills are
|
||||
added. Enumerate the live set from the repo instead:
|
||||
|
||||
```bash
|
||||
for f in skills/*/skill.json; do
|
||||
jq -r '"\(.name)\t\(.openclaw.category // "-")\tinternal=\(.openclaw.internal // false)"' "$f"
|
||||
done
|
||||
```
|
||||
|
||||
Each skill's category and internal flag are the source of truth in its own
|
||||
`skills/<name>/skill.json` (`.openclaw.category` and `.openclaw.internal`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,24 +1,44 @@
|
||||
{
|
||||
"name": "claw-release",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"description": "Release automation for Claw skills and website. Guides through version bumping, tagging, and release verification.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"homepage": "https://clawsec.prompt.security",
|
||||
"keywords": ["release", "versioning", "deployment", "automation", "ci-cd", "skills"],
|
||||
|
||||
"keywords": [
|
||||
"release",
|
||||
"versioning",
|
||||
"deployment",
|
||||
"automation",
|
||||
"ci-cd",
|
||||
"skills"
|
||||
],
|
||||
"sbom": {
|
||||
"files": [
|
||||
{ "path": "SKILL.md", "required": true, "description": "Release workflow guide" },
|
||||
{ "path": "CHANGELOG.md", "required": true, "description": "Version history and release notes" }
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"required": true,
|
||||
"description": "Release workflow guide"
|
||||
},
|
||||
{
|
||||
"path": "CHANGELOG.md",
|
||||
"required": true,
|
||||
"description": "Version history and release notes"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"openclaw": {
|
||||
"emoji": "🚀",
|
||||
"category": "utility",
|
||||
"internal": true,
|
||||
"requires": { "bins": ["bash", "git", "jq", "gh"] },
|
||||
"requires": {
|
||||
"bins": [
|
||||
"bash",
|
||||
"git",
|
||||
"jq",
|
||||
"gh"
|
||||
]
|
||||
},
|
||||
"runtime": {
|
||||
"required_env": [
|
||||
"GH_TOKEN or existing gh auth"
|
||||
|
||||
+1515
-1172
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
jPrlTYwicRwoQgTs5Rk3Y3g6Lz78jNRs9ZNf0R09M4jkJokZENxfvhvHphI9MH4u+7wv0sFZ+yZbQtJ42y+hCQ==
|
||||
agiAAFvzM1vNHxH2+bGtyeKqFScLWJHnNreBcPpTODUqD0xqFi0cnyP/ZaZX+Rsw1Y9uZ7pGdFdA93pD4lh2BQ==
|
||||
Reference in New Issue
Block a user