Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"name": "Node.js",
"image": "mcr.microsoft.com/devcontainers/javascript-node:5.2.1-24-trixie@sha256:7a81958053c4e3b5b20fa122f7a422d32dd0eebe87a6726f7b14483585ea60fd",
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:4": {
"moby": false
}
},
"postCreateCommand": "bash ./scripts/setup-cloud-environment.sh"
}
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# High-risk paths. Last matching pattern wins.
# Root-anchored so nested copies (e.g. skills/**/scripts/) are not owned here.
/.github/ @jaredwray
/.vscode/ @jaredwray
/.cursor/ @jaredwray
/.devcontainer/ @jaredwray
/scripts/ @jaredwray
11 changes: 9 additions & 2 deletions .github/workflows/check-workflows.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Check Workflows
name: check-workflows

on:
push:
Expand All @@ -13,7 +13,10 @@ jobs:
permissions:
contents: read
actions: read
security-events: write # SARIF upload to code scanning
# SARIF upload. GitHub grants read-only on fork PRs regardless; permission
# values cannot be expressions, so the write grant stays and the zizmor
# step below skips Advanced Security on forks.
security-events: write
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -26,3 +29,7 @@ jobs:
firewall-version: "1.15.2"
- name: Run zizmor
uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
with:
# Fork PRs cannot upload SARIF (read-only token). Lint with annotations instead.
advanced-security: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
annotations: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ permissions:

jobs:
analyze:
name: Analyze
name: analyze
runs-on: ubuntu-latest
permissions:
actions: read
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-site.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:

jobs:
setup-build-deploy:
name: Deploy Website
name: deploy-website
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:

jobs:
aikido-gate:
name: Aikido release gate
name: aikido-gate
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down
20 changes: 11 additions & 9 deletions DEFENSE_IN_DEPTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ Profile: npm library · public

## 2. CODEOWNERS and cloud bootstrap

- [x] `.github/CODEOWNERS` covers `/.github/`, `/.cursor/`, `/.devcontainer/`, `/scripts/` with owners the maintainer names — PR #184
- [ ] `.github/CODEOWNERS` covers `/.github/`, `/.vscode/`, `/.cursor/`, `/.devcontainer/`, `/scripts/` with owners the maintainer names (PR #212 pending)
- [x] Codespaces and Cursor Cloud Agents bootstrap Aikido Safe Chain via scripts/setup-cloud-environment.sh (--ci shims, frozen lockfile) — PR #185
- [x] Dev Container `image` pinned by digest (`name:<tag>@sha256:<digest>`; not a floating tag) — PR #208

## 3. Dependencies (pnpm)

- [x] `packageManager: pnpm@11.3+` pinned in `package.json` — verified `pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c`
- [x] 7-day cooldown: `minimumReleaseAge: 10080`, `minimumReleaseAgeStrict: true`, `minimumReleaseAgeIgnoreMissingTime: false`; no first-party `minimumReleaseAgeExclude` — PR #186
- [x] `trustPolicy: no-downgrade`; no first-party `trustPolicyExclude` — PR #187
- [x] Lifecycle scripts blocked: `strictDepBuilds: true`, `dangerouslyAllowAllBuilds: false`, `allowBuilds: {}` baseline — verified (third-party `allowBuilds` exceptions: esbuild, sharp, unrs-resolver, workerd)
- [x] Lifecycle scripts blocked: `strictDepBuilds: true`, `dangerouslyAllowAllBuilds: false`, `allowBuilds: {}` baseline — verified (third-party `allowBuilds` exceptions: esbuild, sharp, workerd)
- [x] `blockExoticSubdeps: true` — verified
- [x] Lockfile committed; CI installs with `pnpm install --frozen-lockfile` — verified
- [x] No `.github/dependabot.yml`; other dependency-update tools (if any) open PRs only — never auto-merge — verified
Expand All @@ -31,18 +32,19 @@ Profile: npm library · public
- [x] Every action pinned to a full commit SHA (`npx actions-up`) — PR #188
- [x] Every job installs Socket Firewall (`SocketDev/action` SHA-pinned, `firewall-version` pinned); `pnpm install` / `npm install` run as `sfw pnpm install` / `sfw npm install` — PR #189
- [x] `.github/workflows/check-workflows.yaml` lints workflows with zizmor on every PR — PR #190
- [ ] Workflow `name:` and job `name:` contain no spaces (kebab-case) so they can be set as required status checks (PR #212 pending)
- [x] `persist-credentials: false` on checkouts that don't push — PR #191
- [x] No `pull_request_target` on workflows that run untrusted PR code — verified
- [x] Artifact-publishing workflows disable `actions/setup-node` default caching (`package-manager-cache: false`) to prevent cache poisoning — PR #192
- [x] No npm tokens (or other registry credentials) in Actions secrets — verified (no npm/registry tokens in workflow YAML; publish uses OIDC `id-token`)

## 5. npm publishing — npm libraries only

- [x] OIDC trusted publishing configured **stage-only** on npmjs.com for the publish workflow — it can stage, never publish live — verified (maintainer)
- [x] OIDC trusted publishing configured **stage-only** on npmjs.com for the publish workflow — it can stage, never publish live (manual) — verified (maintainer)
- [x] `.github/workflows/release.yaml` packs then stages with `pnpm stage publish ./packed/*.tgz --no-git-checks` — PR #193
- [x] Maintainer promotes staged versions with 2FA — verified (maintainer)
- [x] Drydock connected — staged releases reviewed before promotion — verified (maintainer)
- [x] No direct publish rights: package requires 2FA and disallows tokens — verified (maintainer)
- [x] Maintainer promotes staged versions with 2FA (manual) — verified (maintainer)
- [x] Drydock connected — staged releases reviewed before promotion (manual) — verified (maintainer)
- [x] No direct publish rights: package requires 2FA and disallows tokens (manual) — verified (maintainer)
- [x] `package.json` `repository.url` accurate so provenance maps to this repo — verified

## 6. Security tooling
Expand All @@ -53,6 +55,6 @@ Profile: npm library · public

## 7. Repository lockdown

- [x] `lockdown-repo.sh` applied; `--check` with `--required-checks "test,zizmor"` and `--allowed-actions "codecov/*,cloudflare/*"` passes (PRs required on the default branch, merges blocked unless required status checks pass, tag ruleset, immutable releases, fork-PR approval, read-only workflow tokens, Actions allowlist, secret scanning, Dependabot disabled, private vulnerability reporting as applicable) — PR #195
- [x] Phishing-resistant 2FA (passkeys / hardware keys) on the GitHub and npm accounts — verified (maintainer)
- [x] Recovery codes stored offline in a password manager — verified (maintainer)
- [x] Phishing-resistant 2FA (passkeys / hardware keys) on the GitHub and npm accounts (manual) — verified (maintainer)
- [x] Recovery codes stored offline in a password manager (manual) — verified (maintainer)
- [x] `lockdown-repo.sh` applied by a repo admin (never committed to this repo) — PR #195. Latest `--check` (`--required-checks "test,zizmor"`, `--allowed-actions "codecov/*,cloudflare/*"`) still fails the branch ruleset: it has no owner `pull_request` bypass. The tag ruleset has no repository-admin bypass. Remaining settings were unreadable here (non-admin token, HTTP 403). Admin re-apply is still required.
3 changes: 2 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ This repository follows the [defense-in-depth](https://github.com/jaredwray/agen
- Workflow runs from outside collaborators always require maintainer approval, and only allowlisted GitHub Actions can run.
- CI runs with read-only permissions (only jobs whose purpose is mutating the repo get `contents: write`); generated output is an artifact, never committed back; every action is pinned to a full commit SHA; Socket Firewall (`sfw`) wraps `pnpm install` / `npm install`; workflows are security-linted with zizmor on every PR.
- Codespaces and Cursor Cloud Agents install through Aikido Safe Chain; package-manager shims must not be bypassed.
- The Codespaces Dev Container image is pinned by digest (`name:<tag>@sha256:<digest>`), not a floating tag.
- Dependencies install through pnpm with a 7-day cooldown on new versions, lifecycle scripts blocked by default, and `trustPolicy: no-downgrade`. Socket reviews every dependency change; Aikido scans every build.
- npm releases are staged, never published directly: CI publishes via stage-only OIDC trusted publishing, Drydock reviews the exact staged artifact, and a maintainer promotes it with 2FA. There are no npm tokens.
- npm releases are staged, never published directly: CI publishes via stage-only OIDC trusted publishing, Drydock reviews the exact staged artifact, and a maintainer promotes it with 2FA. There are no npm publish tokens.
1 change: 0 additions & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ trustPolicy: no-downgrade
allowBuilds:
esbuild: true
sharp: true
unrs-resolver: true
workerd: true
16 changes: 13 additions & 3 deletions scripts/setup-cloud-environment.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ if [[ ! -f pnpm-lock.yaml ]]; then
exit 1
fi

if [[ -f package.json ]] && grep -q '"packageManager"' package.json && command -v corepack >/dev/null; then
corepack enable
if ! command -v pnpm >/dev/null \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the pinned pnpm version when pnpm is preinstalled

When a Codespace or cloud-agent image already exposes pnpm, this condition skips Corepack and the later install runs whichever version the image provides rather than the exact pnpm@12.4.1 declared in package.json. An older pnpm can reject or ignore newer workspace security settings such as trustPolicy and minimumReleaseAgeStrict, undermining this bootstrap's protections or breaking installation; create the Corepack shim whenever Corepack and packageManager are available, or explicitly verify that the existing executable matches the pinned version.

Useful? React with 👍 / 👎.

&& [[ -f package.json ]] \
&& grep -q '"packageManager"' package.json \
&& command -v corepack >/dev/null; then
mkdir -p "$SAFE_CHAIN_BIN"
corepack enable --install-directory "$SAFE_CHAIN_BIN" pnpm
export PATH="${SAFE_CHAIN_BIN}:${PATH}"
fi

if ! command -v pnpm >/dev/null; then
Expand All @@ -36,7 +41,12 @@ trap 'rm -f "$installer"' EXIT
curl -fsSL "$SAFE_CHAIN_INSTALLER_URL" -o "$installer"
echo "${SAFE_CHAIN_INSTALLER_SHA256} ${installer}" | sha256sum -c -

sh "$installer" --ci
# NVM auto-selects from the current directory when sourced. Run outside the
# repository so .nvmrc cannot break the installer's optional legacy scan.
(
cd /
sh "$installer" --ci
)

export PATH="${SAFE_CHAIN_SHIMS}:${SAFE_CHAIN_BIN}:${PATH}"

Expand Down
Loading