create-daloy 0.34.2 → 0.35.0

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.
Files changed (38) hide show
  1. package/README.md +62 -6
  2. package/bin/create-daloy.mjs +344 -25
  3. package/package.json +2 -2
  4. package/sbom.cdx.json +13 -9
  5. package/sbom.spdx.json +5 -5
  6. package/templates/_ci/deno/SECURITY.md +31 -2
  7. package/templates/_ci/deno/_github/CODEOWNERS +2 -0
  8. package/templates/_ci/deno/_github/workflows/container-scan.yml +42 -8
  9. package/templates/_ci/deno/_github/workflows/deploy.yml +183 -0
  10. package/templates/_ci/deno/_github/workflows/opengrep.yml +137 -0
  11. package/templates/_ci/deno/_github/workflows/osv-scan.yml +121 -0
  12. package/templates/_ci/deno/_github/workflows/secret-scan.yml +106 -0
  13. package/templates/_ci/node/SECURITY.md +100 -1
  14. package/templates/_ci/node/_github/CODEOWNERS +3 -0
  15. package/templates/_ci/node/_github/workflows/container-scan.yml +46 -10
  16. package/templates/_ci/node/_github/workflows/dast.yml +177 -0
  17. package/templates/_ci/node/_github/workflows/deploy.yml +94 -0
  18. package/templates/_ci/node/_github/workflows/opengrep.yml +169 -0
  19. package/templates/_ci/node/_github/workflows/osv-scan.yml +135 -0
  20. package/templates/_ci/node/_github/workflows/secret-scan.yml +106 -0
  21. package/templates/bun-basic/AGENTS.md +12 -0
  22. package/templates/bun-basic/_gitignore +5 -0
  23. package/templates/bun-basic/package.json +3 -2
  24. package/templates/cloudflare-worker/AGENTS.md +13 -0
  25. package/templates/cloudflare-worker/_gitignore +9 -0
  26. package/templates/cloudflare-worker/_npmrc +2 -1
  27. package/templates/cloudflare-worker/package.json +3 -2
  28. package/templates/deno-basic/AGENTS.md +12 -0
  29. package/templates/deno-basic/_gitignore +5 -0
  30. package/templates/deno-basic/deno.json +2 -2
  31. package/templates/node-basic/AGENTS.md +12 -0
  32. package/templates/node-basic/_gitignore +5 -0
  33. package/templates/node-basic/_npmrc +2 -2
  34. package/templates/node-basic/package.json +1 -1
  35. package/templates/vercel-edge/AGENTS.md +12 -0
  36. package/templates/vercel-edge/_gitignore +5 -0
  37. package/templates/vercel-edge/_npmrc +2 -1
  38. package/templates/vercel-edge/package.json +3 -2
package/sbom.cdx.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.5",
4
- "serialNumber": "urn:uuid:6244f261-e99d-5b5f-9ccb-5a10bc0de8b7",
4
+ "serialNumber": "urn:uuid:94862215-9d79-5a0f-9ee1-81c52885d00f",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2026-05-23T09:06:22.609Z",
7
+ "timestamp": "2026-05-24T23:40:28.395Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "DaloyJS",
11
11
  "name": "daloy-generate-sbom",
12
- "version": "0.34.2"
12
+ "version": "0.35.0"
13
13
  }
14
14
  ],
15
15
  "authors": [],
16
16
  "component": {
17
17
  "type": "library",
18
- "bom-ref": "pkg:npm/create-daloy@0.34.2",
18
+ "bom-ref": "pkg:npm/create-daloy@0.35.0",
19
19
  "name": "create-daloy",
20
- "version": "0.34.2",
20
+ "version": "0.35.0",
21
21
  "description": "Scaffold a new DaloyJS project. Run with `pnpm create daloy`, `npm create daloy@latest`, `yarn create daloy`, or `bun create daloy`.",
22
- "purl": "pkg:npm/create-daloy@0.34.2",
22
+ "purl": "pkg:npm/create-daloy@0.35.0",
23
23
  "licenses": [
24
24
  {
25
25
  "license": {
@@ -35,12 +35,16 @@
35
35
  {
36
36
  "type": "website",
37
37
  "url": "https://github.com/daloyjs/daloy/tree/main/packages/create-daloy#readme"
38
+ },
39
+ {
40
+ "type": "distribution",
41
+ "url": "https://www.npmjs.com/package/create-daloy"
38
42
  }
39
43
  ],
40
44
  "swid": {
41
- "tagId": "swidtag-create-daloy-0.34.2",
45
+ "tagId": "swidtag-create-daloy-0.35.0",
42
46
  "name": "create-daloy",
43
- "version": "0.34.2",
47
+ "version": "0.35.0",
44
48
  "tagVersion": 0,
45
49
  "patch": false
46
50
  }
@@ -49,7 +53,7 @@
49
53
  "components": [],
50
54
  "dependencies": [
51
55
  {
52
- "ref": "pkg:npm/create-daloy@0.34.2",
56
+ "ref": "pkg:npm/create-daloy@0.35.0",
53
57
  "dependsOn": []
54
58
  }
55
59
  ]
package/sbom.spdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "create-daloy-0.34.2",
6
- "documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-0.34.2-6244f261-e99d-5b5f-9ccb-5a10bc0de8b7",
5
+ "name": "create-daloy-0.35.0",
6
+ "documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-0.35.0-94862215-9d79-5a0f-9ee1-81c52885d00f",
7
7
  "creationInfo": {
8
- "created": "2026-05-23T09:06:22.609Z",
8
+ "created": "2026-05-24T23:40:28.395Z",
9
9
  "creators": [
10
10
  "Tool: daloy-generate-sbom",
11
11
  "Organization: DaloyJS"
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "SPDXID": "SPDXRef-Package-create-daloy",
18
18
  "name": "create-daloy",
19
- "versionInfo": "0.34.2",
19
+ "versionInfo": "0.35.0",
20
20
  "downloadLocation": "https://github.com/daloyjs/daloy",
21
21
  "filesAnalyzed": false,
22
22
  "licenseConcluded": "MIT",
@@ -27,7 +27,7 @@
27
27
  {
28
28
  "referenceCategory": "PACKAGE-MANAGER",
29
29
  "referenceType": "purl",
30
- "referenceLocator": "pkg:npm/create-daloy@0.34.2"
30
+ "referenceLocator": "pkg:npm/create-daloy@0.35.0"
31
31
  }
32
32
  ]
33
33
  }
@@ -23,14 +23,43 @@ The `--with-ci` bundle adds these defaults:
23
23
  - If the generated project keeps the `Dockerfile`, container scanning lints it
24
24
  with hadolint, scans the source tree and built image with Trivy, and warns
25
25
  when `FROM` lines are not pinned to an immutable `@sha256:` digest.
26
- - CodeQL, OpenSSF Scorecard, zizmor, Dependabot for GitHub Actions and Docker
26
+ - CodeQL, Opengrep (second SAST engine, Aikido's LGPL-2.1 fork of
27
+ Semgrep, verified via sigstore cosign before execution), OpenSSF
28
+ Scorecard, zizmor, Dependabot for GitHub Actions and Docker
27
29
  base images, and CODEOWNERS are generated.
30
+ - A `secret-scan.yml` workflow runs [gitleaks](https://github.com/gitleaks/gitleaks) against the working tree on every PR / push and against the **full git history** on a daily schedule. The gitleaks binary is downloaded from a pinned official release and verified by SHA-256 before execution so the scan does not introduce a new third-party action into the supply chain. Findings block the merge; matched values are redacted from the public log. The rationale (and the remediation playbook for a confirmed leak) follows Aikido's [Secrets Detection guide](https://www.aikido.dev/blog/secret-detection-application-security): a secret in any commit, branch, or tag should be treated as compromised, and detection must consider the entire history — not just the latest snapshot — alongside GitHub-native push protection.
31
+
32
+ ## Cloud posture (operator checklist)
33
+
34
+ The framework cannot author your cloud configuration for you, but the
35
+ [Aikido "Top 7 Cloud Security Vulnerabilities"](https://www.aikido.dev/blog/top-cloud-security-vulnerabilities)
36
+ write-up maps cleanly onto a short checklist. Adopt these alongside the
37
+ container and CI defaults above:
38
+
39
+ - **IMDSv2 only.** On EC2 / equivalent, require token-based IMDSv2 and disable
40
+ IMDSv1. Combined with the framework's `fetchGuard()` on user-controlled
41
+ outbound fetches, this closes the Capital One 2019 / Pandoc CVE-2025-51591
42
+ SSRF → IAM chain.
43
+ - **Least-privilege execution role.** Scope the workload's IAM role to the
44
+ minimum actions and resource ARNs the handler actually calls (e.g.
45
+ `s3:GetObject` on a single bucket prefix, never `s3:*`).
46
+ - **Pod security on Kubernetes.** `runAsNonRoot: true`,
47
+ `readOnlyRootFilesystem: true`, `allowPrivilegeEscalation: false`,
48
+ `capabilities: { drop: ["ALL"] }`, and `automountServiceAccountToken: false`
49
+ on pods that do not call the kube API.
50
+ - **Network segmentation.** Keep dev / staging / prod in separate accounts /
51
+ projects. Apply default-deny `NetworkPolicy` or Security Group egress so a
52
+ compromised workload cannot freely reach the metadata service or the
53
+ database tier.
54
+ - **Log agent isolation.** Run FluentBit / Vector under its own ServiceAccount
55
+ with read-only access to the log path, pin the agent image to a digest, and
56
+ subscribe to its CVE feed.
28
57
 
29
58
  ## Required repository settings
30
59
 
31
60
  Before relying on these files for a company project:
32
61
 
33
62
  1. Replace `@your-org/security-team` in `.github/CODEOWNERS` or pass `--code-owner` when scaffolding.
34
- 2. Protect the `main` branch and require the CI, CodeQL, Scorecard, and zizmor checks.
63
+ 2. Protect the `main` branch and require the CI, CodeQL, Opengrep, Scorecard, and zizmor checks.
35
64
  3. Enable GitHub secret scanning and push protection.
36
65
  4. Keep Deno permissions narrow; do not switch tasks to `--allow-all`.
@@ -6,6 +6,8 @@
6
6
  # Workflow / CI / CD.
7
7
  /.github/ __CODE_OWNER__
8
8
  /.github/workflows/ __CODE_OWNER__
9
+ /.github/workflows/osv-scan.yml __CODE_OWNER__
10
+ /.github/workflows/secret-scan.yml __CODE_OWNER__
9
11
  /.github/dependabot.yml __CODE_OWNER__
10
12
  /.github/CODEOWNERS __CODE_OWNER__
11
13
 
@@ -1,12 +1,20 @@
1
- # Container security scan generated by create-daloy --with-ci.
1
+ # Container + IaC security scan generated by create-daloy --with-ci.
2
2
  #
3
- # The Deno template has no npm package manager, but its Dockerfile still
4
- # deserves the same container review as Node-based scaffolds:
3
+ # The Deno template has no npm package manager, but its Dockerfile and any
4
+ # IaC files (Terraform / Kubernetes / Helm / CloudFormation) the project
5
+ # adds still deserve the same review as Node-based scaffolds. Modeled on
6
+ # Aikido's "IaC security scanning for Terraform & Kubernetes
7
+ # misconfigurations" article
8
+ # (https://www.aikido.dev/blog/iac-security-scanning-terraform-kubernetes-misconfigurations):
5
9
  #
6
10
  # - hadolint lints the Dockerfile for known anti-patterns and unsafe
7
11
  # instructions (CIS Docker Benchmark coverage).
8
- # - Trivy scans the source tree (config + secrets + vulnerable lockfile
9
- # entries), then scans the built image for OS + language CVEs.
12
+ # - Trivy filesystem scan with the `misconfig` scanner enabled catches
13
+ # IaC misconfigurations (Terraform, Kubernetes, Helm, Dockerfile,
14
+ # CloudFormation) plus secrets and vulnerable lockfile entries. It
15
+ # runs even when no Dockerfile is present, so a project that only
16
+ # ships Terraform / K8s still gets the Aikido-style posture review.
17
+ # - Trivy image scan covers OS + language CVEs once the image is built.
10
18
  # - SARIF results are uploaded to the Code Scanning tab so findings show
11
19
  # up in the same place as CodeQL.
12
20
 
@@ -19,6 +27,17 @@ on:
19
27
  - ".dockerignore"
20
28
  - "deno.json"
21
29
  - "deno.lock"
30
+ - "**/*.tf"
31
+ - "**/*.tfvars"
32
+ - "**/*.hcl"
33
+ - "k8s/**"
34
+ - "helm/**"
35
+ - "**/deployment.yaml"
36
+ - "**/deployment.yml"
37
+ - "**/kustomization.yaml"
38
+ - "**/kustomization.yml"
39
+ - "**/Chart.yaml"
40
+ - "**/values.yaml"
22
41
  - ".github/workflows/container-scan.yml"
23
42
  push:
24
43
  branches: [main]
@@ -27,6 +46,17 @@ on:
27
46
  - ".dockerignore"
28
47
  - "deno.json"
29
48
  - "deno.lock"
49
+ - "**/*.tf"
50
+ - "**/*.tfvars"
51
+ - "**/*.hcl"
52
+ - "k8s/**"
53
+ - "helm/**"
54
+ - "**/deployment.yaml"
55
+ - "**/deployment.yml"
56
+ - "**/kustomization.yaml"
57
+ - "**/kustomization.yml"
58
+ - "**/Chart.yaml"
59
+ - "**/values.yaml"
30
60
  - ".github/workflows/container-scan.yml"
31
61
  schedule:
32
62
  # Weekly run catches newly-disclosed base-image CVEs even when the
@@ -114,12 +144,17 @@ jobs:
114
144
  sarif_file: hadolint.sarif
115
145
  category: hadolint
116
146
 
117
- - name: Trivy filesystem scan (config + secrets + vulns)
118
- if: steps.detect.outputs.present == 'true'
147
+ - name: Trivy filesystem scan (IaC misconfig + secrets + vulns)
148
+ # `misconfig` is the IaC posture scanner — Terraform, Kubernetes,
149
+ # Helm, Dockerfile, CloudFormation — per Aikido's "IaC security
150
+ # scanning for Terraform & Kubernetes misconfigurations" article.
151
+ # It runs unconditionally so a project that only ships Terraform
152
+ # or K8s (no Dockerfile) still gets reviewed on every PR.
119
153
  uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
120
154
  with:
121
155
  scan-type: fs
122
156
  scan-ref: .
157
+ scanners: vuln,secret,misconfig
123
158
  severity: HIGH,CRITICAL
124
159
  ignore-unfixed: true
125
160
  format: sarif
@@ -127,7 +162,6 @@ jobs:
127
162
  exit-code: "0"
128
163
 
129
164
  - name: Upload Trivy filesystem SARIF
130
- if: steps.detect.outputs.present == 'true'
131
165
  uses: github/codeql-action/upload-sarif@52485aec7be33610227643b0fe83936b8b5f061a # v3
132
166
  with:
133
167
  sarif_file: trivy-fs.sarif
@@ -0,0 +1,183 @@
1
+ # Starter deployment workflow generated by create-daloy --with-ci.
2
+ #
3
+ # This workflow is intentionally manual-only so a fresh scaffold does not fail
4
+ # on every push before you wire in environment protection. Once you are ready,
5
+ # add your preferred push/tag trigger.
6
+ #
7
+ # Default target: GitHub Container Registry (GHCR).
8
+ # No extra secret is required; the workflow uses the repo-scoped GITHUB_TOKEN.
9
+ # After the image is published, point your platform at GHCR or replace the
10
+ # final push steps with your provider's deploy command.
11
+ #
12
+ # Every pushed image is also signed and attested with Sigstore Cosign
13
+ # (keyless / OIDC) and an SPDX SBOM, so consumers can verify provenance
14
+ # with `cosign verify` and `cosign verify-attestation --type spdxjson`
15
+ # instead of trusting the registry alone. See Aikido's "Container
16
+ # Security Best Practices" (https://www.aikido.dev/blog/container-security-best-practices)
17
+ # for why signed images + SBOM attestation are the supply-chain floor.
18
+ #
19
+ # Optional container-host handoff examples:
20
+ # - Fly.io: install flyctl in a later step and run
21
+ # flyctl deploy --image "$IMAGE_REPO@${IMAGE_DIGEST}" --remote-only
22
+ # - Render: create an image-backed service that tracks
23
+ # ghcr.io/<owner>/<repo>@${IMAGE_DIGEST}
24
+
25
+ name: Deploy
26
+
27
+ on:
28
+ workflow_dispatch:
29
+
30
+ permissions: {}
31
+
32
+ concurrency:
33
+ group: deploy-${{ github.workflow }}-${{ github.ref }}
34
+ cancel-in-progress: false
35
+
36
+ jobs:
37
+ verify:
38
+ name: Verify before deploy
39
+ runs-on: ubuntu-latest
40
+ timeout-minutes: 20
41
+ permissions:
42
+ contents: read
43
+
44
+ steps:
45
+ - name: Harden runner
46
+ uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
47
+ with:
48
+ egress-policy: audit
49
+ disable-sudo: true
50
+
51
+ - name: Checkout
52
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
53
+ with:
54
+ persist-credentials: false
55
+ show-progress: false
56
+
57
+ - name: Set up Deno
58
+ uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4
59
+ with:
60
+ deno-version: v2.x
61
+
62
+ - name: Typecheck
63
+ run: deno task typecheck
64
+
65
+ - name: Test
66
+ run: deno task test
67
+
68
+ deploy:
69
+ name: Publish container image
70
+ needs: verify
71
+ # Lock production deploys to main or a tag push. `workflow_dispatch`
72
+ # accepts any branch, so this guard stops an accidental dispatch from a
73
+ # feature branch from shipping unreviewed code.
74
+ if: github.ref == 'refs/heads/main' || github.ref_type == 'tag'
75
+ runs-on: ubuntu-latest
76
+ timeout-minutes: 20
77
+ environment:
78
+ name: production
79
+ permissions:
80
+ contents: read
81
+ # `packages: write` lets us push to GHCR; `id-token: write` lets
82
+ # Cosign mint a short-lived OIDC identity for keyless image signing
83
+ # + SBOM attestation. Both are scoped to this single job — the
84
+ # top-level workflow still has `permissions: {}`.
85
+ packages: write
86
+ id-token: write
87
+
88
+ steps:
89
+ - name: Harden runner
90
+ uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
91
+ with:
92
+ egress-policy: audit
93
+ disable-sudo: true
94
+
95
+ - name: Checkout
96
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
97
+ with:
98
+ persist-credentials: false
99
+ show-progress: false
100
+
101
+ - name: Log in to GHCR
102
+ env:
103
+ GH_TOKEN: ${{ github.token }}
104
+ run: |
105
+ set -eu
106
+ echo "$GH_TOKEN" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
107
+
108
+ - name: Derive image name
109
+ run: |
110
+ set -eu
111
+ owner="${GITHUB_REPOSITORY_OWNER,,}"
112
+ repo="${GITHUB_REPOSITORY#*/}"
113
+ repo="${repo,,}"
114
+ echo "IMAGE_REPO=ghcr.io/${owner}/${repo}" >> "$GITHUB_ENV"
115
+
116
+ - name: Build image
117
+ env:
118
+ DOCKER_BUILDKIT: "1"
119
+ run: |
120
+ set -eu
121
+ docker build \
122
+ --tag "$IMAGE_REPO:sha-${GITHUB_SHA}" \
123
+ --tag "$IMAGE_REPO:latest" \
124
+ .
125
+
126
+ - name: Push image
127
+ run: |
128
+ set -eu
129
+ docker push "$IMAGE_REPO:sha-${GITHUB_SHA}"
130
+ docker push "$IMAGE_REPO:latest"
131
+
132
+ - name: Resolve pushed image digest
133
+ # Resolve and pin the immutable digest the rest of the workflow
134
+ # signs and attests against. Signing a mutable tag would let any
135
+ # later push silently re-point a "verified" tag at attacker
136
+ # content; binding cosign + the SBOM attestation to
137
+ # ${IMAGE_REPO}@sha256:<digest> closes that race.
138
+ run: |
139
+ set -eu
140
+ digest="$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE_REPO:sha-${GITHUB_SHA}" | awk -F@ '{print $2}')"
141
+ if [ -z "$digest" ]; then
142
+ echo "::error::Failed to resolve image digest for $IMAGE_REPO:sha-${GITHUB_SHA}" >&2
143
+ exit 1
144
+ fi
145
+ echo "IMAGE_DIGEST=$digest" >> "$GITHUB_ENV"
146
+ echo "IMAGE_REF=$IMAGE_REPO@$digest" >> "$GITHUB_ENV"
147
+ echo "::notice::Signed image reference: $IMAGE_REPO@$digest"
148
+
149
+ - name: Install Cosign
150
+ # SHA-pinned per the project's actions-pinning policy. v4.1.2,
151
+ # commit 6f9f17788090df1f26f669e9d70d6ae9567deba6.
152
+ uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
153
+
154
+ - name: Sign image with Cosign (keyless / OIDC)
155
+ # Keyless signing uses the workflow's `id-token` OIDC identity
156
+ # — no long-lived signing key to leak. Verifiers can pin to
157
+ # `--certificate-identity` matching this workflow URL.
158
+ env:
159
+ COSIGN_EXPERIMENTAL: "1"
160
+ run: |
161
+ set -eu
162
+ cosign sign --yes "$IMAGE_REF"
163
+
164
+ - name: Generate SPDX SBOM for image
165
+ # SHA-pinned per the project's actions-pinning policy. v0.24.0,
166
+ # commit e22c389904149dbc22b58101806040fa8d37a610.
167
+ uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
168
+ with:
169
+ image: ${{ env.IMAGE_REF }}
170
+ format: spdx-json
171
+ output-file: sbom.spdx.json
172
+ upload-artifact: false
173
+ upload-release-assets: false
174
+
175
+ - name: Attest SBOM with Cosign
176
+ env:
177
+ COSIGN_EXPERIMENTAL: "1"
178
+ run: |
179
+ set -eu
180
+ cosign attest --yes \
181
+ --predicate sbom.spdx.json \
182
+ --type spdxjson \
183
+ "$IMAGE_REF"
@@ -0,0 +1,137 @@
1
+ # Opengrep — second SAST engine, complementing CodeQL.
2
+ #
3
+ # Why this exists in your scaffolded Daloy (Deno) 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) with Opengrep
12
+ # (pattern-based AST matching, community rule packs) gives
13
+ # defense-in-depth at the source layer — the same posture Daloy
14
+ # 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`, `contents: read`).
19
+ # * Third-party actions are SHA-pinned with version comments.
20
+ # * Opengrep itself is NOT installed via a third-party action;
21
+ # the binary is downloaded from a pinned GitHub release and its
22
+ # sigstore cosign signature is verified before execution.
23
+ # * `step-security/harden-runner` runs in audit mode.
24
+ # * `actions/checkout` runs with `persist-credentials: false`.
25
+ # * `set -euo pipefail` on every shell step.
26
+
27
+ name: Opengrep
28
+
29
+ on:
30
+ push:
31
+ branches: [main]
32
+ pull_request:
33
+ branches: [main]
34
+ schedule:
35
+ - cron: "51 5 * * 4"
36
+ workflow_dispatch:
37
+
38
+ permissions: {}
39
+
40
+ concurrency:
41
+ group: opengrep-${{ github.workflow }}-${{ github.ref }}
42
+ cancel-in-progress: true
43
+
44
+ jobs:
45
+ scan:
46
+ name: Opengrep SAST
47
+ runs-on: ubuntu-latest
48
+ timeout-minutes: 20
49
+ permissions:
50
+ security-events: write
51
+ contents: read
52
+
53
+ env:
54
+ OPENGREP_VERSION: "v1.22.0"
55
+ OPENGREP_ASSET: "opengrep_manylinux_x86"
56
+
57
+ steps:
58
+ - name: Harden runner
59
+ uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
60
+ with:
61
+ egress-policy: audit
62
+ disable-sudo: true
63
+
64
+ - name: Checkout
65
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
66
+ with:
67
+ persist-credentials: false
68
+ show-progress: false
69
+
70
+ - name: Download Opengrep release binary + signature
71
+ run: |
72
+ set -euo pipefail
73
+ mkdir -p .opengrep
74
+ base="https://github.com/opengrep/opengrep/releases/download/${OPENGREP_VERSION}/${OPENGREP_ASSET}"
75
+ echo "Fetching ${base}"
76
+ curl --proto '=https' --tlsv1.2 -fsSL --retry 3 --retry-delay 2 \
77
+ -o .opengrep/opengrep "${base}"
78
+ curl --proto '=https' --tlsv1.2 -fsSL --retry 3 --retry-delay 2 \
79
+ -o .opengrep/opengrep.cert "${base}.cert"
80
+ curl --proto '=https' --tlsv1.2 -fsSL --retry 3 --retry-delay 2 \
81
+ -o .opengrep/opengrep.sig "${base}.sig"
82
+ chmod +x .opengrep/opengrep
83
+
84
+ - name: Verify Opengrep signature (cosign / sigstore)
85
+ run: |
86
+ set -euo pipefail
87
+ if ! command -v cosign >/dev/null 2>&1; then
88
+ echo "::error::cosign is required but not available on the runner."
89
+ exit 1
90
+ fi
91
+ cosign version
92
+ cosign verify-blob \
93
+ --cert ".opengrep/opengrep.cert" \
94
+ --signature ".opengrep/opengrep.sig" \
95
+ --certificate-identity-regexp "https://github.com/opengrep/opengrep.+" \
96
+ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
97
+ ".opengrep/opengrep"
98
+ echo "Opengrep ${OPENGREP_VERSION} signature verified."
99
+
100
+ - name: Print Opengrep version
101
+ run: |
102
+ set -euo pipefail
103
+ .opengrep/opengrep --version
104
+
105
+ - name: Run Opengrep scan (SARIF)
106
+ # Curated security-focused rule packs from the Semgrep registry:
107
+ # * p/security-audit / p/owasp-top-ten / p/cwe-top-25 — general
108
+ # * p/javascript / p/typescript — language-specific
109
+ # * p/secrets — hardcoded credentials / API keys
110
+ # `p/nodejs` is omitted on Deno because its sinks (e.g.
111
+ # `require()`, `process.*`) are not idiomatic on the Deno runtime.
112
+ run: |
113
+ set -euo pipefail
114
+ .opengrep/opengrep scan \
115
+ --config p/security-audit \
116
+ --config p/owasp-top-ten \
117
+ --config p/cwe-top-25 \
118
+ --config p/javascript \
119
+ --config p/typescript \
120
+ --config p/secrets \
121
+ --sarif-output opengrep.sarif \
122
+ --metrics off \
123
+ --timeout 120 \
124
+ --max-target-bytes 1500000 \
125
+ --exclude node_modules \
126
+ --exclude dist \
127
+ --exclude build \
128
+ --exclude coverage \
129
+ --exclude .opengrep \
130
+ .
131
+
132
+ - name: Upload SARIF to GitHub code scanning
133
+ if: always()
134
+ uses: github/codeql-action/upload-sarif@52485aec7be33610227643b0fe83936b8b5f061a # v3
135
+ with:
136
+ sarif_file: opengrep.sarif
137
+ category: opengrep
@@ -0,0 +1,121 @@
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 Deno service:
5
+ # Deno does not ship a built-in `audit` command, so without this
6
+ # workflow a scaffolded Deno project has NO scheduled SCA coverage
7
+ # at all. The Aikido write-up "SAST vs SCA" calls this gap out
8
+ # directly: SAST (CodeQL, Opengrep) only inspects your own source;
9
+ # it does not know that one of your `npm:` or `jsr:` imports just
10
+ # had a malware advisory published against the exact version your
11
+ # `deno.lock` pins.
12
+ #
13
+ # This workflow runs Google's OSV-Scanner against `deno.lock` on
14
+ # every PR and on a daily schedule. OSV-Scanner queries OSV.dev and
15
+ # cross-references the OpenSSF `malicious-packages` corpus
16
+ # (https://github.com/ossf/malicious-packages), so it surfaces both
17
+ # classic CVEs and explicitly-flagged malware drops — the exact
18
+ # classes of issue the September 2025 npm phishing wave that
19
+ # compromised `debug`, `chalk`, and ~2B downloads/week of dependent
20
+ # packages (https://lwn.net/Articles/1037167/) made famous.
21
+ #
22
+ # Defense-in-depth context (see SECURITY.md):
23
+ # * Deno's permission model (`--allow-net`, etc.) limits blast radius
24
+ # of a compromised dep at runtime.
25
+ # * `deno.lock` is checked into the repo and `deno task ...` runs
26
+ # with `--cached-only` in CI so an attacker cannot swap a dep
27
+ # between scan and run.
28
+ # * This OSV pass is the LAST layer: "did a pinned version turn out
29
+ # to carry a CVE or a malware advisory after we pinned it?"
30
+ #
31
+ # Hardening:
32
+ # * `permissions: {}` at the top level; the single job opts in to
33
+ # `contents: read` only. No write permissions of any kind.
34
+ # * `step-security/harden-runner` in audit mode.
35
+ # * `actions/checkout` runs with `persist-credentials: false`.
36
+ # * The OSV-Scanner binary is downloaded directly from the official
37
+ # google/osv-scanner GitHub release and its SHA-256 is verified
38
+ # against a locally-pinned constant BEFORE the binary is executed.
39
+ # We deliberately avoid the third-party `google/osv-scanner-action`
40
+ # wrapper so the only third-party surface is the binary itself, not
41
+ # a layer of action-side YAML that could be retagged.
42
+ # * The scan is read-only and runs on the committed lockfile without
43
+ # fetching or executing any dependency code.
44
+
45
+ name: OSV scan
46
+
47
+ on:
48
+ push:
49
+ branches: [main]
50
+ paths:
51
+ - "deno.lock"
52
+ - "deno.json"
53
+ - ".github/workflows/osv-scan.yml"
54
+ pull_request:
55
+ branches: [main]
56
+ paths:
57
+ - "deno.lock"
58
+ - "deno.json"
59
+ - ".github/workflows/osv-scan.yml"
60
+ schedule:
61
+ # 06:47 UTC daily — offset from Zizmor (07:00) so the SCA jobs do
62
+ # not collide.
63
+ - cron: "47 6 * * *"
64
+ workflow_dispatch:
65
+
66
+ permissions: {}
67
+
68
+ concurrency:
69
+ group: osv-scan-${{ github.workflow }}-${{ github.ref }}
70
+ cancel-in-progress: true
71
+
72
+ jobs:
73
+ scan:
74
+ name: OSV.dev + malicious-packages scan
75
+ runs-on: ubuntu-latest
76
+ timeout-minutes: 15
77
+ permissions:
78
+ contents: read
79
+
80
+ env:
81
+ # Pinned OSV-Scanner release. Bump together with the SHA-256 below.
82
+ # Source: https://github.com/google/osv-scanner/releases/tag/v2.3.8
83
+ OSV_SCANNER_VERSION: "2.3.8"
84
+ # SHA-256 of `osv-scanner_linux_amd64` from the v2.3.8
85
+ # `osv-scanner_SHA256SUMS` asset.
86
+ OSV_SCANNER_SHA256: "bc98e15319ed0d515e3f9235287ba53cdc5535d576d24fd573978ecfe9ab92dc"
87
+
88
+ steps:
89
+ - name: Harden runner
90
+ uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
91
+ with:
92
+ egress-policy: audit
93
+ disable-sudo: true
94
+ disable-file-monitoring: false
95
+
96
+ - name: Checkout
97
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
98
+ with:
99
+ persist-credentials: false
100
+ show-progress: false
101
+
102
+ - name: Download and verify OSV-Scanner binary
103
+ # Pin the binary by SHA-256 BEFORE executing it. A registry or
104
+ # GitHub Releases compromise that swaps the asset would fail
105
+ # this check and abort the job.
106
+ run: |
107
+ set -euo pipefail
108
+ url="https://github.com/google/osv-scanner/releases/download/v${OSV_SCANNER_VERSION}/osv-scanner_linux_amd64"
109
+ curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \
110
+ --output osv-scanner "${url}"
111
+ echo "${OSV_SCANNER_SHA256} osv-scanner" | sha256sum --check --status
112
+ chmod +x osv-scanner
113
+ ./osv-scanner --version
114
+
115
+ - name: Scan deno.lock (OSV.dev + malicious-packages)
116
+ # Exits non-zero if any vulnerability or malware advisory matches
117
+ # a pinned `npm:` or `jsr:` version. This is the only scheduled
118
+ # SCA gate for a Deno scaffold, which has no built-in `audit`.
119
+ run: |
120
+ set -euo pipefail
121
+ ./osv-scanner scan source --lockfile=deno.lock --format=table