From 65c40f67d91748e4afcb4541be0c475e0f5e954a Mon Sep 17 00:00:00 2001 From: davida-ps Date: Mon, 16 Feb 2026 15:00:43 +0100 Subject: [PATCH] Feat/codescan (#27) * feat: add Dependabot configuration for GitHub Actions, npm, and pip updates feat: implement CodeQL analysis workflow for security scanning fix: update permissions in community advisory workflow for better access control fix: adjust permissions in poll NVD CVEs workflow for enhanced functionality fix: update Scorecard workflow to use specific version of upload-sarif action fix: refine permissions in skill release workflow for improved security and functionality * feat: add guidance documentation for agents and development setup * Update .github/workflows/codeql.yml Co-authored-by: baz-reviewer[bot] <174234987+baz-reviewer[bot]@users.noreply.github.com> --------- Co-authored-by: baz-reviewer[bot] <174234987+baz-reviewer[bot]@users.noreply.github.com> --- .github/dependabot.yml | 19 ++++ .github/workflows/ci.yml | 10 +- .github/workflows/codeql.yml | 41 ++++++++ .github/workflows/community-advisory.yml | 9 +- .github/workflows/poll-nvd-cves.yml | 7 +- .github/workflows/scorecard.yml | 2 +- .github/workflows/skill-release.yml | 15 +-- AGENTS.md | 12 +++ CLAUDE.md | 116 +++++++++++++++++++++++ 9 files changed, 211 insertions(+), 20 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c9b2d50 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + + - package-ecosystem: "pip" + directory: "/.github" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4378c33..29d14bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,8 @@ on: push: branches: [main] +permissions: read-all + jobs: lint-typescript: name: Lint TypeScript/React @@ -32,14 +34,10 @@ jobs: - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.12' - cache: 'pip' - cache-dependency-path: '.github/requirements-lint-python.txt' - - name: Install linters - run: python -m pip install -r .github/requirements-lint-python.txt - name: Ruff (lint + format check) - run: ruff check utils/ --output-format=github + run: pipx run --spec "ruff==0.6.9" ruff check utils/ --output-format=github - name: Bandit (security) - run: bandit -r utils/ -ll + run: pipx run --spec "bandit==1.7.9" bandit -r utils/ -ll lint-shell: name: Lint Shell Scripts diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..55aa74c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: "17 3 * * 1" + +permissions: read-all + +jobs: + analyze: + name: Analyze (CodeQL) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: ["javascript-typescript"] + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4 + with: + languages: ${{ matrix.language }} + + - name: Install dependencies + run: npm ci + + - name: Build project + run: npm run build + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4 diff --git a/.github/workflows/community-advisory.yml b/.github/workflows/community-advisory.yml index 3ef543f..5b3cbc3 100644 --- a/.github/workflows/community-advisory.yml +++ b/.github/workflows/community-advisory.yml @@ -4,10 +4,7 @@ on: issues: types: [labeled] -permissions: - contents: write - issues: write - pull-requests: write +permissions: read-all concurrency: group: community-advisory @@ -23,6 +20,10 @@ jobs: process-advisory: if: github.event.label.name == 'advisory-approved' runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/poll-nvd-cves.yml b/.github/workflows/poll-nvd-cves.yml index 3bee19f..6f798ed 100644 --- a/.github/workflows/poll-nvd-cves.yml +++ b/.github/workflows/poll-nvd-cves.yml @@ -12,9 +12,7 @@ on: default: 'false' type: boolean -permissions: - contents: write - pull-requests: write +permissions: read-all concurrency: group: poll-nvd-cves @@ -31,6 +29,9 @@ env: jobs: poll-and-update: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ec9b53b..d4e34df 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -73,6 +73,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@a4e1a019f5e24960714ff6296aee04b736cbc3cf # v3.29.6 with: sarif_file: results.sarif diff --git a/.github/workflows/skill-release.yml b/.github/workflows/skill-release.yml index 1befd4c..c17e228 100644 --- a/.github/workflows/skill-release.yml +++ b/.github/workflows/skill-release.yml @@ -15,10 +15,7 @@ on: required: true type: string -permissions: - contents: write - pages: write - id-token: write +permissions: read-all concurrency: group: skill-release-${{ github.ref }} @@ -505,6 +502,8 @@ jobs: release-tag: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest + permissions: + contents: write outputs: skill_name: ${{ steps.parse.outputs.skill_name }} version: ${{ steps.parse.outputs.version }} @@ -947,6 +946,8 @@ jobs: needs: release-tag runs-on: ubuntu-latest continue-on-error: true + permissions: + contents: read env: CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }} steps: @@ -968,7 +969,7 @@ jobs: - name: Install clawhub CLI if: needs.release-tag.outputs.publishable == 'true' && env.CLAWHUB_TOKEN != '' - run: npm install -g clawhub + run: npm install -g clawhub@0.7.0 - name: Login to ClawHub if: needs.release-tag.outputs.publishable == 'true' && env.CLAWHUB_TOKEN != '' @@ -1022,6 +1023,8 @@ jobs: # Usage: Go to Actions → Skill Release → Run workflow → Enter tag name if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + permissions: + contents: read env: CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }} steps: @@ -1077,7 +1080,7 @@ jobs: node-version: 20 - name: Install clawhub CLI - run: npm install -g clawhub + run: npm install -g clawhub@0.7.0 - name: Login to ClawHub run: | diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f3da321 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,12 @@ +- Delete unused or obsolete files when your changes make them irrelevant (refactors, feature removals, etc.), and revert files only when the change is yours or explicitly requested. If a git operation leaves you unsure about other agents' in-flight work, stop and coordinate instead of deleting. +- **Before attempting to delete a file to resolve a local type/lint failure, stop and ask the user.** Other agents are often editing adjacent files; deleting their work to silence an error is never acceptable without explicit approval. +- NEVER edit `.env` or any environment variable files—only the user may change them. +- Coordinate with other agents before removing their in-progress edits—don't revert or delete work you didn't author unless everyone agrees. +- Moving/renaming and restoring files is allowed. +- ABSOLUTELY NEVER run destructive git operations (e.g., `git reset --hard`, `rm`, `git checkout`/`git restore` to an older commit) unless the user gives an explicit, written instruction in this conversation. Treat these commands as catastrophic; if you are even slightly unsure, stop and ask before touching them. *(When working within Cursor or Codex Web, these git limitations do not apply; use the tooling's capabilities as needed.)* +- Never use `git restore` (or similar commands) to revert files you didn't author—coordinate with other agents instead so their in-progress work stays intact. +- Always double-check git status before any commit +- Keep commits atomic: commit only the files you touched and list each path explicitly. For tracked files run `git commit -m "" -- path/to/file1 path/to/file2`. For brand-new files, use the one-liner `git restore --staged :/ && git add "path/to/file1" "path/to/file2" && git commit -m "" -- path/to/file1 path/to/file2`. +- Quote any git paths containing brackets or parentheses (e.g., `src/app/[candidate]/**`) when staging or committing so the shell does not treat them as globs or subshells. +- When running `git rebase`, avoid opening editors—export `GIT_EDITOR=:` and `GIT_SEQUENCE_EDITOR=:` (or pass `--no-edit`) so the default messages are used automatically. +- Never amend commits unless you have explicit written approval in the task thread. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..338ca97 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,116 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Setup + +```bash +npm install # install JS dependencies +npm run dev # start Vite dev server on http://localhost:3000 +npm run build # production build to dist/ +``` + +Python environment (use `uv`, not raw `pip`): + +```bash +uv venv # create .venv in repo root +source .venv/bin/activate +uv pip install ruff bandit # linters configured in pyproject.toml +``` + +Required tools: Node 20+, Python 3.10+, openssl, jq, shellcheck (`brew install shellcheck`). + +## Common Commands + +**Pre-push validation** (mirrors CI — run before pushing): + +```bash +./scripts/prepare-to-push.sh # lint, typecheck, build, security scans +./scripts/prepare-to-push.sh --fix # auto-fix where possible +``` + +**Lint:** + +```bash +npx eslint . --ext .ts,.tsx,.js,.jsx,.mjs --max-warnings 0 # JS/TS +ruff check utils/ # Python +bandit -r utils/ -ll # Python security +``` + +**Tests** (vanilla Node.js — no framework, no npm test script): + +```bash +node skills/clawsec-suite/test/feed_verification.test.mjs +node skills/clawsec-suite/test/guarded_install.test.mjs +node skills/clawsec-suite/test/skill_catalog_discovery.test.mjs +``` + +**Validate a skill's structure:** + +```bash +python utils/validate_skill.py skills/ +``` + +**Signing key consistency check:** + +```bash +./scripts/ci/verify_signing_key_consistency.sh +``` + +**Populate local dev data:** + +```bash +./scripts/populate-local-skills.sh # build public/skills/index.json from local skills/ +./scripts/populate-local-feed.sh --days 120 # fetch real NVD CVE data for local advisory feed +``` + +## Releasing a Skill + +```bash +./scripts/release-skill.sh [--force-tag] +# Example: ./scripts/release-skill.sh clawsec-feed 0.0.5 +``` + +- **Feature branch:** bumps version in skill.json + SKILL.md frontmatter, commits. No tag. +- **Main branch:** same + creates annotated git tag + GitHub release with changelog. +- Tag format: `-v` (e.g., `clawsec-suite-v0.1.0`). +- Pushing the tag triggers the `skill-release.yml` workflow (sign, package, publish). + +## Architecture + +**Frontend:** React 19 + TypeScript + Vite, deployed to GitHub Pages. Hash-based routing. Tailwind via CDN. + +**Skills:** Each skill lives in `skills//` with: +- `skill.json` — metadata, SBOM (file manifest), OpenClaw config (emoji, triggers, required bins) +- `SKILL.md` — YAML frontmatter (`name`, `version`, `description`) + agent-readable markdown +- Version in `skill.json` and `SKILL.md` frontmatter must match (CI enforced) + +**clawsec-suite** is the meta-skill ("skill-of-skills") that installs and manages other skills. It embeds: +- Advisory feed with Ed25519 signature verification (`hooks/clawsec-advisory-guardian/`) +- Guarded skill installer with two-stage approval for advisory-flagged skills +- Dynamic catalog discovery from `https://clawsec.prompt.security/skills/index.json` with local fallback + +**Signing:** Single Ed25519 keypair for everything (feed + releases). +- Private key lives only in GitHub secret `CLAWSEC_SIGNING_PRIVATE_KEY` — never committed. +- Public key committed in three canonical locations: `clawsec-signing-public.pem`, `advisories/feed-signing-public.pem`, `skills/clawsec-suite/advisories/feed-signing-public.pem`. +- `SKILL.md` embeds the same key inline for offline installation verification. +- Drift guard: `scripts/ci/verify_signing_key_consistency.sh` enforces all references resolve to the same fingerprint. Runs on every PR and tag push. + +## CI Workflows + +| Workflow | Trigger | What it does | +|---|---|---| +| `ci.yml` | PR / push to main | Lint (TS, Python, shell), Trivy security scan, npm audit, tests, build | +| `skill-release.yml` | Tag `*-v*.*.*` or PR touching skill files | Sign checksums, publish to GitHub Releases, supersede old versions | +| `deploy-pages.yml` | After CI or release succeeds | Build web frontend + skills catalog, deploy to GitHub Pages | +| `poll-nvd-cves.yml` | Daily 06:00 UTC | Poll NVD for CVEs, update `advisories/feed.json` + signature | +| `community-advisory.yml` | Issue labeled `advisory-approved` | Process community report into `CLAW-YYYY-NNNN` advisory | + +## Key Conventions + +- **ESLint:** flat config (`eslint.config.js`), zero warnings policy +- **Python:** ruff + bandit, configured in `pyproject.toml`, line-length 120 +- **Shell:** shellcheck on `scripts/*.sh` +- **Tests:** each `.test.mjs` is a standalone Node.js script with its own pass/fail counters and `process.exit(1)` on failure. Tests generate ephemeral Ed25519 keys — they don't use the repo signing keys. +- **Advisory feed:** fail-closed signature verification by default. `CLAWSEC_ALLOW_UNSIGNED_FEED=1` is a temporary migration bypass only. +- **Hook event model:** hooks mutate `event.messages` array in-place (not return values). Rate-limited to 300s by default (`CLAWSEC_HOOK_INTERVAL_SECONDS`).