From 52710805c48223f8530ff651a3a40ec6252cf604 Mon Sep 17 00:00:00 2001 From: "Harun R. Rayhan" Date: Wed, 4 Nov 2026 13:00:03 +0600 Subject: [PATCH] v1.2.0: cve-review, a CVE and end-of-life review --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- .cursor-plugin/plugin.json | 2 +- CHANGELOG.md | 10 ++ README.md | 9 +- VERSION | 2 +- docs/agents.md | 2 +- docs/guide.md | 4 +- install.sh | 8 +- plugin.json | 4 +- skills/app-security-review/SKILL.md | 2 +- skills/audit/SKILL.md | 16 +-- skills/cve-review/SKILL.md | 101 ++++++++++++++++++ skills/cve-review/references/checklist.md | 123 ++++++++++++++++++++++ skills/raystack/references/scans.md | 8 +- tests/check-linkage.sh | 2 +- tests/test-install.sh | 40 +++---- 17 files changed, 289 insertions(+), 48 deletions(-) create mode 100644 skills/cve-review/SKILL.md create mode 100644 skills/cve-review/references/checklist.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c62429f..1153ece 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "raystack", "source": "./", - "version": "1.1.1", + "version": "1.2.0", "description": "Skills by Harun R. Rayhan: a full development flow (new project intake, research, PRD, specs, plans, reviewed builds, ship; gap-fill, design review and migration for existing projects), read-only scans and reviews (security, performance, scalability, reliability and availability, architecture incl. MVC and CQRS, frontend, code health, CI/CD pipelines, AWS, infrastructure, databases), working principles and local browser verification.", "category": "devops" } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 398db8d..c22cc5a 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "raystack", - "version": "1.1.1", + "version": "1.2.0", "description": "Skills by Harun R. Rayhan: a full development flow (new project intake, research, PRD, specs, plans, reviewed builds, ship; gap-fill, design review and migration for existing projects), read-only scans and reviews (security, performance, scalability, reliability and availability, architecture incl. MVC and CQRS, frontend, code health, CI/CD pipelines, AWS, infrastructure, databases), working principles and local browser verification.", "author": { "name": "Harun R. Rayhan", diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index 6d46d0e..dbcb924 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "raystack", "displayName": "RayStack", - "version": "1.1.1", + "version": "1.2.0", "description": "Skills by Harun R. Rayhan: a full development flow (new project intake, research, PRD, specs, plans, reviewed builds, ship; gap-fill, design review and migration for existing projects), read-only scans and reviews (security, performance, scalability, reliability and availability, architecture incl. MVC and CQRS, frontend, code health, CI/CD pipelines, AWS, infrastructure, databases), working principles and local browser verification.", "author": { "name": "Harun R. Rayhan" diff --git a/CHANGELOG.md b/CHANGELOG.md index a538075..4b7c60e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.2.0 (2026-11-04) + +- New `cve-review`: finds known CVEs in dependencies (osv-scanner and the ecosystem audits, + with a check that your code reaches the vulnerable function) and software that will + collect CVEs: end-of-life runtimes and frameworks, old or floating base images, unsupported + cloud versions, vendored libraries, and no update process. End-of-life dates and the CISA + known-exploited list are looked up only with consent. Image scans are listed for you to + run, not run. Run it with `/audit cve`; `/audit security` and `/audit all` include it. + RayStack is now thirty-two skills. + ## 1.1.1 (2026-11-04) - The installer knows OpenClaw (`--agent openclaw`, `~/.openclaw/skills`) and its earlier diff --git a/README.md b/README.md index d9b00a9..c382d66 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ file:line evidence and a suggested fix. They never change your code or anything | Review | What it looks at | | --- | --- | | `app-security-review` | Authorization and IDOR, injection, secrets, sessions, client IP spoofing, dependency risk | +| `cve-review` | Known CVEs in dependencies, and software that will collect them: end-of-life runtimes and frameworks, old base images, unpinned or vendored code, no update process | | `pipeline-security-review` | CI/CD: workflow permissions, untrusted input, pinned actions, Dockerfiles, supply chain | | `infra-review` | Deploys, rollback, backups, monitoring and exposure on any platform (Railway, Cloudflare, VPS, Kubernetes, AWS, GCP, Azure) | | `performance-review` | N+1 queries, hot paths, blocking calls, caching, payload and bundle weight | @@ -82,7 +83,7 @@ live data `(unconfirmed)`. Each report ends with the commands it needs you to ru **Scan.** `/audit all` or `/audit ` runs a set of reviews as one job with one round of questions. Types: `security`, `performance`, `scalability`, `reliability`, `infra`, `aws`, -`database`, `architecture`, `code`, `frontend` and `all`. When two or more reviews run, it also +`database`, `cve`, `architecture`, `code`, `frontend` and `all`. When two or more reviews run, it also writes one merged, ranked report. What each type runs is in the [guide](docs/guide.md#scans). @@ -476,7 +477,7 @@ above). Start a new agent session afterwards. ## Skills -RayStack is thirty-one skills. You only need `/raystack` (or `/dev` to build and `/audit` to review); they call the rest. +RayStack is thirty-two skills. You only need `/raystack` (or `/dev` to build and `/audit` to review); they call the rest. | Skill | What it does | | --- | --- | @@ -491,7 +492,7 @@ RayStack is thirty-one skills. You only need `/raystack` (or `/dev` to build and | `dev-gaps` | Finds the docs an existing project is missing and writes them from the code and your answers | | `dev-review` | Read-only check of the architecture, specs and plan against the code | | `dev-migrate` | Moves part of a project to another language, framework or database, step by step with parity tests | -| 13 reviews | See [What it does](#what-it-does) | +| 14 reviews | See [What it does](#what-it-does) | | `aws-readonly-setup` | Creates a read-only AWS profile for the AWS reviews | | `local-verify` | Checks a user-facing change in a real local browser before it ships | | `principle-ship` | Done means live: PR, self-review, green CI, merge, deploy confirmed | @@ -524,7 +525,7 @@ Or in a terminal: rm -rf ~/.raystack ~/.raystack-installs # then the checkout and its list ``` -`--uninstall` removes exactly the thirty-one RayStack skills and nothing else. +`--uninstall` removes exactly the thirty-two RayStack skills and nothing else. ## Credits diff --git a/VERSION b/VERSION index 524cb55..26aaba0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.1 +1.2.0 diff --git a/docs/agents.md b/docs/agents.md index eef70c5..9c6437b 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -1,6 +1,6 @@ # Agent support -RayStack ships the same thirty-one skills (`skills//SKILL.md`) to every agent. Each agent gets +RayStack ships the same thirty-two skills (`skills//SKILL.md`) to every agent. Each agent gets a manifest only when it has a documented plugin format. Researched 2026-10-28. Anything marked **unverified** was not confirmed against official docs or a local run. diff --git a/docs/guide.md b/docs/guide.md index 4606644..1754d33 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -150,6 +150,7 @@ It offers a PDF at the end. | `scaling-review` | What breaks first at 10x and 100x load | | `codebase-review` | Test gaps on core paths, big files that change often, dead code, duplicated business rules, swallowed errors, outdated or abandoned dependencies | | `app-security-review` | Authorization and IDOR, injection, secrets in code and git history, sessions, security logging, vulnerable dependencies | +| `cve-review` | Known CVEs in dependencies (osv-scanner and the ecosystem audits, with reach checked in your code), end-of-life runtimes and frameworks, base images, pinned cloud versions, vendored libraries, and whether anything opens update PRs. Image scans (`trivy`) are listed for you to run, not run | | `pipeline-security-review` | GitHub Actions and GitLab CI, pinned actions, workflow permissions, Dockerfiles, IaC, lockfiles and supply chain | | `infra-review` | Deploys, rollback, backups, monitoring and exposure on any platform | | `aws-billing-review`, `aws-security-review`, `aws-bottleneck-review` | AWS spend ($/mo savings), IAM and exposure, throttling and limits | @@ -170,7 +171,7 @@ or more ran, it also writes one merged, ranked summary, `reviews/YYYY-MM-DD-scan | Type | Runs | | --- | --- | -| `security` | app security, pipeline security, AWS security, infra | +| `security` | app security, CVEs, pipeline security, AWS security, infra | | `performance` | performance, database, AWS bottlenecks | | `scalability` | scaling, database | | `reliability` | reliability, database, scaling, infra | @@ -180,6 +181,7 @@ or more ran, it also writes one merged, ranked summary, `reviews/YYYY-MM-DD-scan | `infra` | infra, pipeline security, AWS security, AWS bottlenecks | | `aws` | the three AWS reviews | | `database` | database | +| `cve` | cve | | `all` | every review that fits the repo | ## What RayStack writes diff --git a/install.sh b/install.sh index 5568466..787ab11 100755 --- a/install.sh +++ b/install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Install (or remove) the thirty-one RayStack skills for any agent that reads SKILL.md folders. +# Install (or remove) the thirty-two RayStack skills for any agent that reads SKILL.md folders. set -eu # Resolve symlinks to this script so ROOT is the real checkout. @@ -11,14 +11,14 @@ done ROOT=$(cd "$(dirname "$self")" && pwd -P) # Skills earlier versions shipped; removed from the target only when RayStack installed them. RETIRED="blog blog-topics blog-draft humanize blog-cover" -SKILLS="raystack audit aws-billing-review aws-security-review aws-bottleneck-review infra-review scaling-review database-review app-security-review performance-review pipeline-security-review aws-readonly-setup principle-ship principle-smallest-change principle-ask-with-question-tool principle-secrets-layout local-verify dev dev-hub dev-research dev-prd dev-spec dev-plan dev-build dev-gaps dev-review dev-migrate architecture-review frontend-review reliability-review codebase-review" +SKILLS="raystack audit aws-billing-review aws-security-review aws-bottleneck-review infra-review scaling-review database-review app-security-review performance-review pipeline-security-review aws-readonly-setup principle-ship principle-smallest-change principle-ask-with-question-tool principle-secrets-layout local-verify dev dev-hub dev-research dev-prd dev-spec dev-plan dev-build dev-gaps dev-review dev-migrate architecture-review frontend-review reliability-review codebase-review cve-review" usage() { cat <<'USAGE' Usage: install.sh [--agent all||custom] [--dir ] [--project []] [--link] [--uninstall] [--update] [--dry-run] [--yes] [--force] [-h|--help] -Copies the thirty-one RayStack skills into an agent's skills directory (default agent: claude). +Copies the thirty-two RayStack skills into an agent's skills directory (default agent: claude). Agents: claude codex cursor grok antigravity gemini copilot windsurf cline kiro roo kilo amp goose opencode qwen trae junie augment warp zed openhands continue openclaw clawdbot moltbot. @@ -30,7 +30,7 @@ clawdbot moltbot. --update pull the latest RayStack into this checkout, then reinstall every place this checkout installed to (global and project); other flags are ignored --link symlink to this checkout instead of copying - --uninstall remove exactly the thirty-one RayStack skills from the target + --uninstall remove exactly the thirty-two RayStack skills from the target --dry-run print what would happen, change nothing --force also replace/remove same-named folders RayStack did not install Same-named folders without the .raystack-installed marker (or a link into this checkout) diff --git a/plugin.json b/plugin.json index 7680cd3..4c833a0 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { - "$schema": "https://agent-plugins.org/schemas/1.1.1/plugin.schema.json", + "$schema": "https://agent-plugins.org/schemas/1.2.0/plugin.schema.json", "name": "raystack", - "version": "1.1.1", + "version": "1.2.0", "description": "Skills by Harun R. Rayhan: a full development flow (new project intake, research, PRD, specs, plans, reviewed builds, ship; gap-fill, design review and migration for existing projects), read-only scans and reviews (security, performance, scalability, reliability and availability, architecture incl. MVC and CQRS, frontend, code health, CI/CD pipelines, AWS, infrastructure, databases), working principles and local browser verification.", "author": { "name": "Harun R. Rayhan", diff --git a/skills/app-security-review/SKILL.md b/skills/app-security-review/SKILL.md index 25a1b9c..faf23e7 100644 --- a/skills/app-security-review/SKILL.md +++ b/skills/app-security-review/SKILL.md @@ -79,7 +79,7 @@ Work through `references/checklist.md`, every area, with the framework notes tha goes under a "Worth hardening" section after the findings, never ranked as a finding. Framework defaults (CSRF tokens, template escaping, ORM binding) are not findings unless the code shows them disabled or bypassed. -- Dependency CVEs: confirm the app's own code reaches the vulnerable function before ranking +- Dependency CVEs: `cve-review` goes deeper (end-of-life runtimes, images, cloud versions); when its report exists, add a one-line `see ` instead of repeating. Confirm the app's own code reaches the vulnerable function before ranking it. Otherwise it goes under "Worth hardening" with the upgrade. - Logging findings (area 12) use a **Blind spot** line (the attack that would go unseen) instead of **Exploit**, and rank at most Medium. diff --git a/skills/audit/SKILL.md b/skills/audit/SKILL.md index 6c9b8a1..b3b1213 100644 --- a/skills/audit/SKILL.md +++ b/skills/audit/SKILL.md @@ -1,6 +1,6 @@ --- name: audit -description: Entry point for RayStack's read-only reviews and scans. Use for "/audit", "/audit all", "/audit security", "audit my stack", "review my codebase", "which review should I run", "review my AWS", "review my database", cloud cost, security, bottlenecks, deploys, scaling, databases, app code security, performance, N+1 queries, CI/CD or GitHub Actions security, architecture, CQRS, MVC, frontend, reliability, availability, code quality or tech debt, and scans that run several reviews as one job ("scan", "scan all", "scan security"). +description: Entry point for RayStack's read-only reviews and scans. Use for "/audit", "/audit all", "/audit security", "audit my stack", "review my codebase", "which review should I run", "CVE scan", "review my AWS", "review my database", cloud cost, security, bottlenecks, deploys, scaling, databases, app code security, performance, N+1 queries, CI/CD or GitHub Actions security, architecture, CQRS, MVC, frontend, reliability, availability, code quality or tech debt, and scans that run several reviews as one job ("scan", "scan all", "scan security"). --- # Audit @@ -15,10 +15,10 @@ read-only. | `/audit ` (security, database, frontend, architecture, AWS cost...) | That review, step 2 skips the pick | | `/audit all` or `/audit ` | A scan: a set of reviews as one job (Scan below) | -The thirteen reviews: `aws-billing-review`, `aws-security-review`, `aws-bottleneck-review`, +The fourteen reviews: `aws-billing-review`, `aws-security-review`, `aws-bottleneck-review`, `infra-review`, `scaling-review`, `database-review`, `app-security-review`, `performance-review`, `pipeline-security-review`, `architecture-review`, `frontend-review`, -`reliability-review`, `codebase-review`. Read-only AWS access: `aws-readonly-setup`. +`reliability-review`, `codebase-review`, `cve-review`. Read-only AWS access: `aws-readonly-setup`. ## 0. Scan or review @@ -39,7 +39,8 @@ names only and are fine), `*.tfstate*`, kubeconfigs or credential files. or IaC: `database-review`. Queues, workers, autoscaling config: `scaling-review`. - App code (routes, controllers, an ORM): `app-security-review`; with an ORM or a frontend build also `performance-review`. -- CI workflows, a `Dockerfile`, IaC or k8s: `pipeline-security-review`. +- CI workflows, a `Dockerfile`, IaC or k8s: `pipeline-security-review`. A lockfile, a runtime + version file, a `Dockerfile` or pinned cloud versions: `cve-review`. - App code: also `architecture-review` and `codebase-review`. A frontend build (React, Vue, Svelte, Angular, Next, Nuxt, Inertia): `frontend-review`. App code with outbound calls, queues, jobs or deploy config: `reliability-review`. @@ -50,7 +51,8 @@ Then read the user's words: `aws-security-review`. "slow", "throttling", "limits": `aws-bottleneck-review`. - "slow page", "slow endpoint", "N+1": `performance-review`. "10x", "launch", "will it scale": `scaling-review`. -- "security audit of my app", "IDOR", "XSS", "SQL injection": `app-security-review`. +- "security audit of my app", "IDOR", "XSS", "SQL injection": `app-security-review`. "CVE", + "vulnerable dependencies", "end of life", "known vulnerabilities": `cve-review`. - "architecture", "CQRS", "MVC", "layers", "god class", "coupling": `architecture-review`. "frontend", "React", "Vue", "accessibility", "re-renders": `frontend-review`. "reliability", "availability", "uptime", "SLO", "retries", "failover": `reliability-review`. "code quality", @@ -69,7 +71,7 @@ Use plain text only without such a tool. It allows up to 4 questions with up to 1. "AWS and CI reviews" (multi-select): the three `aws-*` reviews, `pipeline-security-review`. 2. "Code reviews" (multi-select): `app-security-review`, `performance-review`, - `architecture-review`, `codebase-review`, `frontend-review`. + `architecture-review`, `codebase-review`, `frontend-review`, `cve-review`. 3. "Platform reviews" (multi-select): `infra-review`, `scaling-review`, `database-review`, `reliability-review`. @@ -108,7 +110,7 @@ reference, not a second entry". Run `database-review` before `infra-review`. ## Scan `scan ` (security, performance, scalability, reliability, architecture, code, frontend, -infra, aws, database, all) runs steps 1, 3 and 4 on +infra, aws, database, cve, all) runs steps 1, 3 and 4 on the type's review list. Type and consent: [scans.md](../raystack/references/scans.md), which replaces step 2 (no review pick) with one question-tool call. It also has the table, skipped reviews and the combined `reviews/YYYY-MM-DD-scan-.md` (skill `scan-`). diff --git a/skills/cve-review/SKILL.md b/skills/cve-review/SKILL.md new file mode 100644 index 0000000..2bd9518 --- /dev/null +++ b/skills/cve-review/SKILL.md @@ -0,0 +1,101 @@ +--- +name: cve-review +description: Read-only review of known and likely vulnerabilities (CVEs) in your dependencies, runtimes, container images and cloud versions, with a ranked findings report. Use for "CVE scan", "vulnerable dependencies", "known vulnerabilities", "am I affected by", "outdated packages with security issues", "end of life runtime", "unsupported PHP or Node version", "base image vulnerabilities", "could we have a CVE", "npm audit or composer audit", "SBOM", or a Log4Shell or Heartbleed style check. +hooks: + PreToolUse: + - matcher: Bash + hooks: + - type: command + command: 'for f in "${CLAUDE_PLUGIN_ROOT}/skills/raystack/hooks/readonly-guard.sh" "${CLAUDE_PLUGIN_ROOT}/../raystack/hooks/readonly-guard.sh" "$HOME/.claude/skills/raystack/hooks/readonly-guard.sh"; do [ -x "$f" ] && exec "$f"; done; echo "RayStack review is read-only: guard missing" >&2; exit 2' +--- + +# cve-review + +Find where the system runs software with a known CVE, and where it runs software that +will collect CVEs nobody patches (end of life, unpinned, vendored, no update process). +Report only. Never upgrade a package, edit a lockfile or change a version. + +## 1. Read the contract + +Read `../raystack/references/review-contract.md` first and follow it for inputs, live consent, +the read-only rules, questions, the report and client data. This file only adds what is +specific to CVEs. + +## 2. Detect what is there + +| Files | Checklist area | +| --- | --- | +| `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `composer.lock`, `Gemfile.lock`, `poetry.lock`, `uv.lock`, `requirements*.txt`, `go.sum`, `Cargo.lock`, `pom.xml`, `gradle.lockfile` | A. Known CVEs | +| `package.json`, `composer.json`, `Gemfile`, `pyproject.toml`, `go.mod`, `.nvmrc`, `.tool-versions`, `.python-version`, `runtime.txt` | A (ranges), B. Runtimes and frameworks | +| `Dockerfile*`, `docker-compose*`, k8s manifests, `Chart.yaml` | C. Containers and OS | +| `*.tf`, CloudFormation, CDK, `serverless.yml`, `app.yaml` | D. Managed versions | +| `.github/dependabot.yml`, `renovate.json`, CI workflows | E. Update process | +| `vendor/` or `public/` with bundled libraries, `wp-content/plugins/` | E. Vendored code | + +Read `README`, `docs/`, `SECURITY.md`, `CLAUDE.md`/`AGENTS.md` for what is internet-facing +and what is internal: reachability decides severity. + +## 3. Ask for consent + +Audits and osv-scanner send the package list to public advisory registries, and an end-of-life +lookup sends product names and versions to endoflife.date, so they are live calls. Ask once, +per the contract, with one batched call to the host agent's question tool: +- whether the dependency audits and osv-scanner may run, unless the router passed it +- whether end-of-life dates and the CISA known-exploited list may be looked up on the web + (only product names, versions and CVE ids are sent), unless the router passed it +- which services face the internet, if the repo does not say + +Declined: list the commands under "Commands for you to run". Without a lookup, mark every +end-of-life or known-exploited claim `(unconfirmed, from memory)`. + +## 4. Run the scanners + +Use the forms in `../raystack/references/scanners.md` exactly, and the audit forms in +`../app-security-review/SKILL.md` section 2 for each lockfile. Never install a scanner. +- osv-scanner on every lockfile (it covers all ecosystems above), plus the ecosystem audit + (`npm audit --omit=dev`, `composer audit --no-plugins --no-scripts`, `pip-audit`, + `bundle-audit check`) when present. Count a package once. +- Missing ("command not found"): say so under "Inputs used / missing", put the install line + under "Commands for you to run" and check by reading. +- Image and filesystem scans (`trivy image IMAGE:TAG`, `grype IMAGE:TAG`) are never run: list + them under "Commands for you to run" for each image the repo builds or deploys. +- An empty or failed scan is not a clean result. Say so. + +## 5. Work the checklist + +Work `references/checklist.md`, every area that has files. + +- **Finding bar for a known CVE.** Name the advisory id, the package and installed version, the + fixed version, and whether the app's own code reaches the vulnerable function or the + package is a build or dev tool only. An advisory with no reach stays a finding, rated by its + place in the system, not dropped. Group many advisories in one package into one finding. +- **Finding bar for a "likely CVE".** An end-of-life runtime, image or engine, or a version + with no security updates, is a finding with the end-of-life date and source. Do not invent + CVE ids for it. +- Severity guide: + - **Critical**: a CVE that is unauthenticated remote code execution, auth bypass or data + exposure, in a production dependency, on an internet-facing path the code reaches, or + listed as known exploited. + - **High**: a high or critical CVE in a production dependency with reach unproven; an + end-of-life runtime or framework on an internet-facing service. + - **Medium**: a medium CVE in production, a high CVE only in a dev or build tool, an + end-of-life image on an internal service, no lockfile. + - **Low**: low CVEs, no update automation, floating tags with no known issue. +- Hand-offs, noted briefly: abandoned packages and outdated majors to `codebase-review`; + workflow and install-script supply chain to `pipeline-security-review`; code flaws to + `app-security-review`. If one of those reports already lists a CVE, add a one-line + `see ` instead of a second entry. +- Est. $/mo: leave the line out. + +## 6. Write the report + +Write the report per the contract with skill name `cve-review`, at +`reviews/YYYY-MM-DD-cve-review.md`. Under "Inputs used / missing", list the lockfiles and +runtimes found, the scanners run or missing, and whether lookups ran. In each finding add an +**Affected** line (package or runtime, installed version, fixed version or end-of-life date) +and **Reach** (reachable, unproven, build-only). After the findings add "Reviewed, found +clean". Put every image scan, missing install and declined lookup under "Commands for you to +run". + +In chat, show the top 3 findings and the report path, then offer the PDF unless the router +runs it (contract section 5). diff --git a/skills/cve-review/references/checklist.md b/skills/cve-review/references/checklist.md new file mode 100644 index 0000000..8ad6d23 --- /dev/null +++ b/skills/cve-review/references/checklist.md @@ -0,0 +1,123 @@ +# cve-review checklist + +Work every area that has files in the repo. Each item gives: what to check, the evidence (a +read-only search or file), the exploit or failure to look for, and the fix. + +Every search is one command that passes the read-only guard. Run one command per Bash call. +Write tool names inside a pattern with a bracket class (`[n]pm`, `[d]ocker`, `[a]ws`, `[h]elm`), +or the guard blocks the command. A recursive grep over `.` also takes `--exclude='.env*' +--exclude-dir=.secrets --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=vendor`. +Scanner and audit forms: `../../raystack/references/scanners.md` and +`../../app-security-review/SKILL.md` section 2; use them exactly. Never print secret values. + +End-of-life dates and known-exploited status come from the lookup in SKILL.md section 3. +Without it, say `(unconfirmed, from memory)`. Never state a CVE id you did not read in a scan +or on the lookup. + +## A. Known CVEs + +### 1. Dependencies with a published advisory +- **No installed production dependency has a known advisory.** Evidence: the osv-scanner and + audit output. For each hit record the advisory id, package, installed version, fixed + version and CVSS. Then check reach: `grep -rln 'PACKAGE' --include='*.js' --include='*.ts' --include='*.php' --include='*.py' --include='*.rb' --exclude-dir=node_modules --exclude-dir=vendor .` + and read how the vulnerable function is called. Exploit: the advisory's own description, + reached with input an outsider controls. Fix: upgrade to the fixed version, or pin a patched + fork, or remove the package. +- **Transitive hits name the parent.** Evidence: the scanner's dependency path, or + `grep -n 'PACKAGE' LOCKFILE`. Fix: upgrade the parent, or add an override or resolution to + the fixed version. +- **A dev or build tool with an advisory is rated by where it runs.** Evidence: the audit with + and without dev dependencies (`--omit=dev` output against the full list). Rate High only if + the tool runs on untrusted input in CI or on a deploy host. + +### 2. Versions that admit a vulnerable release +- **A lockfile exists and is committed.** Evidence: `git ls-files -- LOCKFILE`. Without it, + every install can pull a different, newer or compromised version, and a scan checks nothing + stable. Fix: commit the lockfile and install from it. +- **No `*`, `latest` or unbounded range on a production dependency.** Evidence: + `grep -nE '"[^"]+": *"([*]|latest|>=?[0-9.]+)"' package.json` and + `grep -nE '"[^"]+": *"([*]|dev-master|>=)' composer.json`. Fix: bounded ranges plus the + lockfile. +- **No dependency installed from a git branch or URL.** Evidence: + `grep -nE '(github:|git[+]|git://|https?://)' package.json composer.json requirements.txt`. + These skip registry advisories. Fix: a tagged release from the registry. + +## B. Runtimes and frameworks + +### 3. The language runtime is supported +- **Every runtime in use gets security fixes today.** Evidence: the version in `.nvmrc`, + `.node-version`, `.tool-versions`, `.python-version`, `runtime.txt`, `go.mod` (`go` line), + `composer.json` (`require.php`), `Gemfile` (`ruby`), `pom.xml` (`java.version`), the CI + setup step, and each Dockerfile `FROM`. Look up its end-of-life date. An unsupported + runtime gets no patch for the next CVE in the interpreter, TLS library or standard library. + Rate High when internet-facing. Fix: move to a supported version. +- **Local, CI and production agree.** Evidence: compare the files above and + `grep -rnE 'node-version|php-version|python-version' .github/workflows`. A newer CI than + production hides what production runs. + +### 4. The framework major gets security fixes +- **The web framework and its major version are still supported.** Evidence: installed + version in `composer.lock` (`laravel/framework`, `symfony/symfony`), `Gemfile.lock` + (`rails`), `package-lock.json` (`next`, `express`, `nuxt`), `poetry.lock` or + `requirements*.txt` (`django`, `flask`), `pom.xml` (Spring Boot). Look up its support end. + Fix: upgrade to a supported major. +- **Security-critical libraries are current.** Evidence: versions of the auth, session, JWT, + crypto, XML, YAML, image and archive libraries in the lockfile. An old major of these is + where past RCEs and signature bypasses live. Fix: upgrade, or replace. + +## C. Containers and OS + +### 5. Base images +- **Each `FROM` is a supported, patched image.** Evidence: `grep -n '^FROM' Dockerfile` + (and every `Dockerfile.*`). Flag an end-of-life distro or runtime tag (a `buster`, `stretch`, + `bionic`, `node:14`-style tag), and note the image's own end-of-life date. +- **No floating tag in a production image.** Evidence: the same grep: `latest`, a missing + tag, or a major-only tag. Exploit: a rebuild pulls different code, and a scan of the tag + means nothing later. Fix: a full version tag, ideally a digest, rebuilt on a schedule. +- **OS packages are upgraded at build, or the base is rebuilt often.** Evidence: + `grep -nE 'apt-get|apk|yum|dnf' Dockerfile`. A pinned old base with no upgrade step keeps + its OS CVEs. Fix: rebuild on a schedule, use a minimal or distroless base. + +### 6. Image scanning +- **Images are scanned before they ship.** Evidence: + `grep -rnE '[t]rivy|grype|snyk|scout' .github/workflows .gitlab-ci.yml`. None means nobody + finds OS or library CVEs inside the image. Fix: add an image scan to the build. +- Never run trivy or grype here. Put `trivy image IMAGE:TAG` for each image under "Commands + for you to run". + +## D. Managed versions in IaC + +### 7. Cloud runtimes and engines +- **No unsupported runtime or engine version is pinned.** Evidence: + `grep -rnE 'runtime|engine_version|kubernetes_version|cluster_version|node_version' --include='*.tf' --include='*.yml' --include='*.yaml' --include='*.json' .`. + Check Lambda and function runtimes, database engine versions (RDS, Cloud SQL), Kubernetes + versions (EKS, GKE, AKS), cache and search engine versions. Look up each end-of-life date. + Exploit: the platform stops patching or forces an upgrade. Fix: move to a supported version. + +## E. Update process and vendored code + +### 8. Updates are automatic and checked +- **A tool opens security update PRs.** Evidence: `.github/dependabot.yml` or + `renovate.json` exists and covers each ecosystem found in A. +- **CI fails on a new known advisory.** Evidence: an audit or scan step in a workflow: + `grep -rnE 'audit|[o]sv-scanner|[t]rivy|grype|snyk' .github/workflows .gitlab-ci.yml`. + None means a CVE found tomorrow reaches nobody. Fix: a scheduled audit job that opens an + issue. +- **Alerts are read.** If the repo is on GitHub, the user can check Dependabot alerts under + Security. List it under "Commands for you to run"; do not call the API unasked. + +### 9. Code no lockfile covers +- **Vendored and bundled libraries are known and current.** Evidence: + `grep -rlE 'jquery|bootstrap|lodash|moment|angular' --include='*.min.js' --exclude-dir=node_modules .` + and `ls vendor` when `vendor/` is committed. A copied library has no advisory feed. Read + its version header, then look it up. Fix: install it from a package manager and lock it. +- **CMS core and plugins are current.** Evidence: `wp-content/plugins/*/` plugin headers + (`Version:`), theme and core version files. Plugins are the common CVE path. Put + `wpscan --url SITE` under "Commands for you to run" when this applies. +- **No archived or unmaintained package carries an advisory.** Evidence: a scan hit whose + fixed version does not exist. Rate it by the advisory; hand the replacement to + `codebase-review`. + +## Reviewed, found clean + +Name each item above that you checked and found nothing, with the search that came back empty. diff --git a/skills/raystack/references/scans.md b/skills/raystack/references/scans.md index 7ffc0d0..ba4f191 100644 --- a/skills/raystack/references/scans.md +++ b/skills/raystack/references/scans.md @@ -5,20 +5,22 @@ when its signals are found (router step 1). Run the reviews in this order. | Type | Reviews, in run order | | --- | --- | -| security | `app-security-review`, `pipeline-security-review*`, `aws-security-review*`, `infra-review*` | +| security | `app-security-review`, `cve-review*`, `pipeline-security-review*`, `aws-security-review*`, `infra-review*` | | performance | `performance-review`, `database-review*`, `aws-bottleneck-review*` | | scalability | `scaling-review`, `database-review*` | | infra | `infra-review`, `pipeline-security-review*`, `aws-security-review*`, `aws-bottleneck-review*` | | aws | `aws-billing-review`, `aws-security-review`, `aws-bottleneck-review` | | database | `database-review` | +| cve | `cve-review` | | reliability | `reliability-review`, `database-review*`, `scaling-review*`, `infra-review*` | | architecture | `architecture-review`, `codebase-review` | | code | `codebase-review`, `architecture-review`, `frontend-review*` | | frontend | `frontend-review`, `performance-review*` | -| all | `app-security-review*`, `pipeline-security-review*`, `architecture-review*`, `codebase-review*`, `frontend-review*`, `performance-review*`, `database-review*`, `scaling-review*`, `reliability-review*`, `infra-review*`, `aws-security-review*`, `aws-bottleneck-review*`, `aws-billing-review*` | +| all | `app-security-review*`, `cve-review*`, `pipeline-security-review*`, `architecture-review*`, `codebase-review*`, `frontend-review*`, `performance-review*`, `database-review*`, `scaling-review*`, `reliability-review*`, `infra-review*`, `aws-security-review*`, `aws-bottleneck-review*`, `aws-billing-review*` | For `all`, every review is conditional: those with no signals are listed as skipped. Scaling signals: queues, workers, schedulers, autoscaling or replica-count config (step 1). +CVE signals: a lockfile, a runtime or framework version file, a `Dockerfile` or pinned cloud versions in IaC. Pipeline signals: CI workflows, a `Dockerfile`, IaC or a lockfile (a lockfile alone runs the supply-chain items). @@ -29,7 +31,7 @@ Skip step 2's review pick. Make ONE question-tool call with only what the mapped 1. "Scan type", only when the type is missing or unknown: `all` (Recommended), security, performance, and "Other" for the rest (scalability, reliability, architecture, code, - frontend, infra, aws, database). When this is + frontend, infra, aws, database, cve). When this is asked, size questions 2 to 4 for the reviews `all` would run here. 2. "Live access" as in step 2 item 4 (at most 4 options). Only offer AWS setup when AWS signals are found or the type is aws. diff --git a/tests/check-linkage.sh b/tests/check-linkage.sh index feed9f2..6f65958 100644 --- a/tests/check-linkage.sh +++ b/tests/check-linkage.sh @@ -5,7 +5,7 @@ cd "$(dirname "$0")/.." || exit 1 # shellcheck source=/dev/null . ./tests/lib.sh -REVIEWS=(aws-billing-review aws-security-review aws-bottleneck-review infra-review scaling-review database-review app-security-review performance-review pipeline-security-review architecture-review frontend-review reliability-review codebase-review) +REVIEWS=(aws-billing-review aws-security-review aws-bottleneck-review infra-review scaling-review database-review app-security-review performance-review pipeline-security-review architecture-review frontend-review reliability-review codebase-review cve-review) CONTRACT='../raystack/references/review-contract.md' # shellcheck disable=SC2016 # literal text expected in the frontmatter HOOK='for f in "${CLAUDE_PLUGIN_ROOT}/skills/raystack/hooks/readonly-guard.sh" "${CLAUDE_PLUGIN_ROOT}/../raystack/hooks/readonly-guard.sh" "$HOME/.claude/skills/raystack/hooks/readonly-guard.sh"; do [ -x "$f" ] && exec "$f"; done; echo "RayStack review is read-only: guard missing" >&2; exit 2' diff --git a/tests/test-install.sh b/tests/test-install.sh index 0117e44..72981a3 100644 --- a/tests/test-install.sh +++ b/tests/test-install.sh @@ -6,7 +6,7 @@ cd "$(dirname "$0")/.." || exit 1 . ./tests/lib.sh INSTALL=./install.sh if [ ! -f "$INSTALL" ]; then fail "install.sh missing"; exit 1; fi -SKILLS="raystack audit aws-billing-review aws-security-review aws-bottleneck-review infra-review scaling-review database-review app-security-review performance-review pipeline-security-review aws-readonly-setup principle-ship principle-smallest-change principle-ask-with-question-tool principle-secrets-layout local-verify dev dev-hub dev-research dev-prd dev-spec dev-plan dev-build dev-gaps dev-review dev-migrate architecture-review frontend-review reliability-review codebase-review" +SKILLS="raystack audit aws-billing-review aws-security-review aws-bottleneck-review infra-review scaling-review database-review app-security-review performance-review pipeline-security-review aws-readonly-setup principle-ship principle-smallest-change principle-ask-with-question-tool principle-secrets-layout local-verify dev dev-hub dev-research dev-prd dev-spec dev-plan dev-build dev-gaps dev-review dev-migrate architecture-review frontend-review reliability-review codebase-review cve-review" T=$(mktemp -d) trap 'rm -rf "$T"' EXIT export RAYSTACK_REGISTRY="$T/registry" @@ -16,7 +16,7 @@ count() { n=0; for s in $SKILLS; do [ -e "$1/$s/SKILL.md" ] && n=$((n + 1)); don # install twice into a dir that holds another skill D="$T/a"; mkdir -p "$D/other-skill"; echo keep > "$D/other-skill/SKILL.md" bash "$INSTALL" --dir "$D" >/dev/null; assert_eq "$?" 0 "install exits 0" -assert_eq "$(count "$D")" 31 "all thirty-one skills installed" +assert_eq "$(count "$D")" 32 "all thirty-two skills installed" assert_eq "$(cat "$D/other-skill/SKILL.md")" keep "other-skill intact" assert_eq "$(find "$D" -maxdepth 1 -name ".raystack-tmp*" | wc -l | tr -d " ")" 0 "no staging folder left behind" cp -R "$D" "$T/snap" @@ -31,7 +31,7 @@ assert_eq "$(gl "$IC")" 0 "installed guard allows its own gitleaks config" assert_eq "$(gl "$(pwd -P)/skills/raystack/references/gitleaks.toml")" 2 "installed guard denies the checkout's config" [ ! -e "$D/.denylist" ] && [ ! -e "$D/hooks" ] && [ ! -e "$D/scripts" ] && [ ! -e "$D/docs" ] && [ ! -e "$D/raystack/.denylist" ] \ && pass "nothing outside skills/ copied" || fail "extra files copied" -assert_eq "$(ls "$D" | wc -l | tr -d ' ')" 32 "target holds thirty-one skills plus other-skill" +assert_eq "$(ls "$D" | wc -l | tr -d ' ')" 33 "target holds thirty-two skills plus other-skill" # uninstall removes only ours bash "$INSTALL" --dir "$D" --uninstall >/dev/null @@ -42,7 +42,7 @@ assert_eq "$(ls "$D")" other-skill "uninstall left other-skill" L="$T/l" bash "$INSTALL" --dir "$L" --link >/dev/null 2>&1 links=0; for s in $SKILLS; do [ -L "$L/$s" ] && links=$((links + 1)); done -assert_eq "$links" 31 "--link makes thirty-one symlinks" +assert_eq "$links" 32 "--link makes thirty-two symlinks" bash "$INSTALL" --dir "$L" >/dev/null assert_eq "$([ -L "$L/raystack" ] && echo link || echo dir)" dir "copy replaces a symlink with a real dir" bash "$INSTALL" --dir "$L" --uninstall >/dev/null @@ -54,7 +54,7 @@ bash "$INSTALL" --dir "$T/dry" --dry-run >/dev/null bash "$INSTALL" --dir "$D" --uninstall --dry-run >/dev/null bash "$INSTALL" --dir "$D" >/dev/null bash "$INSTALL" --dir "$D" --uninstall --dry-run >/dev/null -assert_eq "$(count "$D")" 31 "--uninstall --dry-run removes nothing" +assert_eq "$(count "$D")" 32 "--uninstall --dry-run removes nothing" # refusals bash "$INSTALL" --dir / >/dev/null 2>&1; assert_eq "$?" 1 "refuses /" @@ -66,7 +66,7 @@ bash "$INSTALL" --agent nope >/dev/null 2>&1; assert_eq "$?" 1 "unknown agent" # agent default dir, caveat, works from another cwd H="$T/home"; mkdir -p "$H" out=$(cd "$T" && HOME="$H" bash "$OLDPWD/$INSTALL" --agent codex 2>&1) -assert_eq "$(count "$H/.agents/skills")" 31 "codex default dir" +assert_eq "$(count "$H/.agents/skills")" 32 "codex default dir" case "$out" in *docs/agents.md*) pass "prints guard caveat";; *) fail "no guard caveat";; esac case "$out" in *"type /raystack"*) pass "prints the next step";; *) fail "no next step";; esac case "$out" in *"grok plugin marketplace"*) fail "stale grok tip";; *) pass "no stale grok tip";; esac @@ -76,24 +76,24 @@ assert_eq "$(sed -n 2p "$T/portable/raystack/.raystack-installed")" "source=~/rs # --agent all installs for each agent found in $HOME; Grok only when it has no Claude or Cursor folder to read. HA="$T/homeall"; mkdir -p "$HA/.claude" "$HA/.codex" "$HA/.grok" outall=$(HOME="$HA" bash ./install.sh --agent all --yes 2>&1) -assert_eq "$(count "$HA/.claude/skills")" 31 "all: claude" -assert_eq "$(count "$HA/.agents/skills")" 31 "all: codex" +assert_eq "$(count "$HA/.claude/skills")" 32 "all: claude" +assert_eq "$(count "$HA/.agents/skills")" 32 "all: codex" [ ! -d "$HA/.grok/skills" ] && pass "all: grok reads ~/.claude/skills" || fail "all: grok got its own copy" case "$outall" in *"Installed for: Claude Code, Codex."*) pass "all: summary";; *) fail "all: summary (got $outall)";; esac HN="$T/homenone"; mkdir -p "$HN"; HOME="$HN" bash ./install.sh --agent all --yes >/dev/null 2>&1 -assert_eq "$(count "$HN/.claude/skills")" 31 "all: claude when no agent found" +assert_eq "$(count "$HN/.claude/skills")" 32 "all: claude when no agent found" # Agents that share a folder get one copy; each agent maps to its own global folder. HS="$T/homeshared"; mkdir -p "$HS/.codex" "$HS/.cline" "$HS/.codeium/windsurf" "$HS/.gemini/antigravity" outs=$(HOME="$HS" bash ./install.sh --agent all --yes 2>&1) -assert_eq "$(count "$HS/.codeium/windsurf/skills")" 31 "all: windsurf folder" -assert_eq "$(count "$HS/.gemini/antigravity/skills")" 31 "all: antigravity folder" -assert_eq "$(count "$HS/.gemini/skills")" 31 "all: gemini folder" +assert_eq "$(count "$HS/.codeium/windsurf/skills")" 32 "all: windsurf folder" +assert_eq "$(count "$HS/.gemini/antigravity/skills")" 32 "all: antigravity folder" +assert_eq "$(count "$HS/.gemini/skills")" 32 "all: gemini folder" case "$outs" in *"same folder: Cline"*) pass "all: shared folder named";; *) fail "all: shared folder not named";; esac HOME="$T/x" bash ./install.sh --agent nosuch >/dev/null 2>&1 && fail "unknown agent accepted" || pass "unknown agent rejected" P="$T/projgem"; mkdir -p "$P"; HOME="$T/x" bash ./install.sh --agent gemini --project "$P" >/dev/null 2>&1 -assert_eq "$(count "$P/.agents/skills")" 31 "gemini project folder" +assert_eq "$(count "$P/.agents/skills")" 32 "gemini project folder" HO="$T/homeclaw"; mkdir -p "$HO/.openclaw"; HOME="$HO" bash ./install.sh --agent all --yes >/dev/null 2>&1 -assert_eq "$(count "$HO/.openclaw/skills")" 31 "all: openclaw folder" +assert_eq "$(count "$HO/.openclaw/skills")" 32 "all: openclaw folder" bash -n get.sh && pass "get.sh parses" || fail "get.sh syntax" [ "$(tail -1 get.sh | cut -c1-10)" = 'main "$@" ' ] && pass "get.sh runs main last" || fail "get.sh must end with main" case "$out" in *"codex plugin"*) pass "prints plugin hint";; *) fail "no plugin hint";; esac @@ -104,7 +104,7 @@ err=$(bash "$INSTALL" --dir "$F" 2>&1 >/dev/null); rc=$? assert_eq "$rc" 1 "install exits 1 when a foreign dir is skipped" assert_eq "$(cat "$F/infra-review/notes.txt")" mine "foreign dir preserved by install" case "$err" in *"skipping $F/infra-review: not installed by RayStack (use --force to replace)"*) pass "skip warning";; *) fail "no skip warning: $err";; esac -assert_eq "$(count "$F")" 30 "other thirty still installed" +assert_eq "$(count "$F")" 31 "other thirty-one still installed" bash "$INSTALL" --dir "$F" --uninstall >/dev/null 2>&1; assert_eq "$?" 1 "uninstall exits 1 on foreign dir" assert_eq "$(cat "$F/infra-review/notes.txt")" mine "foreign dir preserved by uninstall" assert_eq "$(find "$F" -mindepth 1 -maxdepth 1 | wc -l | tr -d " ")" 1 "uninstall removed only ours" @@ -127,7 +127,7 @@ bash "$INSTALL" --dir "$R" --uninstall >/dev/null 2>&1 assert_eq "$(cat "$R/blog-cover/notes.txt")" mine "uninstall keeps foreign retired-name dir" # path with spaces -bash "$INSTALL" --dir "$T/with space/skills" >/dev/null; assert_eq "$(count "$T/with space/skills")" 31 "dir with spaces" +bash "$INSTALL" --dir "$T/with space/skills" >/dev/null; assert_eq "$(count "$T/with space/skills")" 32 "dir with spaces" # source tree and HOME/. refused bash "$INSTALL" --dir "$PWD/skills" >/dev/null 2>&1; assert_eq "$?" 1 "refuses source skills dir" @@ -166,14 +166,14 @@ bash "$INSTALL" --dir "$T/bad\"dir" >/dev/null 2>&1; assert_eq "$?" 1 "refuses q # symlinked install.sh still finds the checkout mkdir -p "$T/bin"; ln -s "$PWD/install.sh" "$T/bin/install.sh" -bash "$T/bin/install.sh" --dir "$T/viaLink" >/dev/null; assert_eq "$(count "$T/viaLink")" 31 "works via symlinked install.sh" +bash "$T/bin/install.sh" --dir "$T/viaLink" >/dev/null; assert_eq "$(count "$T/viaLink")" 32 "works via symlinked install.sh" # --project installs into the repo's agent folder (default: current folder) P="$T/repo"; mkdir -p "$P" -bash "$INSTALL" --project "$P" >/dev/null; assert_eq "$(count "$P/.claude/skills")" 31 "--project installs to .claude/skills" -bash "$INSTALL" --agent codex --project "$P" >/dev/null; assert_eq "$(count "$P/.agents/skills")" 31 "--agent codex --project installs to .agents/skills" +bash "$INSTALL" --project "$P" >/dev/null; assert_eq "$(count "$P/.claude/skills")" 32 "--project installs to .claude/skills" +bash "$INSTALL" --agent codex --project "$P" >/dev/null; assert_eq "$(count "$P/.agents/skills")" 32 "--agent codex --project installs to .agents/skills" P2="$T/repo2"; mkdir -p "$P2"; I=$PWD/install.sh -(cd "$P2" && bash "$I" --project >/dev/null); assert_eq "$(count "$P2/.claude/skills")" 31 "--project with no path uses the current folder" +(cd "$P2" && bash "$I" --project >/dev/null); assert_eq "$(count "$P2/.claude/skills")" 32 "--project with no path uses the current folder" bash "$INSTALL" --project "$T/missing" >/dev/null 2>&1; assert_eq "$?" 1 "--project refuses a missing folder" grep -q "^$(cd "$P" && pwd -P)/.claude/skills 0$" "$RAYSTACK_REGISTRY" && pass "registry records the project target" || fail "registry missing project target" bash "$INSTALL" --project "$P" --uninstall >/dev/null