From d44a8cda717b77c6e8bbc17a34cc4d4573b0372e Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Fri, 5 Jun 2026 14:34:48 -0400 Subject: [PATCH 1/7] chore: set up release infrastructure Adds CI, CodeQL, and a release-please workflow that publishes @posthog/warlock to npm via OIDC trusted publishing + provenance. No long-lived NPM_TOKEN. The publish step is gated by a Release GitHub Environment with @PostHog/team-docs-wizard as required reviewers. Slack notifications are intentionally omitted for v1 - GitHub's native env-pending notifications are sufficient given the lower release cadence. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/CODEOWNERS | 3 ++ .github/dependabot.yml | 18 +++++++++ .github/workflows/ci.yml | 38 ++++++++++++++++++ .github/workflows/codeql.yml | 41 +++++++++++++++++++ .github/workflows/release.yml | 76 +++++++++++++++++++++++++++++++++++ .nvmrc | 1 + .release-please-manifest.json | 3 ++ README.md | 18 +++++++++ package.json | 9 +++-- pnpm-lock.yaml | 2 +- release-please-config.json | 11 +++++ 11 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/release.yml create mode 100644 .nvmrc create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..1736cfe --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# The Docs & Wizard team owns the Warlock day-to-day (see CONTRIBUTING.md). +# Security team is consulted on security-sensitive changes but is not a code owner. +* @PostHog/team-docs-wizard diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0638144 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + # npm dependencies (currently just @virustotal/yara-x + dev tooling). + - package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + dev-dependencies: + dependency-type: development + + # GitHub Actions used in our workflows – keeps pinned action SHAs current. + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..16b428a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + test: + name: Test & build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Install pnpm + uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2.4.1 + with: + version: 9 + run_install: false + + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version-file: '.nvmrc' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests + run: pnpm test + + - name: Build + run: pnpm build diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..aceccd0 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Mondays 09:00 UTC – catches new CodeQL queries weekly even if no code changes. + - cron: '0 9 * * 1' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + security-events: write + packages: read + actions: read + contents: read + strategy: + fail-fast: false + matrix: + language: [javascript-typescript] + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Initialize CodeQL + uses: github/codeql-action/init@411bbbe57033eedfc1a82d68c01345aa96c737d7 # v4 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@411bbbe57033eedfc1a82d68c01345aa96c737d7 # v4 + with: + category: '/language:${{ matrix.language }}' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..36a0227 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: 'Release' + +permissions: + contents: read + +on: + push: + branches: [main] + workflow_dispatch: + +# Only one release runs at a time. Prevents races when multiple PRs +# merge close together. +concurrency: + group: release + cancel-in-progress: false + +jobs: + release-please: + name: Open or update release PR / tag release + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: write + pull-requests: write + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + steps: + - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0 + id: release + with: + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + + publish: + name: Publish to npm + needs: release-please + if: needs.release-please.outputs.release_created == 'true' + runs-on: ubuntu-latest + timeout-minutes: 10 + # Required reviewers for this environment are configured in the repo + # settings (Settings → Environments → Release): `@PostHog/team-docs-wizard`. + # GitHub notifies reviewers in-product and by email when a publish is pending. + environment: 'Release' + permissions: + contents: read + id-token: write # for npm OIDC trusted publishing + provenance + steps: + - name: Checkout release tag + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + ref: ${{ needs.release-please.outputs.tag_name }} + + - name: Install pnpm + uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2.4.1 + with: + version: 10 + run_install: false + + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version-file: '.nvmrc' + cache: 'pnpm' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Publish to npm (OIDC trusted publishing + provenance) + run: pnpm publish --access public --no-git-checks + env: + NPM_CONFIG_PROVENANCE: 'true' diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..466df71 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.0" +} diff --git a/README.md b/README.md index 64c08c5..4f839e2 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,24 @@ pnpm test:watch # run tests in watch mode pnpm build # compile TypeScript and copy rule files to dist/ ``` +## Releasing + +Releases follow the spirit of PostHog's [SDK release procedure](https://posthog.com/handbook/engineering/sdks/releases) – semi-automatic, with a human approval gate on the publish step and npm [OIDC trusted publishing](https://docs.npmjs.com/trusted-publishers) for the actual publish. Nobody runs `npm publish` by hand :) + +How it works: + +1. Land changes on `main` with [Conventional Commits](https://www.conventionalcommits.org/) messages (`fix:` → patch, `feat:` → minor, `feat!:` or a `BREAKING CHANGE:` footer → major). That's how [release-please](https://github.com/googleapis/release-please) knows what to bump. +2. release-please keeps a **release PR** open that bumps the version in `package.json` and updates `CHANGELOG.md`. It keeps updating itself as more commits land. +3. When you're ready to ship, review and merge that release PR. Merging it tags the release. +4. The publish job then waits for approval on the **`Release` GitHub Environment**. The Docs & Wizard team (`@PostHog/team-docs-wizard`) gets a pending-deployment notification in-product and by email. +5. Once approved, `@posthog/warlock` is published to npm via OIDC trusted publishing – no long-lived `NPM_TOKEN` anywhere. + +A few rules of thumb: + +- Don't bump the version in `package.json` by hand – release-please owns it. +- Doc-only / CI / test-only changes use `docs:`, `ci:`, `test:` prefixes and don't trigger a version bump. +- Anything user-facing should be `fix:` or `feat:` so it ends up in the release PR. + ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution process, review model, rule-writing guide, and category-addition policy. diff --git a/package.json b/package.json index d63f5a1..e26b758 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,12 @@ { "name": "@posthog/warlock", - "version": "0.0.0", + "version": "0.1.0", "description": "Security scanner for PostHog's agentic flows", "license": "MIT", - "private": true, + "repository": { + "type": "git", + "url": "git+https://github.com/PostHog/warlock.git" + }, "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -28,7 +31,7 @@ "access": "public" }, "dependencies": { - "@virustotal/yara-x": "^1.15.0" + "@virustotal/yara-x": "1.15.0" }, "devDependencies": { "@types/node": "^22.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4664046..4c3a66f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,7 +9,7 @@ importers: .: dependencies: '@virustotal/yara-x': - specifier: ^1.15.0 + specifier: 1.15.0 version: 1.15.0 devDependencies: '@types/node': diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..e5ad9c7 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "package-name": "@posthog/warlock", + "changelog-path": "CHANGELOG.md" + } + }, + "include-component-in-tag": false +} From 32a3bc803781fb52b57ae6ba5ee9bd2690979009 Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Fri, 5 Jun 2026 14:56:56 -0400 Subject: [PATCH 2/7] fix(ci): bump workflow pnpm to 10 + add packages field CI was failing at `pnpm store path` (called by setup-node's pnpm cache setup) with "packages field missing or empty". Two fixes, belt-and-suspenders: - Bump CI workflow pnpm from 9 to 10 to match the version used locally and in release.yml. pnpm 10 is lenient about pnpm-workspace.yaml. - Add an explicit `packages: ['.']` to pnpm-workspace.yaml so the file is valid for any future pnpm 9 or stricter tooling that reads it. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 2 +- pnpm-workspace.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16b428a..62ff42b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@eae0cfeb286e66ffb5155f1a79b90583a127a68b # v2.4.1 with: - version: 9 + version: 10 run_install: false - name: Set up Node diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 09a02ca..d12186c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,5 @@ +packages: + - '.' allowBuilds: esbuild: true onlyBuiltDependencies: From e2fa9fcc4c19f23402a722a369b4a2d9ef070d72 Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Fri, 5 Jun 2026 15:05:40 -0400 Subject: [PATCH 3/7] chore(ci): drop our codeql.yml in favor of GitHub default setup GitHub's default CodeQL setup is already enabled on this repo and conflicts with an advanced workflow file. Leaning on the default for v1 to unblock the first publish; we can switch to advanced (with SHA-pinned actions and security-and-quality queries) as a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/codeql.yml | 41 ------------------------------------ 1 file changed, 41 deletions(-) delete mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index aceccd0..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: CodeQL - -on: - push: - branches: [main] - pull_request: - branches: [main] - schedule: - # Mondays 09:00 UTC – catches new CodeQL queries weekly even if no code changes. - - cron: '0 9 * * 1' - -permissions: - contents: read - -jobs: - analyze: - name: Analyze (${{ matrix.language }}) - runs-on: ubuntu-latest - timeout-minutes: 15 - permissions: - security-events: write - packages: read - actions: read - contents: read - strategy: - fail-fast: false - matrix: - language: [javascript-typescript] - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Initialize CodeQL - uses: github/codeql-action/init@411bbbe57033eedfc1a82d68c01345aa96c737d7 # v4 - with: - languages: ${{ matrix.language }} - queries: security-and-quality - - - name: Perform CodeQL analysis - uses: github/codeql-action/analyze@411bbbe57033eedfc1a82d68c01345aa96c737d7 # v4 - with: - category: '/language:${{ matrix.language }}' From 82d97cfbbf02b98040aa19fb2731fa1127fbc2ba Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Sun, 7 Jun 2026 19:43:24 -0400 Subject: [PATCH 4/7] chore(security): add 7-day cooldown for dependency updates Addresses semgrep findings (3 blocking) and Felipe's PR feedback, both pointing at the same hardening called out in the Shai-Hulud post-mortem: refuse to install dependency versions published less than 7 days ago, so we're not the first to get burned by a malicious release. - pnpm-workspace.yaml: minimumReleaseAge: 10080 (minutes = 7 days) - .github/dependabot.yml: cooldown.default-days: 7 on both package-ecosystem entries (npm + github-actions) Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/dependabot.yml | 6 ++++++ pnpm-workspace.yaml | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0638144..af4413c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,6 +6,10 @@ updates: schedule: interval: weekly open-pull-requests-limit: 5 + # Wait 7 days before proposing newly published versions — defence against + # rapidly-deployed malicious package versions (post-Shai-Hulud tightening). + cooldown: + default-days: 7 groups: dev-dependencies: dependency-type: development @@ -16,3 +20,5 @@ updates: schedule: interval: weekly open-pull-requests-limit: 5 + cooldown: + default-days: 7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d12186c..dd43ab5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,9 @@ packages: - '.' +# Refuse to install any dependency version published less than 7 days ago. +# Defence against rapidly-deployed malicious package versions (post-Shai-Hulud +# tightening). 10080 minutes = 7 days. Requires pnpm >= 10.16.0. +minimumReleaseAge: 10080 allowBuilds: esbuild: true onlyBuiltDependencies: From 20863c6c2a0b00880e506d194e1491bb1eaf645a Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Sun, 7 Jun 2026 20:00:04 -0400 Subject: [PATCH 5/7] nit --- .github/dependabot.yml | 3 +-- pnpm-workspace.yaml | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index af4413c..5bd1279 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,8 +6,7 @@ updates: schedule: interval: weekly open-pull-requests-limit: 5 - # Wait 7 days before proposing newly published versions — defence against - # rapidly-deployed malicious package versions (post-Shai-Hulud tightening). + # Wait 7 days before proposing newly published versions cooldown: default-days: 7 groups: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dd43ab5..c40b97c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,8 +1,6 @@ packages: - '.' -# Refuse to install any dependency version published less than 7 days ago. -# Defence against rapidly-deployed malicious package versions (post-Shai-Hulud -# tightening). 10080 minutes = 7 days. Requires pnpm >= 10.16.0. +# Refuse to install any dependency version published less than 7 days ago, requires pnpm >= 10.16.0. minimumReleaseAge: 10080 allowBuilds: esbuild: true From 8f973468368b2ccec4d50ad378d51d74a61f9ab2 Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Mon, 8 Jun 2026 09:51:40 -0400 Subject: [PATCH 6/7] chore: restore caret on @virustotal/yara-x Reverts the exact pin so consumers of @posthog/warlock can pick up yara-x patch fixes (including future security patches) without needing a manual override. Reproducibility for our own installs is already handled by the lockfile, and minimumReleaseAge gates new versions for 7 days. Co-Authored-By: Claude Opus 4.7 (1M context) --- package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e26b758..d47784d 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "access": "public" }, "dependencies": { - "@virustotal/yara-x": "1.15.0" + "@virustotal/yara-x": "^1.15.0" }, "devDependencies": { "@types/node": "^22.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c3a66f..4664046 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,7 +9,7 @@ importers: .: dependencies: '@virustotal/yara-x': - specifier: 1.15.0 + specifier: ^1.15.0 version: 1.15.0 devDependencies: '@types/node': From 86b6e52241630525443e17451f647d397c2a57cf Mon Sep 17 00:00:00 2001 From: sarahxsanders Date: Mon, 8 Jun 2026 10:28:52 -0400 Subject: [PATCH 7/7] chore: trigger CodeQL