create-daloy 0.34.3 → 0.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -20
- package/bin/create-daloy.mjs +83 -17
- package/package.json +2 -2
- package/sbom.cdx.json +13 -9
- package/sbom.spdx.json +5 -5
- package/templates/_ci/deno/SECURITY.md +31 -2
- package/templates/_ci/deno/_github/CODEOWNERS +2 -0
- package/templates/_ci/deno/_github/workflows/container-scan.yml +42 -8
- package/templates/_ci/deno/_github/workflows/deploy.yml +68 -3
- package/templates/_ci/deno/_github/workflows/opengrep.yml +137 -0
- package/templates/_ci/deno/_github/workflows/osv-scan.yml +121 -0
- package/templates/_ci/deno/_github/workflows/secret-scan.yml +106 -0
- package/templates/_ci/node/SECURITY.md +100 -2
- package/templates/_ci/node/_github/CODEOWNERS +2 -0
- package/templates/_ci/node/_github/workflows/container-scan.yml +46 -10
- package/templates/_ci/node/_github/workflows/opengrep.yml +169 -0
- package/templates/_ci/node/_github/workflows/osv-scan.yml +135 -0
- package/templates/_ci/node/_github/workflows/secret-scan.yml +106 -0
- package/templates/bun-basic/AGENTS.md +12 -0
- package/templates/bun-basic/_gitignore +5 -0
- package/templates/bun-basic/package.json +3 -2
- package/templates/cloudflare-worker/AGENTS.md +13 -0
- package/templates/cloudflare-worker/_gitignore +9 -0
- package/templates/cloudflare-worker/_npmrc +2 -1
- package/templates/cloudflare-worker/package.json +3 -2
- package/templates/deno-basic/AGENTS.md +12 -0
- package/templates/deno-basic/_gitignore +5 -0
- package/templates/deno-basic/deno.json +2 -2
- package/templates/node-basic/AGENTS.md +12 -0
- package/templates/node-basic/_gitignore +5 -0
- package/templates/node-basic/_npmrc +2 -2
- package/templates/node-basic/package.json +1 -1
- package/templates/vercel-edge/AGENTS.md +12 -0
- package/templates/vercel-edge/_gitignore +5 -0
- package/templates/vercel-edge/_npmrc +2 -1
- package/templates/vercel-edge/package.json +3 -2
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Opengrep — second SAST engine, complementing CodeQL.
|
|
2
|
+
#
|
|
3
|
+
# Why this exists in your scaffolded Daloy project:
|
|
4
|
+
# Daloy itself runs two SAST engines (CodeQL + Opengrep) because no
|
|
5
|
+
# single rule corpus catches everything. Opengrep is the LGPL-2.1
|
|
6
|
+
# community fork of Semgrep, ships Semgrep-compatible rules, and is
|
|
7
|
+
# backed by an open consortium. See Aikido's "Ultimate SAST Guide"
|
|
8
|
+
# (https://www.aikido.dev/blog/ultimate-sast-guide-static-application-security-testing)
|
|
9
|
+
# and "Launching Opengrep" (https://www.aikido.dev/blog/launching-opengrep-why-we-forked-semgrep).
|
|
10
|
+
#
|
|
11
|
+
# Pairing CodeQL (semantic, dataflow-heavy, language-server-grade)
|
|
12
|
+
# with Opengrep (pattern-based AST matching, fast, community-maintained
|
|
13
|
+
# rule packs) gives defense-in-depth at the source layer — the same
|
|
14
|
+
# posture Daloy maintains on its own repository.
|
|
15
|
+
#
|
|
16
|
+
# Hardening (matches the rest of the scaffolded bundle):
|
|
17
|
+
# * Top-level `permissions: {}`; the job opts in only to the two
|
|
18
|
+
# scopes it needs (`security-events: write` for SARIF upload,
|
|
19
|
+
# `contents: read` for checkout).
|
|
20
|
+
# * Third-party actions are SHA-pinned with version comments.
|
|
21
|
+
# * Opengrep itself is NOT installed via a third-party action.
|
|
22
|
+
# Instead the binary is downloaded from a pinned GitHub release
|
|
23
|
+
# and its sigstore cosign signature is verified against the
|
|
24
|
+
# official `opengrep/opengrep` release identity before execution.
|
|
25
|
+
# This avoids `curl ... | sh` and avoids adding a new pinned
|
|
26
|
+
# action to your supply chain just for this scan.
|
|
27
|
+
# * `step-security/harden-runner` runs in audit mode — the job
|
|
28
|
+
# needs egress to `github.com` (release artifact),
|
|
29
|
+
# `objects.githubusercontent.com` (release CDN), `semgrep.dev`
|
|
30
|
+
# (Semgrep-registry rule packs), and `rekor.sigstore.dev` /
|
|
31
|
+
# `fulcio.sigstore.dev` (cosign keyless verification). Audit
|
|
32
|
+
# logs every connection for post-hoc review.
|
|
33
|
+
# * `actions/checkout` runs with `persist-credentials: false`.
|
|
34
|
+
# * `set -euo pipefail` on every shell step; binaries are pinned
|
|
35
|
+
# by version + cosign signature.
|
|
36
|
+
|
|
37
|
+
name: Opengrep
|
|
38
|
+
|
|
39
|
+
on:
|
|
40
|
+
push:
|
|
41
|
+
branches: [main]
|
|
42
|
+
pull_request:
|
|
43
|
+
branches: [main]
|
|
44
|
+
schedule:
|
|
45
|
+
# 51 5 * * 4 — weekly Thursday, offset from CodeQL (Mon 04:37)
|
|
46
|
+
# and Scorecard so concurrent SARIF uploads do not collide.
|
|
47
|
+
- cron: "51 5 * * 4"
|
|
48
|
+
workflow_dispatch:
|
|
49
|
+
|
|
50
|
+
permissions: {}
|
|
51
|
+
|
|
52
|
+
concurrency:
|
|
53
|
+
group: opengrep-${{ github.workflow }}-${{ github.ref }}
|
|
54
|
+
cancel-in-progress: true
|
|
55
|
+
|
|
56
|
+
jobs:
|
|
57
|
+
scan:
|
|
58
|
+
name: Opengrep SAST
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
timeout-minutes: 20
|
|
61
|
+
permissions:
|
|
62
|
+
security-events: write
|
|
63
|
+
contents: read
|
|
64
|
+
|
|
65
|
+
env:
|
|
66
|
+
# Pin the Opengrep release we install. Bump this in lockstep
|
|
67
|
+
# with a fresh review of the upstream release notes; the
|
|
68
|
+
# signature verification step below provides a second line of
|
|
69
|
+
# defense if this URL is ever poisoned.
|
|
70
|
+
OPENGREP_VERSION: "v1.22.0"
|
|
71
|
+
OPENGREP_ASSET: "opengrep_manylinux_x86"
|
|
72
|
+
|
|
73
|
+
steps:
|
|
74
|
+
- name: Harden runner
|
|
75
|
+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
|
|
76
|
+
with:
|
|
77
|
+
egress-policy: audit
|
|
78
|
+
disable-sudo: true
|
|
79
|
+
|
|
80
|
+
- name: Checkout
|
|
81
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
82
|
+
with:
|
|
83
|
+
persist-credentials: false
|
|
84
|
+
show-progress: false
|
|
85
|
+
|
|
86
|
+
- name: Download Opengrep release binary + signature
|
|
87
|
+
# We download the binary and its sigstore cert/sig directly
|
|
88
|
+
# from the pinned GitHub release. No `curl ... | sh`.
|
|
89
|
+
run: |
|
|
90
|
+
set -euo pipefail
|
|
91
|
+
mkdir -p .opengrep
|
|
92
|
+
base="https://github.com/opengrep/opengrep/releases/download/${OPENGREP_VERSION}/${OPENGREP_ASSET}"
|
|
93
|
+
echo "Fetching ${base}"
|
|
94
|
+
curl --proto '=https' --tlsv1.2 -fsSL --retry 3 --retry-delay 2 \
|
|
95
|
+
-o .opengrep/opengrep "${base}"
|
|
96
|
+
curl --proto '=https' --tlsv1.2 -fsSL --retry 3 --retry-delay 2 \
|
|
97
|
+
-o .opengrep/opengrep.cert "${base}.cert"
|
|
98
|
+
curl --proto '=https' --tlsv1.2 -fsSL --retry 3 --retry-delay 2 \
|
|
99
|
+
-o .opengrep/opengrep.sig "${base}.sig"
|
|
100
|
+
chmod +x .opengrep/opengrep
|
|
101
|
+
|
|
102
|
+
- name: Verify Opengrep signature (cosign / sigstore)
|
|
103
|
+
# `cosign` is preinstalled on GitHub-hosted `ubuntu-latest`
|
|
104
|
+
# runners. We require an identity issued by GitHub OIDC to
|
|
105
|
+
# the opengrep/opengrep release workflow; anything else is a
|
|
106
|
+
# hard fail (no `|| true`, no fallback).
|
|
107
|
+
run: |
|
|
108
|
+
set -euo pipefail
|
|
109
|
+
if ! command -v cosign >/dev/null 2>&1; then
|
|
110
|
+
echo "::error::cosign is required but not available on the runner."
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
cosign version
|
|
114
|
+
cosign verify-blob \
|
|
115
|
+
--cert ".opengrep/opengrep.cert" \
|
|
116
|
+
--signature ".opengrep/opengrep.sig" \
|
|
117
|
+
--certificate-identity-regexp "https://github.com/opengrep/opengrep.+" \
|
|
118
|
+
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
|
119
|
+
".opengrep/opengrep"
|
|
120
|
+
echo "Opengrep ${OPENGREP_VERSION} signature verified."
|
|
121
|
+
|
|
122
|
+
- name: Print Opengrep version
|
|
123
|
+
run: |
|
|
124
|
+
set -euo pipefail
|
|
125
|
+
.opengrep/opengrep --version
|
|
126
|
+
|
|
127
|
+
- name: Run Opengrep scan (SARIF)
|
|
128
|
+
# Curated security-focused rule packs from the Semgrep registry,
|
|
129
|
+
# which Opengrep consumes unchanged:
|
|
130
|
+
# * p/security-audit — generic SAST audit pack
|
|
131
|
+
# * p/owasp-top-ten — OWASP Top 10 application classes
|
|
132
|
+
# * p/cwe-top-25 — CWE Top 25 dangerous weaknesses
|
|
133
|
+
# * p/javascript / p/typescript — language-specific rules
|
|
134
|
+
# * p/nodejs — Node.js runtime-specific patterns
|
|
135
|
+
# * p/secrets — hardcoded credentials / API keys
|
|
136
|
+
#
|
|
137
|
+
# `--error` is intentionally OFF: SARIF is uploaded to GitHub
|
|
138
|
+
# code scanning and branch protection rules decide whether
|
|
139
|
+
# new findings gate merges. The workflow's exit status is
|
|
140
|
+
# reserved for *infrastructure* failures (download, signature,
|
|
141
|
+
# binary crash), not findings.
|
|
142
|
+
run: |
|
|
143
|
+
set -euo pipefail
|
|
144
|
+
.opengrep/opengrep scan \
|
|
145
|
+
--config p/security-audit \
|
|
146
|
+
--config p/owasp-top-ten \
|
|
147
|
+
--config p/cwe-top-25 \
|
|
148
|
+
--config p/javascript \
|
|
149
|
+
--config p/typescript \
|
|
150
|
+
--config p/nodejs \
|
|
151
|
+
--config p/secrets \
|
|
152
|
+
--sarif-output opengrep.sarif \
|
|
153
|
+
--metrics off \
|
|
154
|
+
--timeout 120 \
|
|
155
|
+
--max-target-bytes 1500000 \
|
|
156
|
+
--exclude node_modules \
|
|
157
|
+
--exclude dist \
|
|
158
|
+
--exclude build \
|
|
159
|
+
--exclude coverage \
|
|
160
|
+
--exclude .next \
|
|
161
|
+
--exclude .opengrep \
|
|
162
|
+
.
|
|
163
|
+
|
|
164
|
+
- name: Upload SARIF to GitHub code scanning
|
|
165
|
+
if: always()
|
|
166
|
+
uses: github/codeql-action/upload-sarif@52485aec7be33610227643b0fe83936b8b5f061a # v3
|
|
167
|
+
with:
|
|
168
|
+
sarif_file: opengrep.sarif
|
|
169
|
+
category: opengrep
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Second-source dependency scan against the OSV.dev database
|
|
2
|
+
# (Aikido "SAST vs SCA" / "npm-audit-guide — the missing layer" gate).
|
|
3
|
+
#
|
|
4
|
+
# Why this exists for a REST API service:
|
|
5
|
+
# `vuln-scan.yml` already runs `__AUDIT_PROD_STEP__`-equivalent
|
|
6
|
+
# `pnpm/npm/yarn/bun audit` against the committed lockfile every day,
|
|
7
|
+
# but that single feed (GHSA) is exactly the failure mode the Aikido
|
|
8
|
+
# write-up "SAST vs SCA" calls out: SCA findings are only as good as
|
|
9
|
+
# the database you query. The September 2025 npm phishing wave that
|
|
10
|
+
# compromised `debug`, `chalk`, and ~2B downloads/week of dependent
|
|
11
|
+
# packages (https://lwn.net/Articles/1037167/) showed that newly-
|
|
12
|
+
# disclosed malicious package versions often land in OSS feeds
|
|
13
|
+
# (OpenSSF `malicious-packages`, OSV.dev, PyPI, Go vuln DB) *before*
|
|
14
|
+
# they propagate into GHSA.
|
|
15
|
+
#
|
|
16
|
+
# This workflow adds a SECOND, independent source of vulnerability
|
|
17
|
+
# and malware intel: Google's OSV-Scanner, which queries OSV.dev and
|
|
18
|
+
# cross-references the OpenSSF `malicious-packages` corpus
|
|
19
|
+
# (https://github.com/ossf/malicious-packages). Findings that overlap
|
|
20
|
+
# with the daily package-manager audit are duplicates; findings that
|
|
21
|
+
# DO NOT overlap are the gap the Aikido article warns about.
|
|
22
|
+
#
|
|
23
|
+
# Defense-in-depth context (see SECURITY.md § Supply chain):
|
|
24
|
+
# * `.npmrc` `ignore-scripts=true` and `minimum-release-age=1440`
|
|
25
|
+
# (pnpm scaffolds) block the install-time and zero-day-window
|
|
26
|
+
# classes of supply-chain attack.
|
|
27
|
+
# * `verify-lockfile-sources.mjs` already refuses non-registry sources.
|
|
28
|
+
# * `pnpm/npm/yarn/bun audit` covers the GHSA feed.
|
|
29
|
+
# * This OSV pass is the LAST layer: "did a pinned version turn out
|
|
30
|
+
# to carry a CVE or a malware advisory after we pinned it?"
|
|
31
|
+
#
|
|
32
|
+
# Hardening:
|
|
33
|
+
# * `permissions: {}` at the top level; the single job opts in to
|
|
34
|
+
# `contents: read` only. No write permissions of any kind.
|
|
35
|
+
# * `step-security/harden-runner` in audit mode.
|
|
36
|
+
# * `actions/checkout` runs with `persist-credentials: false`.
|
|
37
|
+
# * The OSV-Scanner binary is downloaded directly from the official
|
|
38
|
+
# google/osv-scanner GitHub release and its SHA-256 is verified
|
|
39
|
+
# against a locally-pinned constant BEFORE the binary is executed.
|
|
40
|
+
# We deliberately avoid the third-party `google/osv-scanner-action`
|
|
41
|
+
# wrapper so the only third-party surface is the binary itself, not
|
|
42
|
+
# a layer of action-side YAML that could be retagged.
|
|
43
|
+
# * The scan is read-only and runs on the committed lockfile without
|
|
44
|
+
# installing any dependencies, so a malicious dev dep cannot run a
|
|
45
|
+
# postinstall during the scan itself.
|
|
46
|
+
|
|
47
|
+
name: OSV scan
|
|
48
|
+
|
|
49
|
+
on:
|
|
50
|
+
push:
|
|
51
|
+
branches: [main]
|
|
52
|
+
paths:
|
|
53
|
+
- "pnpm-lock.yaml"
|
|
54
|
+
- "package-lock.json"
|
|
55
|
+
- "npm-shrinkwrap.json"
|
|
56
|
+
- "yarn.lock"
|
|
57
|
+
- "bun.lock"
|
|
58
|
+
- "bun.lockb"
|
|
59
|
+
- "package.json"
|
|
60
|
+
- ".github/workflows/osv-scan.yml"
|
|
61
|
+
pull_request:
|
|
62
|
+
branches: [main]
|
|
63
|
+
paths:
|
|
64
|
+
- "pnpm-lock.yaml"
|
|
65
|
+
- "package-lock.json"
|
|
66
|
+
- "npm-shrinkwrap.json"
|
|
67
|
+
- "yarn.lock"
|
|
68
|
+
- "bun.lock"
|
|
69
|
+
- "bun.lockb"
|
|
70
|
+
- "package.json"
|
|
71
|
+
- ".github/workflows/osv-scan.yml"
|
|
72
|
+
schedule:
|
|
73
|
+
# 06:47 UTC daily — offset from vuln-scan (06:13) and Zizmor (07:00)
|
|
74
|
+
# so the SCA jobs do not collide.
|
|
75
|
+
- cron: "47 6 * * *"
|
|
76
|
+
workflow_dispatch:
|
|
77
|
+
|
|
78
|
+
permissions: {}
|
|
79
|
+
|
|
80
|
+
concurrency:
|
|
81
|
+
group: osv-scan-${{ github.workflow }}-${{ github.ref }}
|
|
82
|
+
cancel-in-progress: true
|
|
83
|
+
|
|
84
|
+
jobs:
|
|
85
|
+
scan:
|
|
86
|
+
name: OSV.dev + malicious-packages scan
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
timeout-minutes: 15
|
|
89
|
+
permissions:
|
|
90
|
+
contents: read
|
|
91
|
+
|
|
92
|
+
env:
|
|
93
|
+
# Pinned OSV-Scanner release. Bump together with the SHA-256 below.
|
|
94
|
+
# Source: https://github.com/google/osv-scanner/releases/tag/v2.3.8
|
|
95
|
+
OSV_SCANNER_VERSION: "2.3.8"
|
|
96
|
+
# SHA-256 of `osv-scanner_linux_amd64` from the v2.3.8
|
|
97
|
+
# `osv-scanner_SHA256SUMS` asset.
|
|
98
|
+
OSV_SCANNER_SHA256: "bc98e15319ed0d515e3f9235287ba53cdc5535d576d24fd573978ecfe9ab92dc"
|
|
99
|
+
|
|
100
|
+
steps:
|
|
101
|
+
- name: Harden runner
|
|
102
|
+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
|
|
103
|
+
with:
|
|
104
|
+
egress-policy: audit
|
|
105
|
+
disable-sudo: true
|
|
106
|
+
disable-file-monitoring: false
|
|
107
|
+
|
|
108
|
+
- name: Checkout
|
|
109
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
110
|
+
with:
|
|
111
|
+
persist-credentials: false
|
|
112
|
+
show-progress: false
|
|
113
|
+
|
|
114
|
+
- name: Download and verify OSV-Scanner binary
|
|
115
|
+
# Pin the binary by SHA-256 BEFORE executing it. A registry or
|
|
116
|
+
# GitHub Releases compromise that swaps the asset would fail
|
|
117
|
+
# this check and abort the job.
|
|
118
|
+
run: |
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
url="https://github.com/google/osv-scanner/releases/download/v${OSV_SCANNER_VERSION}/osv-scanner_linux_amd64"
|
|
121
|
+
curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \
|
|
122
|
+
--output osv-scanner "${url}"
|
|
123
|
+
echo "${OSV_SCANNER_SHA256} osv-scanner" | sha256sum --check --status
|
|
124
|
+
chmod +x osv-scanner
|
|
125
|
+
./osv-scanner --version
|
|
126
|
+
|
|
127
|
+
- name: Scan lockfiles (OSV.dev + malicious-packages)
|
|
128
|
+
# `scan source --recursive` auto-detects whichever lockfile the
|
|
129
|
+
# scaffold actually ships (pnpm-lock.yaml, package-lock.json,
|
|
130
|
+
# yarn.lock, bun.lock, ...). Exits non-zero if any vulnerability
|
|
131
|
+
# or malware advisory matches a pinned version. This is the
|
|
132
|
+
# second-source gate beyond the package manager's own audit.
|
|
133
|
+
run: |
|
|
134
|
+
set -euo pipefail
|
|
135
|
+
./osv-scanner scan source --recursive --format=table .
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Secret scanning — gitleaks against PRs, pushes to main, and a daily
|
|
2
|
+
# scheduled sweep of the full git history.
|
|
3
|
+
#
|
|
4
|
+
# Why this exists (Aikido "Secrets Detection: A Practical Guide"
|
|
5
|
+
# https://www.aikido.dev/blog/secret-detection-application-security):
|
|
6
|
+
#
|
|
7
|
+
# GitHub-native push protection only flags provider-shaped secrets at
|
|
8
|
+
# push time. Aikido's guide argues that effective secret detection must
|
|
9
|
+
# also scan the **full git history** (not just the latest snapshot) on
|
|
10
|
+
# a regular cadence, because a single leaked credential anywhere in any
|
|
11
|
+
# commit, branch, or tag should be treated as compromised.
|
|
12
|
+
#
|
|
13
|
+
# This workflow runs `gitleaks` on every PR / push against the working
|
|
14
|
+
# tree and, on a daily schedule, sweeps the entire commit history so
|
|
15
|
+
# long-lived secrets that pre-date push-protection enablement (or
|
|
16
|
+
# secrets that slipped past it via rebased / squashed fork PRs) are
|
|
17
|
+
# still surfaced.
|
|
18
|
+
#
|
|
19
|
+
# Hardening (matches the rest of the scaffolded CI bundle):
|
|
20
|
+
#
|
|
21
|
+
# * No new third-party GitHub Action: the gitleaks binary is fetched
|
|
22
|
+
# from the pinned official release and its SHA-256 is verified
|
|
23
|
+
# before execution.
|
|
24
|
+
# * Top-level `permissions: {}`; the single job opts in to
|
|
25
|
+
# `contents: read`.
|
|
26
|
+
# * `step-security/harden-runner` in audit mode, sudo disabled.
|
|
27
|
+
# * `actions/checkout` with `persist-credentials: false`.
|
|
28
|
+
# * `--redact` keeps any matched value out of the public log.
|
|
29
|
+
#
|
|
30
|
+
# When a finding lands:
|
|
31
|
+
# 1. Rotate the credential immediately.
|
|
32
|
+
# 2. Assess scope (what did the secret access?).
|
|
33
|
+
# 3. Remove it from history if your workflow allows (filter-repo /
|
|
34
|
+
# BFG); otherwise treat the repo + every downstream clone as
|
|
35
|
+
# compromised.
|
|
36
|
+
# 4. Add a `.gitleaksignore` entry only for confirmed false positives.
|
|
37
|
+
|
|
38
|
+
name: Secret scan
|
|
39
|
+
|
|
40
|
+
on:
|
|
41
|
+
pull_request:
|
|
42
|
+
push:
|
|
43
|
+
branches: [main]
|
|
44
|
+
schedule:
|
|
45
|
+
- cron: "21 4 * * *"
|
|
46
|
+
workflow_dispatch:
|
|
47
|
+
|
|
48
|
+
permissions: {}
|
|
49
|
+
|
|
50
|
+
concurrency:
|
|
51
|
+
group: secret-scan-${{ github.workflow }}-${{ github.ref }}
|
|
52
|
+
cancel-in-progress: true
|
|
53
|
+
|
|
54
|
+
jobs:
|
|
55
|
+
gitleaks:
|
|
56
|
+
name: gitleaks
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
timeout-minutes: 10
|
|
59
|
+
permissions:
|
|
60
|
+
contents: read
|
|
61
|
+
|
|
62
|
+
env:
|
|
63
|
+
# Pinned gitleaks release — bump in lockstep with the SHA-256 below.
|
|
64
|
+
# Source of truth: https://github.com/gitleaks/gitleaks/releases
|
|
65
|
+
GITLEAKS_VERSION: "8.30.1"
|
|
66
|
+
GITLEAKS_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
|
|
67
|
+
|
|
68
|
+
steps:
|
|
69
|
+
- name: Harden runner
|
|
70
|
+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
|
|
71
|
+
with:
|
|
72
|
+
egress-policy: audit
|
|
73
|
+
disable-sudo: true
|
|
74
|
+
disable-file-monitoring: false
|
|
75
|
+
|
|
76
|
+
- name: Checkout
|
|
77
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
78
|
+
with:
|
|
79
|
+
persist-credentials: false
|
|
80
|
+
show-progress: false
|
|
81
|
+
# Full history for the scheduled sweep so gitleaks can walk
|
|
82
|
+
# every commit; for PR / push runs a shallow clone is fine
|
|
83
|
+
# because the checked-out tree is what gitleaks scans.
|
|
84
|
+
fetch-depth: ${{ github.event_name == 'schedule' && 0 || 1 }}
|
|
85
|
+
|
|
86
|
+
- name: Install gitleaks (verified SHA-256)
|
|
87
|
+
run: |
|
|
88
|
+
set -euo pipefail
|
|
89
|
+
archive="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
|
|
90
|
+
url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/${archive}"
|
|
91
|
+
curl --proto '=https' --tlsv1.2 -sSfL "$url" -o "$archive"
|
|
92
|
+
echo "${GITLEAKS_SHA256} ${archive}" | sha256sum --check --strict
|
|
93
|
+
mkdir -p "$RUNNER_TEMP/gitleaks"
|
|
94
|
+
tar -xzf "$archive" -C "$RUNNER_TEMP/gitleaks" gitleaks
|
|
95
|
+
rm -f "$archive"
|
|
96
|
+
echo "$RUNNER_TEMP/gitleaks" >> "$GITHUB_PATH"
|
|
97
|
+
|
|
98
|
+
- name: gitleaks version
|
|
99
|
+
run: gitleaks version
|
|
100
|
+
|
|
101
|
+
- name: gitleaks (working tree)
|
|
102
|
+
run: gitleaks dir --no-banner --redact --verbose --exit-code 1 .
|
|
103
|
+
|
|
104
|
+
- name: gitleaks (full git history)
|
|
105
|
+
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
|
|
106
|
+
run: gitleaks git --no-banner --redact --verbose --exit-code 1 .
|
|
@@ -41,6 +41,18 @@ Do not write `.js` here — that's the Node NodeNext convention and will fail to
|
|
|
41
41
|
6. Every new route ships with a test that covers a happy path and at least one unhappy path.
|
|
42
42
|
7. After any route change: `bun run gen:openapi && bun run gen:client && bun run typecheck && bun test`.
|
|
43
43
|
|
|
44
|
+
## Secure-by-default (do not let an AI strip these)
|
|
45
|
+
|
|
46
|
+
Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): *"If you tell an AI to make something work, it might remove the very security checks that protect you."* When a guard rejects a request, **satisfy it, do not delete it.**
|
|
47
|
+
|
|
48
|
+
- Keep `secureHeaders()`, `requestId()`, `rateLimit()` registered, and `bodyLimitBytes` / `requestTimeoutMs` set on `new App({...})`. Tighten per-route; never raise globally to pass a test.
|
|
49
|
+
- Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
|
|
50
|
+
- Every protected route attaches an auth `beforeHandle` and ships an unhappy-path test proving an unauthenticated request returns `401` (and wrong scope returns `403`) — the HTTP-boundary equivalent of Supabase's pgTAP policy tests.
|
|
51
|
+
- JWT verifiers keep an explicit `algorithms` allowlist; never trust the token's `alg` header, never allow `none`, always check `exp` / `nbf`.
|
|
52
|
+
- Credential / HMAC comparisons use `timingSafeEqual`, never `===`. Throw typed errors from `@daloyjs/core` so problem+json redacts in prod; never return raw stack traces.
|
|
53
|
+
- `.env`, secrets, and private keys never get committed — the template `_gitignore` is the source of truth.
|
|
54
|
+
- Do not bypass safety checks (`--no-verify`, `bun install --trust`) without recording the reason in the PR.
|
|
55
|
+
|
|
44
56
|
## Process expectations
|
|
45
57
|
|
|
46
58
|
- Quality gates must pass before declaring work done: `bun run typecheck` and `bun test`.
|
|
@@ -8,3 +8,8 @@ coverage/
|
|
|
8
8
|
!.env.example
|
|
9
9
|
generated/
|
|
10
10
|
bun.lockb
|
|
11
|
+
|
|
12
|
+
# An `npm-shrinkwrap.json` published with a package overrides the
|
|
13
|
+
# consumer's lockfile and can pin transitives to unsigned tarballs.
|
|
14
|
+
# See https://socket.dev/blog/understanding-the-security-concerns-of-npm-shrinkwrap
|
|
15
|
+
npm-shrinkwrap.json
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
"test": "bun test",
|
|
14
14
|
"gen:openapi": "bun run scripts/dump-openapi.ts",
|
|
15
15
|
"gen:client": "openapi-ts",
|
|
16
|
-
"gen": "pnpm gen:openapi && pnpm gen:client"
|
|
16
|
+
"gen": "pnpm gen:openapi && pnpm gen:client",
|
|
17
|
+
"audit": "pnpm audit --prod"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
|
-
"@daloyjs/core": "^0.
|
|
20
|
+
"@daloyjs/core": "^0.35.1",
|
|
20
21
|
"zod": "^4.4.3"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
@@ -31,6 +31,19 @@ A [DaloyJS](https://daloyjs.dev) REST API deployed to **Cloudflare Workers**. **
|
|
|
31
31
|
8. Long-running work belongs in `ctx.waitUntil(...)`, not blocking the response.
|
|
32
32
|
9. Every new route ships with a test that covers a happy path and at least one unhappy path.
|
|
33
33
|
|
|
34
|
+
## Secure-by-default (do not let an AI strip these)
|
|
35
|
+
|
|
36
|
+
Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): *"If you tell an AI to make something work, it might remove the very security checks that protect you."* When a guard rejects a request, **satisfy it, do not delete it.**
|
|
37
|
+
|
|
38
|
+
- Keep `secureHeaders()`, `requestId()`, `rateLimit()` registered, and `bodyLimitBytes` / `requestTimeoutMs` set on `new App({...})`. For production, add Cloudflare's native rate-limit binding **in addition to** the in-memory limiter, not instead of it.
|
|
39
|
+
- Read secrets and bindings (KV, D1, R2) from the `env` argument; never hard-code, never log them.
|
|
40
|
+
- Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
|
|
41
|
+
- Every protected route attaches an auth `beforeHandle` and ships an unhappy-path test proving an unauthenticated request returns `401` (and wrong scope returns `403`) — the HTTP-boundary equivalent of Supabase's pgTAP policy tests.
|
|
42
|
+
- JWT verifiers keep an explicit `algorithms` allowlist; never trust the token's `alg` header, never allow `none`, always check `exp` / `nbf`.
|
|
43
|
+
- Credential / HMAC comparisons use `crypto.subtle.timingSafeEqual`, never `===`. Throw typed errors from `@daloyjs/core` so problem+json redacts in prod; never return raw stack traces.
|
|
44
|
+
- Keep `compatibility_date` pinned; do not enable `nodejs_compat` unless a feature requires it.
|
|
45
|
+
- `.env`, `.dev.vars`, secrets, private keys: never commit. Use `wrangler secret put` for production secrets.
|
|
46
|
+
|
|
34
47
|
## Process expectations
|
|
35
48
|
|
|
36
49
|
- Quality gates must pass before declaring work done: `pnpm typecheck` and `pnpm test`.
|
|
@@ -4,4 +4,13 @@ dist/
|
|
|
4
4
|
.DS_Store
|
|
5
5
|
*.log
|
|
6
6
|
.env
|
|
7
|
+
.env.*
|
|
8
|
+
!.env.example
|
|
7
9
|
.dev.vars
|
|
10
|
+
.dev.vars.*
|
|
11
|
+
!.dev.vars.example
|
|
12
|
+
|
|
13
|
+
# An `npm-shrinkwrap.json` published with a package overrides the
|
|
14
|
+
# consumer's lockfile and can pin transitives to unsigned tarballs.
|
|
15
|
+
# See https://socket.dev/blog/understanding-the-security-concerns-of-npm-shrinkwrap
|
|
16
|
+
npm-shrinkwrap.json
|
|
@@ -12,5 +12,6 @@ minimum-release-age=1440
|
|
|
12
12
|
|
|
13
13
|
# Block postinstall/preinstall/prepare hooks from transitive deps —
|
|
14
14
|
# the primary execution channel for chalk/debug, node-ipc, Shai-Hulud.
|
|
15
|
-
#
|
|
15
|
+
# If you later adopt pnpm's build-script allowlist, keep it in
|
|
16
|
+
# pnpm-workspace.yaml instead of turning this off.
|
|
16
17
|
ignore-scripts=true
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
"dev": "wrangler dev",
|
|
8
8
|
"deploy": "wrangler deploy",
|
|
9
9
|
"typecheck": "tsc --noEmit",
|
|
10
|
-
"test": "node --import tsx/esm --test tests/**/*.test.ts"
|
|
10
|
+
"test": "node --import tsx/esm --test tests/**/*.test.ts",
|
|
11
|
+
"audit": "pnpm audit --prod"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
|
-
"@daloyjs/core": "^0.
|
|
14
|
+
"@daloyjs/core": "^0.35.1",
|
|
14
15
|
"zod": "^4.4.3"
|
|
15
16
|
},
|
|
16
17
|
"devDependencies": {
|
|
@@ -33,6 +33,18 @@ The typed Hey API SDK is generated outside Deno (Hey API has no Deno entrypoint
|
|
|
33
33
|
7. Every new route ships with a test that covers a happy path and at least one unhappy path.
|
|
34
34
|
8. After any route change: `deno task gen:openapi && deno task typecheck && deno task test`.
|
|
35
35
|
|
|
36
|
+
## Secure-by-default (do not let an AI strip these)
|
|
37
|
+
|
|
38
|
+
Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): *"If you tell an AI to make something work, it might remove the very security checks that protect you."* When a guard rejects a request, **satisfy it, do not delete it.**
|
|
39
|
+
|
|
40
|
+
- Keep `secureHeaders()`, `requestId()`, `rateLimit()` registered, and `bodyLimitBytes` / `requestTimeoutMs` set on `new App({...})`. Tighten per-route; never raise globally to pass a test.
|
|
41
|
+
- Keep Deno permissions narrow. Never add `--allow-all`; never broaden `--allow-net` / `--allow-read` / `--allow-env` to silence a prompt — add the specific host / path / var.
|
|
42
|
+
- Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
|
|
43
|
+
- Every protected route attaches an auth `beforeHandle` and ships an unhappy-path test proving an unauthenticated request returns `401` (and wrong scope returns `403`) — the HTTP-boundary equivalent of Supabase's pgTAP policy tests.
|
|
44
|
+
- JWT verifiers keep an explicit `algorithms` allowlist; never trust the token's `alg` header, never allow `none`, always check `exp` / `nbf`.
|
|
45
|
+
- Credential / HMAC comparisons use a constant-time comparison, never `===`. Throw typed errors from `@daloyjs/core` so problem+json redacts in prod; never return raw stack traces.
|
|
46
|
+
- `.env`, secrets, and private keys never get committed — the template `_gitignore` is the source of truth.
|
|
47
|
+
|
|
36
48
|
## Process expectations
|
|
37
49
|
|
|
38
50
|
- Quality gates must pass before declaring work done: `deno task typecheck` and `deno task test`.
|
|
@@ -5,3 +5,8 @@ node_modules/
|
|
|
5
5
|
.env.*
|
|
6
6
|
!.env.example
|
|
7
7
|
generated/
|
|
8
|
+
|
|
9
|
+
# An `npm-shrinkwrap.json` published with a package overrides the
|
|
10
|
+
# consumer's lockfile and can pin transitives to unsigned tarballs.
|
|
11
|
+
# See https://socket.dev/blog/understanding-the-security-concerns-of-npm-shrinkwrap
|
|
12
|
+
npm-shrinkwrap.json
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"gen:openapi": "deno run --allow-net --allow-env --allow-read --allow-write scripts/dump-openapi.ts"
|
|
9
9
|
},
|
|
10
10
|
"imports": {
|
|
11
|
-
"@daloyjs/core": "npm:@daloyjs/core@^0.
|
|
12
|
-
"@daloyjs/core/": "npm:@daloyjs/core@^0.
|
|
11
|
+
"@daloyjs/core": "npm:@daloyjs/core@^0.35.1",
|
|
12
|
+
"@daloyjs/core/": "npm:@daloyjs/core@^0.35.1/",
|
|
13
13
|
"zod": "npm:zod@^4.4.3"
|
|
14
14
|
},
|
|
15
15
|
"compilerOptions": {
|
|
@@ -45,6 +45,18 @@ This is the official Node.js ESM convention — TypeScript rewrites the specifie
|
|
|
45
45
|
6. Every new route ships with a test that covers a happy path and at least one unhappy path.
|
|
46
46
|
7. After any route change: `pnpm gen && pnpm typecheck && pnpm test`.
|
|
47
47
|
|
|
48
|
+
## Secure-by-default (do not let an AI strip these)
|
|
49
|
+
|
|
50
|
+
Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): *"If you tell an AI to make something work, it might remove the very security checks that protect you."* When a guard rejects a request, **satisfy it, do not delete it.**
|
|
51
|
+
|
|
52
|
+
- Keep `secureHeaders()`, `requestId()`, `rateLimit()` registered, and `bodyLimitBytes` / `requestTimeoutMs` set on `new App({...})`. Tighten per-route; never raise globally to pass a test.
|
|
53
|
+
- Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
|
|
54
|
+
- Every protected route attaches an auth `beforeHandle` and ships an unhappy-path test proving an unauthenticated request returns `401` (and wrong scope returns `403`) — the HTTP-boundary equivalent of Supabase's pgTAP policy tests.
|
|
55
|
+
- JWT verifiers keep an explicit `algorithms` allowlist; never trust the token's `alg` header, never allow `none`, always check `exp` / `nbf`.
|
|
56
|
+
- Credential / HMAC comparisons use `timingSafeEqual`, never `===`. Throw typed errors from `@daloyjs/core` so problem+json redacts in prod; never return raw stack traces.
|
|
57
|
+
- `.env`, secrets, and private keys never get committed — the template `_gitignore` is the source of truth.
|
|
58
|
+
- Do not bypass safety checks (`--no-verify`, `--ignore-scripts=false`, lowering the 24h `minimum-release-age` in `.npmrc`) without recording the reason in the PR.
|
|
59
|
+
|
|
48
60
|
## Process expectations
|
|
49
61
|
|
|
50
62
|
- Quality gates must pass before declaring work done: `pnpm typecheck` and `pnpm test`.
|
|
@@ -7,3 +7,8 @@ coverage/
|
|
|
7
7
|
.env.*
|
|
8
8
|
!.env.example
|
|
9
9
|
generated/
|
|
10
|
+
|
|
11
|
+
# An `npm-shrinkwrap.json` published with a package overrides the
|
|
12
|
+
# consumer's lockfile and can pin transitives to unsigned tarballs.
|
|
13
|
+
# See https://socket.dev/blog/understanding-the-security-concerns-of-npm-shrinkwrap
|
|
14
|
+
npm-shrinkwrap.json
|
|
@@ -16,6 +16,6 @@ minimum-release-age=1440
|
|
|
16
16
|
|
|
17
17
|
# postinstall / preinstall / prepare hooks from transitive deps are the
|
|
18
18
|
# main execution channel used by chalk/debug, node-ipc, and Shai-Hulud
|
|
19
|
-
# malware.
|
|
20
|
-
#
|
|
19
|
+
# malware. If you later adopt pnpm's build-script allowlist, keep it in
|
|
20
|
+
# pnpm-workspace.yaml instead of turning this off.
|
|
21
21
|
ignore-scripts=true
|
|
@@ -40,6 +40,18 @@ This is the official Node.js ESM convention — TypeScript rewrites the specifie
|
|
|
40
40
|
7. The catch-all `api/[...path].ts` must remain a catch-all so DaloyJS handles routing.
|
|
41
41
|
8. Every new route ships with a test that covers a happy path and at least one unhappy path.
|
|
42
42
|
|
|
43
|
+
## Secure-by-default (do not let an AI strip these)
|
|
44
|
+
|
|
45
|
+
Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): *"If you tell an AI to make something work, it might remove the very security checks that protect you."* When a guard rejects a request, **satisfy it, do not delete it.**
|
|
46
|
+
|
|
47
|
+
- Keep `secureHeaders()`, `requestId()`, `rateLimit()` registered, and `bodyLimitBytes` / `requestTimeoutMs` set on `new App({...})`. For production, back the limiter with Vercel KV **in addition to** the in-memory limiter (which resets per instance).
|
|
48
|
+
- Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
|
|
49
|
+
- Every protected route attaches an auth `beforeHandle` and ships an unhappy-path test proving an unauthenticated request returns `401` (and wrong scope returns `403`) — the HTTP-boundary equivalent of Supabase's pgTAP policy tests.
|
|
50
|
+
- JWT verifiers keep an explicit `algorithms` allowlist; never trust the token's `alg` header, never allow `none`, always check `exp` / `nbf`.
|
|
51
|
+
- Credential / HMAC comparisons use `crypto.subtle.timingSafeEqual`, never `===`. Throw typed errors from `@daloyjs/core` so problem+json redacts in prod; never return raw stack traces.
|
|
52
|
+
- Keep `api/[...path].ts` a catch-all so DaloyJS owns routing — do not split into per-path files that bypass the middleware chain.
|
|
53
|
+
- `.env`, `.env.local`, secrets, private keys: never commit. Use `vercel env` for production secrets.
|
|
54
|
+
|
|
43
55
|
## Process expectations
|
|
44
56
|
|
|
45
57
|
- Quality gates must pass before declaring work done: `pnpm typecheck` and `pnpm test`.
|
|
@@ -7,3 +7,8 @@ coverage/
|
|
|
7
7
|
.env
|
|
8
8
|
.env.*
|
|
9
9
|
!.env.example
|
|
10
|
+
|
|
11
|
+
# An `npm-shrinkwrap.json` published with a package overrides the
|
|
12
|
+
# consumer's lockfile and can pin transitives to unsigned tarballs.
|
|
13
|
+
# See https://socket.dev/blog/understanding-the-security-concerns-of-npm-shrinkwrap
|
|
14
|
+
npm-shrinkwrap.json
|
|
@@ -12,5 +12,6 @@ minimum-release-age=1440
|
|
|
12
12
|
|
|
13
13
|
# Block postinstall/preinstall/prepare hooks from transitive deps —
|
|
14
14
|
# the primary execution channel for chalk/debug, node-ipc, Shai-Hulud.
|
|
15
|
-
#
|
|
15
|
+
# If you later adopt pnpm's build-script allowlist, keep it in
|
|
16
|
+
# pnpm-workspace.yaml instead of turning this off.
|
|
16
17
|
ignore-scripts=true
|