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.
- package/README.md +62 -6
- package/bin/create-daloy.mjs +344 -25
- 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 +183 -0
- 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 -1
- package/templates/_ci/node/_github/CODEOWNERS +3 -0
- package/templates/_ci/node/_github/workflows/container-scan.yml +46 -10
- package/templates/_ci/node/_github/workflows/dast.yml +177 -0
- package/templates/_ci/node/_github/workflows/deploy.yml +94 -0
- 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
package/sbom.cdx.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.5",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:94862215-9d79-5a0f-9ee1-81c52885d00f",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-05-
|
|
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.
|
|
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.
|
|
18
|
+
"bom-ref": "pkg:npm/create-daloy@0.35.0",
|
|
19
19
|
"name": "create-daloy",
|
|
20
|
-
"version": "0.
|
|
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.
|
|
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.
|
|
45
|
+
"tagId": "swidtag-create-daloy-0.35.0",
|
|
42
46
|
"name": "create-daloy",
|
|
43
|
-
"version": "0.
|
|
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.
|
|
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.
|
|
6
|
-
"documentNamespace": "https://github.com/daloyjs/daloy/sbom/create-daloy-0.
|
|
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-
|
|
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.
|
|
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.
|
|
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,
|
|
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
|
|
4
|
-
#
|
|
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
|
|
9
|
-
#
|
|
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 (
|
|
118
|
-
|
|
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
|