@kaademos/secure-sdlc 1.1.0 → 1.2.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/.claude/agents/grc-analyst.md +11 -5
- package/.claude/agents/product-manager.md +3 -3
- package/.claude-plugin/marketplace.json +51 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/secure-sdlc-gate.yml +47 -8
- package/CHANGELOG.md +24 -0
- package/CLAUDE.md +1 -0
- package/README.md +12 -5
- package/cli/src/commands/init.js +7 -2
- package/cli/src/utils/stack-detect.js +26 -0
- package/docs/templates/compliance-attestation.md +40 -1
- package/docs/templates/threat-model.md +1 -1
- package/package.json +2 -1
- package/skills/compliance-and-audit/SKILL.md +3 -3
- package/stacks/django.md +7 -7
- package/stacks/express.md +6 -6
- package/stacks/fastapi.md +6 -6
- package/stacks/golang.md +274 -0
- package/stacks/nextjs.md +6 -6
- package/stacks/rails.md +6 -6
|
@@ -29,7 +29,9 @@ Maintain awareness of applicable controls from:
|
|
|
29
29
|
- **PCI DSS v4.0** (if payment card data is in scope)
|
|
30
30
|
- **OWASP ASVS** (as the technical requirements anchor)
|
|
31
31
|
- **GDPR / UK GDPR** (if personal data is processed)
|
|
32
|
-
- **
|
|
32
|
+
- **HIPAA** (Security & Privacy Rules — if protected health information is in scope)
|
|
33
|
+
- **DORA** (if applicable to EU financial services)
|
|
34
|
+
- **FedRAMP** (NIST SP 800-53 baseline — if selling to US federal agencies)
|
|
33
35
|
|
|
34
36
|
---
|
|
35
37
|
|
|
@@ -54,10 +56,14 @@ When invoked at the start of a project or feature:
|
|
|
54
56
|
```markdown
|
|
55
57
|
## Control Mapping
|
|
56
58
|
|
|
57
|
-
| ASVS Ref | Requirement | SOC 2 | ISO 27001 | NIST CSF | PCI DSS |
|
|
58
|
-
|
|
59
|
-
|
|
|
60
|
-
|
|
|
59
|
+
| ASVS Ref | Requirement | SOC 2 | ISO 27001 | NIST CSF | PCI DSS | HIPAA | DORA | FedRAMP |
|
|
60
|
+
|----------|-------------|-------|-----------|----------|---------|-------|------|---------|
|
|
61
|
+
| V6.2.1 | Password complexity | CC6.1 | A.8.5 | PR.AC-1 | Req 8.3 | §164.312(d) | Art. 9 | IA-5 |
|
|
62
|
+
| V14.1.1 | Encryption at rest | CC6.7 | A.8.24 | PR.DS-1 | Req 3.5 | §164.312(a)(2)(iv) | Art. 9 | SC-28 |
|
|
63
|
+
|
|
64
|
+
> Only populate columns for frameworks selected in `secure-sdlc.yaml`. Add HIPAA when
|
|
65
|
+
> protected health information is processed, DORA for EU financial entities, and FedRAMP
|
|
66
|
+
> (NIST SP 800-53 control families: AC, AU, IA, SC, …) when targeting US federal agencies.
|
|
61
67
|
```
|
|
62
68
|
|
|
63
69
|
---
|
|
@@ -51,9 +51,9 @@ For each feature, produce a `docs/security-requirements.md` using this structure
|
|
|
51
51
|
|
|
52
52
|
| ID | Requirement | ASVS Ref | Priority | Acceptance Criteria |
|
|
53
53
|
|----|-------------|----------|----------|---------------------|
|
|
54
|
-
| SR-001 | All API endpoints require authentication |
|
|
55
|
-
| SR-002 | Passwords must meet complexity requirements |
|
|
56
|
-
| SR-003 | Sensitive data encrypted at rest |
|
|
54
|
+
| SR-001 | All API endpoints require authentication | V8.3.1 | MUST | Unauthenticated requests return HTTP 401 |
|
|
55
|
+
| SR-002 | Passwords must meet complexity requirements | V6.2.1 | MUST | Passwords < 8 chars or common passwords rejected |
|
|
56
|
+
| SR-003 | Sensitive data encrypted at rest | V14.1.1 | MUST | AES-256 or equivalent; key management documented |
|
|
57
57
|
|
|
58
58
|
### Privacy Requirements
|
|
59
59
|
- [ ] Data minimisation: only collect fields required for this feature
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "secure-sdlc-agents",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "Kaademos",
|
|
5
|
+
"email": "kaademos@github.com"
|
|
6
|
+
},
|
|
7
|
+
"metadata": {
|
|
8
|
+
"description": "A team of 8 AI security specialists embedded in your coding workflow — covering every phase of the Secure SDLC from requirements to release gating.",
|
|
9
|
+
"version": "1.2.0"
|
|
10
|
+
},
|
|
11
|
+
"plugins": [
|
|
12
|
+
{
|
|
13
|
+
"name": "secure-sdlc-agents",
|
|
14
|
+
"source": {
|
|
15
|
+
"source": "github",
|
|
16
|
+
"repo": "Kaademos/secure-sdlc-agents"
|
|
17
|
+
},
|
|
18
|
+
"description": "8 AI security specialist agents for the full Secure SDLC: threat modelling, AppSec, GRC, IaC review, AI/LLM security, and release gating. Works with Claude Code, Cursor, Windsurf, and any MCP-compatible tool.",
|
|
19
|
+
"version": "1.2.0",
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Kaademos"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/Kaademos/secure-sdlc-agents",
|
|
24
|
+
"repository": "https://github.com/Kaademos/secure-sdlc-agents",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"keywords": [
|
|
27
|
+
"security",
|
|
28
|
+
"appsec",
|
|
29
|
+
"sdlc",
|
|
30
|
+
"owasp",
|
|
31
|
+
"asvs",
|
|
32
|
+
"compliance",
|
|
33
|
+
"threat-modeling",
|
|
34
|
+
"secure-coding",
|
|
35
|
+
"devsecops",
|
|
36
|
+
"grc"
|
|
37
|
+
],
|
|
38
|
+
"category": "security",
|
|
39
|
+
"tags": [
|
|
40
|
+
"security",
|
|
41
|
+
"appsec",
|
|
42
|
+
"devsecops",
|
|
43
|
+
"owasp",
|
|
44
|
+
"compliance",
|
|
45
|
+
"threat-modeling",
|
|
46
|
+
"agent-skills"
|
|
47
|
+
],
|
|
48
|
+
"strict": true
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "secure-sdlc-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "A team of AI security specialists embedded in your coding workflow. 8 agents covering every phase of the Secure SDLC: requirements, threat modelling, code review, IaC security, compliance, and release gating. Works with Claude Code, Cursor, Windsurf, and any MCP-compatible tool.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Kaademos",
|
|
@@ -241,7 +241,7 @@ jobs:
|
|
|
241
241
|
strategy:
|
|
242
242
|
fail-fast: false
|
|
243
243
|
matrix:
|
|
244
|
-
language: ['javascript-typescript', 'python']
|
|
244
|
+
language: ['javascript-typescript', 'python', 'ruby', 'go', 'java-kotlin']
|
|
245
245
|
steps:
|
|
246
246
|
- name: Checkout
|
|
247
247
|
uses: actions/checkout@v4
|
|
@@ -250,25 +250,64 @@ jobs:
|
|
|
250
250
|
id: check-lang
|
|
251
251
|
run: |
|
|
252
252
|
LANG="${{ matrix.language }}"
|
|
253
|
+
# EXISTS gates every downstream step so absent languages skip cleanly.
|
|
254
|
+
# BUILD_MODE tells CodeQL how to build: interpreted languages need no
|
|
255
|
+
# build ("none"); Go must be built ("autobuild"); java-kotlin can use
|
|
256
|
+
# "none" for pure-Java repos but MUST build when Kotlin is present
|
|
257
|
+
# (Kotlin analysis has no build-mode: none support).
|
|
258
|
+
EXISTS=false
|
|
259
|
+
BUILD_MODE=none
|
|
253
260
|
if [ "$LANG" = "javascript-typescript" ]; then
|
|
254
|
-
find . -name "*.js" -o -name "*.ts" | grep -v node_modules | grep -q . &&
|
|
261
|
+
find . -name "*.js" -o -name "*.ts" | grep -v node_modules | grep -q . && EXISTS=true
|
|
255
262
|
elif [ "$LANG" = "python" ]; then
|
|
256
|
-
find . -name "*.py" | grep -q . &&
|
|
263
|
+
find . -name "*.py" | grep -q . && EXISTS=true
|
|
264
|
+
elif [ "$LANG" = "ruby" ]; then
|
|
265
|
+
find . -name "*.rb" | grep -q . && EXISTS=true
|
|
266
|
+
elif [ "$LANG" = "go" ]; then
|
|
267
|
+
find . -name "*.go" | grep -q . && EXISTS=true
|
|
268
|
+
BUILD_MODE=autobuild
|
|
269
|
+
elif [ "$LANG" = "java-kotlin" ]; then
|
|
270
|
+
find . -name "*.java" -o -name "*.kt" | grep -q . && EXISTS=true
|
|
271
|
+
# Kotlin requires a build; pure-Java repos skip it for reliability.
|
|
272
|
+
if find . -name "*.kt" | grep -q .; then
|
|
273
|
+
BUILD_MODE=autobuild
|
|
274
|
+
fi
|
|
257
275
|
else
|
|
258
|
-
|
|
276
|
+
EXISTS=true
|
|
259
277
|
fi
|
|
278
|
+
echo "EXISTS=$EXISTS" >> $GITHUB_OUTPUT
|
|
279
|
+
echo "BUILD_MODE=$BUILD_MODE" >> $GITHUB_OUTPUT
|
|
280
|
+
echo "Language=$LANG Exists=$EXISTS BuildMode=$BUILD_MODE"
|
|
281
|
+
|
|
282
|
+
# Set up the toolchain BEFORE CodeQL so autobuild can resolve the build.
|
|
283
|
+
# Without a matching toolchain, autobuild is the #1 cause of CodeQL CI
|
|
284
|
+
# failures on enterprise Go/Maven/Gradle projects.
|
|
285
|
+
- name: Set up Go
|
|
286
|
+
if: steps.check-lang.outputs.EXISTS == 'true' && matrix.language == 'go'
|
|
287
|
+
uses: actions/setup-go@v5
|
|
288
|
+
with:
|
|
289
|
+
go-version: stable
|
|
290
|
+
cache: false # no go.sum path assumptions; CodeQL only needs a build
|
|
291
|
+
|
|
292
|
+
- name: Set up JDK
|
|
293
|
+
if: steps.check-lang.outputs.EXISTS == 'true' && matrix.language == 'java-kotlin' && steps.check-lang.outputs.BUILD_MODE == 'autobuild'
|
|
294
|
+
uses: actions/setup-java@v4
|
|
295
|
+
with:
|
|
296
|
+
distribution: temurin
|
|
297
|
+
# Latest LTS. If your project targets an older JDK (11/17) and the
|
|
298
|
+
# build fails, change this to match — that's the one knob to turn.
|
|
299
|
+
java-version: '21'
|
|
260
300
|
|
|
261
301
|
- name: Initialize CodeQL
|
|
262
302
|
if: steps.check-lang.outputs.EXISTS == 'true'
|
|
263
303
|
uses: github/codeql-action/init@v3
|
|
264
304
|
with:
|
|
265
305
|
languages: ${{ matrix.language }}
|
|
306
|
+
build-mode: ${{ steps.check-lang.outputs.BUILD_MODE }}
|
|
266
307
|
queries: security-and-quality
|
|
267
308
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
uses: github/codeql-action/autobuild@v3
|
|
271
|
-
|
|
309
|
+
# No standalone Autobuild step: build-mode "autobuild" runs the autobuilder
|
|
310
|
+
# during analysis, and build-mode "none" needs no build at all.
|
|
272
311
|
- name: Perform CodeQL Analysis
|
|
273
312
|
if: steps.check-lang.outputs.EXISTS == 'true'
|
|
274
313
|
uses: github/codeql-action/analyze@v3
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,30 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [1.2.0] — 2026-06-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Go stack profile** (`stacks/golang.md`) — dense, code-driven security guidance for Go (net/http, Gin, Echo, Fiber): `html/template` XSS, `database/sql`/GORM parameterisation, CORS, security headers, `gosec`/`govulncheck`
|
|
13
|
+
- **Go security notes** in `getStackSecurityNotes()` plus a `getStackProfile()` resolver so detected `gin`/`echo`/`fiber` projects map to the `golang` profile and notes
|
|
14
|
+
- **Worked example `04-oauth-flow`** — OAuth 2.0 / OIDC social login (authorization-code + PKCE); `redirect_uri` exact matching, `state` vs `nonce`, ID-token validation, anchored to ASVS 5.0 V10 and RFC 9700
|
|
15
|
+
- **Worked example `05-payment-processing`** — redirect-based hosted checkout (PCI DSS SAQ A); webhook signature verification, idempotency, server-side amount, reflecting the Jan 2025 SAQ A changes
|
|
16
|
+
- **HIPAA, DORA, and FedRAMP** control tables in `compliance-attestation.md` and the GRC agent's control-mapping example
|
|
17
|
+
- **Automated test suite** (`test/`, Node built-in runner, zero new dependencies) — guards version sync across manifests, agent frontmatter, and the stack-detection ↔ `stacks/*.md` mapping
|
|
18
|
+
- **CI workflow** (`.github/workflows/ci.yml`) — runs the suite on Node 18, 20, and 22 plus an `npm pack` content check
|
|
19
|
+
- **Release workflow** (`.github/workflows/release.yml`) — publishes to npm with provenance and creates a GitHub Release on `v*` tags
|
|
20
|
+
- **`CODE_OF_CONDUCT.md`** (Contributor Covenant 2.1), **`.editorconfig`**, npm/CI/Node README badges, and a committed `package-lock.json`
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- **CodeQL SAST** (`secure-sdlc-gate.yml`) — matrix expanded to `ruby`, `go`, and `java-kotlin`; per-language `build-mode` with toolchain setup so compiled-language scans are reliable on enterprise repos (pure-Java uses `build-mode: none`)
|
|
24
|
+
- **ASVS references migrated from 4.0 to 5.0** repo-wide using the official OWASP `mapping_v4.0.3_to_v5.0.0` mapping (stack profiles, examples, agents, templates, skill, PR template)
|
|
25
|
+
- **`secure-sdlc init`** only prints a `stacks/<name>.md` pointer when that profile actually ships
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- Pre-existing CSRF control mislabel in the Django, Express, Rails, and Go stack profiles (`V14.4.5`/HSTS → real CSRF control `V3.5.1`)
|
|
29
|
+
- Broken `stacks/<gin|echo|fiber>.md` reference — Go framework projects now resolve to `stacks/golang.md`
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
9
33
|
## [1.0.2]
|
|
10
34
|
|
|
11
35
|
---
|
package/CLAUDE.md
CHANGED
|
@@ -175,6 +175,7 @@ If the project uses one of these stacks, reference the relevant profile in `stac
|
|
|
175
175
|
| Django | `stacks/django.md` |
|
|
176
176
|
| Express.js | `stacks/express.md` |
|
|
177
177
|
| Ruby on Rails | `stacks/rails.md` |
|
|
178
|
+
| Go (net/http, Gin, Echo, Fiber) | `stacks/golang.md` |
|
|
178
179
|
|
|
179
180
|
Stack profiles contain framework-specific vulnerability patterns, secure coding examples,
|
|
180
181
|
and recommended libraries. Reference them when the dev-lead or appsec-engineer agents
|
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
](https://github.com/Kaademos/secure-sdlc-agents/actions/workflows/ci.yml)
|
|
2
|
+
[](https://www.npmjs.com/package/@kaademos/secure-sdlc)
|
|
3
|
+
[](https://nodejs.org)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+

|
|
5
6
|

|
|
6
7
|
|
|
7
8
|
# Secure SDLC Agents
|
|
@@ -9,6 +10,7 @@
|
|
|
9
10
|
**8 AI security specialists. Invoked at the exact phase where each vulnerability would have been caught.**
|
|
10
11
|
|
|
11
12
|
Requirements → threat modelling → code review → IaC → compliance → release gate.
|
|
13
|
+
|
|
12
14
|
Works in Claude Code, Cursor, Windsurf, Warp, and any MCP-compatible tool.
|
|
13
15
|
|
|
14
16
|
---
|
|
@@ -92,10 +94,12 @@ What are you working on?
|
|
|
92
94
|
|
|
93
95
|
## Quick start
|
|
94
96
|
|
|
95
|
-
### Option 0 — Claude Code Plugin Marketplace
|
|
97
|
+
### Option 0 — Claude Code Plugin Marketplace
|
|
96
98
|
|
|
97
99
|
```bash
|
|
98
100
|
/plugin marketplace add Kaademos/secure-sdlc-agents
|
|
101
|
+
|
|
102
|
+
/plugin install secure-sdlc-agents@secure-sdlc-agents
|
|
99
103
|
```
|
|
100
104
|
|
|
101
105
|
All 8 agents are immediately available in your session. No cloning, no npm, no file copying.
|
|
@@ -331,6 +335,7 @@ Deep, framework-specific security guidance in `stacks/`:
|
|
|
331
335
|
| Django | [`stacks/django.md`](stacks/django.md) — CSRF, strong params, ORM injection, production settings |
|
|
332
336
|
| Express.js | [`stacks/express.md`](stacks/express.md) — helmet, rate limiting, CSRF, Zod validation |
|
|
333
337
|
| Ruby on Rails | [`stacks/rails.md`](stacks/rails.md) — Brakeman, Pundit, strong parameters, credentials |
|
|
338
|
+
| Go (net/http, Gin, Echo, Fiber) | [`stacks/golang.md`](stacks/golang.md) — html/template XSS, database/sql & GORM injection, CORS, gosec/govulncheck |
|
|
334
339
|
|
|
335
340
|
---
|
|
336
341
|
|
|
@@ -372,6 +377,8 @@ In `warp-workflows/` — import into Warp for one-click SDLC automation:
|
|
|
372
377
|
| [`01-login-feature/`](examples/01-login-feature/) | Auth flow (bcrypt, MFA, sessions) | JWT alg:none, hardcoded secrets, cost factor |
|
|
373
378
|
| [`02-api-endpoint/`](examples/02-api-endpoint/) | Public REST API | IDOR via UUID path param, IAM over-privilege |
|
|
374
379
|
| [`03-file-upload/`](examples/03-file-upload/) | File upload to S3 | SVG XSS, magic byte validation, public bucket |
|
|
380
|
+
| [`04-oauth-flow/`](examples/04-oauth-flow/) | OAuth 2.0 / OIDC social login | redirect_uri exact match, PKCE, state vs nonce, ID-token validation |
|
|
381
|
+
| [`05-payment-processing/`](examples/05-payment-processing/) | Card checkout (hosted page) | PCI DSS SAQ A scoping, webhook signature, idempotency, amount tampering |
|
|
375
382
|
|
|
376
383
|
---
|
|
377
384
|
|
package/cli/src/commands/init.js
CHANGED
|
@@ -163,12 +163,17 @@ export default async function init(options) {
|
|
|
163
163
|
console.log(chalk.dim(` secure-sdlc kickoff\n`));
|
|
164
164
|
|
|
165
165
|
if (stack.name !== "unknown") {
|
|
166
|
-
const { getStackSecurityNotes } = await import("../utils/stack-detect.js");
|
|
166
|
+
const { getStackSecurityNotes, getStackProfile } = await import("../utils/stack-detect.js");
|
|
167
167
|
const notes = getStackSecurityNotes(stack.name);
|
|
168
168
|
if (notes.length) {
|
|
169
169
|
console.log(chalk.bold(`\n${stack.display} security notes for your team:\n`));
|
|
170
170
|
notes.slice(0, 3).forEach((n) => console.log(chalk.dim(` • ${n}`)));
|
|
171
|
-
|
|
171
|
+
// Only point to a profile that actually ships — avoids a broken reference
|
|
172
|
+
// for stacks that have notes but no dedicated stacks/<name>.md (e.g. terraform).
|
|
173
|
+
const profile = getStackProfile(stack.name);
|
|
174
|
+
if (existsSync(join(REPO_ROOT, "stacks", `${profile}.md`))) {
|
|
175
|
+
console.log(chalk.dim(` (see stacks/${profile}.md for full guidance)\n`));
|
|
176
|
+
}
|
|
172
177
|
}
|
|
173
178
|
}
|
|
174
179
|
}
|
|
@@ -79,10 +79,29 @@ export function detectStack(projectRoot) {
|
|
|
79
79
|
return { name: "unknown", display: "Unknown", language: "Unknown" };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Maps a detected stack name to the stack that owns its security guidance.
|
|
84
|
+
* Framework variants (e.g. Gin/Echo/Fiber) share their language's profile and notes.
|
|
85
|
+
*/
|
|
86
|
+
const STACK_ALIASES = {
|
|
87
|
+
gin: "golang",
|
|
88
|
+
echo: "golang",
|
|
89
|
+
fiber: "golang",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolves the stack profile (notes key + stacks/<name>.md) for a detected stack.
|
|
94
|
+
* Falls back to the stack name itself when no alias applies.
|
|
95
|
+
*/
|
|
96
|
+
export function getStackProfile(stackName) {
|
|
97
|
+
return STACK_ALIASES[stackName] || stackName;
|
|
98
|
+
}
|
|
99
|
+
|
|
82
100
|
/**
|
|
83
101
|
* Returns the top security considerations for a given stack.
|
|
84
102
|
*/
|
|
85
103
|
export function getStackSecurityNotes(stackName) {
|
|
104
|
+
stackName = getStackProfile(stackName);
|
|
86
105
|
const notes = {
|
|
87
106
|
nextjs: [
|
|
88
107
|
"Review Server Actions for CSRF and authorisation — they're POST endpoints by default",
|
|
@@ -120,6 +139,13 @@ export function getStackSecurityNotes(stackName) {
|
|
|
120
139
|
"Audit before_action filters for auth — ensure every controller action is covered",
|
|
121
140
|
"Brakeman is the standard Rails SAST tool — run on every PR",
|
|
122
141
|
],
|
|
142
|
+
golang: [
|
|
143
|
+
"Render HTML with html/template (context-aware escaping) — never text/template, and avoid template.HTML on user input",
|
|
144
|
+
"Use database/sql placeholders ($1/?) or GORM's ? conditions — never fmt.Sprintf user input into queries",
|
|
145
|
+
"CORS: set an explicit AllowedOrigins list — never combine wildcard/AllowAllOrigins with AllowCredentials:true",
|
|
146
|
+
"net/http ships no security headers — add CSP, HSTS, X-Content-Type-Options via middleware (e.g. unrolled/secure)",
|
|
147
|
+
"Run gosec (SAST) and govulncheck (vulnerable deps) in CI; hash passwords with bcrypt (cost ≥ 12) or argon2id",
|
|
148
|
+
],
|
|
123
149
|
terraform: [
|
|
124
150
|
"Pin provider versions with ~> constraints, not latest",
|
|
125
151
|
"Use terraform-aws-modules/terraform-google-modules — don't write IAM from scratch",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Release version:** v[X.Y.Z]
|
|
4
4
|
**Date:** [YYYY-MM-DD]
|
|
5
5
|
**Author:** GRC Analyst Agent + [Human GRC lead]
|
|
6
|
-
**Frameworks in scope:** [SOC 2 / ISO 27001 / NIST CSF / PCI DSS / GDPR — delete inapplicable]
|
|
6
|
+
**Frameworks in scope:** [SOC 2 / ISO 27001 / NIST CSF / PCI DSS / GDPR / HIPAA / DORA / FedRAMP — delete inapplicable]
|
|
7
7
|
**Status:** Draft / Review / Approved
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -110,6 +110,45 @@ ISO/IEC 27001:2022 Annex A, NIST CSF 2.0]
|
|
|
110
110
|
|
|
111
111
|
---
|
|
112
112
|
|
|
113
|
+
### HIPAA *(complete only if protected health information (PHI) is in scope)*
|
|
114
|
+
|
|
115
|
+
| Standard / Rule | Safeguard | Status | Evidence Reference | Notes |
|
|
116
|
+
|-----------------|-----------|--------|--------------------|-------|
|
|
117
|
+
| §164.308(a)(1) | Security Rule — Security management process (risk analysis) | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
|
|
118
|
+
| §164.312(a)(1) | Security Rule — Access control (technical safeguards) | | | |
|
|
119
|
+
| §164.312(b) | Security Rule — Audit controls | | | |
|
|
120
|
+
| §164.312(e)(1) | Security Rule — Transmission security (encryption in transit) | | | |
|
|
121
|
+
| §164.502(b) | Privacy Rule — Minimum necessary use and disclosure | | | |
|
|
122
|
+
|
|
123
|
+
*Extend with additional Security/Privacy Rule standards (e.g. §164.308 administrative, §164.310 physical) relevant to the systems in scope.*
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### DORA *(complete only if an EU financial entity or critical ICT third-party provider)*
|
|
128
|
+
|
|
129
|
+
| Article / Pillar | Requirement | Status | Evidence Reference | Notes |
|
|
130
|
+
|------------------|-------------|--------|--------------------|-------|
|
|
131
|
+
| Art. 5–15 | ICT risk management framework | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
|
|
132
|
+
| Art. 17–23 | ICT-related incident management, classification & reporting | | | |
|
|
133
|
+
| Art. 24–27 | Digital operational resilience testing (incl. TLPT) | | | |
|
|
134
|
+
| Art. 28–30 | ICT third-party risk management | | | |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### FedRAMP *(complete only if selling to US federal agencies — NIST SP 800-53 baseline)*
|
|
139
|
+
|
|
140
|
+
| Control Family | Control | Status | Evidence Reference | Notes |
|
|
141
|
+
|----------------|---------|--------|--------------------|-------|
|
|
142
|
+
| AC — Access Control | AC-2 Account management | ✅ Met / ⚠️ Gap / 🚫 Fail | | |
|
|
143
|
+
| AU — Audit & Accountability | AU-2 Event logging | | | |
|
|
144
|
+
| IA — Identification & Authentication | IA-2 Identification and authentication (organisational users) | | | |
|
|
145
|
+
| SC — System & Communications Protection | SC-7 Boundary protection | | | |
|
|
146
|
+
| SC — System & Communications Protection | SC-28 Protection of information at rest | | | |
|
|
147
|
+
|
|
148
|
+
*Select the impact baseline (Low / Moderate / High) and extend with the corresponding 800-53 control families (CM, CP, IR, RA, SI, …).*
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
113
152
|
## Gaps
|
|
114
153
|
|
|
115
154
|
Controls that are not fully met at the time of this attestation:
|
|
@@ -116,7 +116,7 @@ Priority list of mitigations to carry into the build phase:
|
|
|
116
116
|
|
|
117
117
|
| Priority | Threat ID(s) | Mitigation | Owner | ASVS Ref |
|
|
118
118
|
|----------|-------------|------------|-------|----------|
|
|
119
|
-
| 1 | T-001, T-004 | Implement account lockout and generic error responses | Dev Lead |
|
|
119
|
+
| 1 | T-001, T-004 | Implement account lockout and generic error responses | Dev Lead | V6.3.1, V14.1.1 |
|
|
120
120
|
| 2 | | | | |
|
|
121
121
|
|
|
122
122
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaademos/secure-sdlc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Secure SDLC agent team — CLI to scaffold docs, hooks, CI, and MCP-ready security workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"prepack": "node cli/bin/secure-sdlc.js --version",
|
|
31
31
|
"sdlc": "node cli/bin/secure-sdlc.js",
|
|
32
|
+
"test": "node --test test/*.test.js",
|
|
32
33
|
"test:pack": "npm pack --dry-run --ignore-scripts 2>&1"
|
|
33
34
|
},
|
|
34
35
|
"keywords": [
|
|
@@ -66,9 +66,9 @@ Produce a control mapping table that connects ASVS requirements to applicable fr
|
|
|
66
66
|
```markdown
|
|
67
67
|
| ASVS Ref | Requirement | SOC 2 | ISO 27001 | NIST CSF | PCI DSS |
|
|
68
68
|
|----------|-------------|-------|-----------|----------|---------|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
69
|
+
| V6.2.1 | Password complexity ≥ 12 chars | CC6.1 | A.8.5 | PR.AC-1 | Req 8.3 |
|
|
70
|
+
| V14.1.1 | Encryption at rest (AES-256) | CC6.7 | A.8.24 | PR.DS-1 | Req 3.5 |
|
|
71
|
+
| V12.2.1 | TLS 1.2+ for all external comms | CC6.7 | A.8.20 | PR.DS-2 | Req 4.2 |
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
### Step 4 — Collect audit evidence at time of implementation
|
package/stacks/django.md
CHANGED
|
@@ -194,13 +194,13 @@ DATABASE_URL = env('DATABASE_URL')
|
|
|
194
194
|
|
|
195
195
|
| ASVS Ref | Control | Django Implementation |
|
|
196
196
|
|----------|---------|----------------------|
|
|
197
|
-
|
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
|
|
|
201
|
-
|
|
|
202
|
-
|
|
|
203
|
-
|
|
|
197
|
+
| V6.2.1 | Password complexity | `AUTH_PASSWORD_VALIDATORS` |
|
|
198
|
+
| V7.4.1 | Session invalidation on logout | `django.contrib.auth.logout()` clears session |
|
|
199
|
+
| V8.3.1 | Auth on endpoints | `@login_required` / `permission_classes` |
|
|
200
|
+
| V8.2.2 | Object-level auth | Explicit ownership checks before returning objects |
|
|
201
|
+
| V1.2.4 | No SQL injection | Use ORM; avoid `.raw()` with user input |
|
|
202
|
+
| V4.1.1 | Security headers | Django SecurityMiddleware |
|
|
203
|
+
| V3.5.1 | CSRF | CsrfViewMiddleware (enabled by default) |
|
|
204
204
|
|
|
205
205
|
---
|
|
206
206
|
|
package/stacks/express.md
CHANGED
|
@@ -204,12 +204,12 @@ const user = await prisma.user.findUnique({ where: { id: userId } });
|
|
|
204
204
|
|
|
205
205
|
| ASVS Ref | Control | Express Implementation |
|
|
206
206
|
|----------|---------|----------------------|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
207
|
+
| V8.3.1 | Auth middleware | passport.js + per-route authentication middleware |
|
|
208
|
+
| V8.2.2 | Object-level auth | Ownership check in every route handler |
|
|
209
|
+
| V2.2.1 | Input validation | Zod validation middleware |
|
|
210
|
+
| V3.5.2 | Rate limiting | express-rate-limit per endpoint |
|
|
211
|
+
| V4.1.1 | Security headers | helmet() |
|
|
212
|
+
| V3.5.1 | CSRF | csrf-csrf or csurf |
|
|
213
213
|
|
|
214
214
|
---
|
|
215
215
|
|
package/stacks/fastapi.md
CHANGED
|
@@ -224,12 +224,12 @@ settings = Settings()
|
|
|
224
224
|
|
|
225
225
|
| ASVS Ref | Control | FastAPI Implementation |
|
|
226
226
|
|----------|---------|----------------------|
|
|
227
|
-
|
|
|
228
|
-
|
|
|
229
|
-
|
|
|
230
|
-
|
|
|
231
|
-
|
|
|
232
|
-
|
|
|
227
|
+
| V8.3.1 | Auth on all endpoints | `Depends(get_current_user)` on every protected endpoint |
|
|
228
|
+
| V8.2.2 | Object-level authorisation | `resource.owner_id == current_user.id` check |
|
|
229
|
+
| V2.2.1 | Input validation | Pydantic models with `Field` constraints |
|
|
230
|
+
| V14.1.1 | Don't confirm resource existence | Return 404 (not 403) for resources the user can't see |
|
|
231
|
+
| V3.5.2 | Rate limiting | slowapi or similar |
|
|
232
|
+
| V4.1.1 | Security headers | Add `SecurityHeadersMiddleware` |
|
|
233
233
|
|
|
234
234
|
---
|
|
235
235
|
|
package/stacks/golang.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Go Security Profile
|
|
2
|
+
|
|
3
|
+
**Language:** Go 1.22+
|
|
4
|
+
**Frameworks:** net/http (stdlib), Gin, Echo, Fiber
|
|
5
|
+
**ASVS Baseline:** L2
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Go's Standard Library Is Safe — Until You Reach Around It
|
|
10
|
+
|
|
11
|
+
Go's stdlib gives you memory safety, `html/template` context-aware escaping, and
|
|
12
|
+
`database/sql` placeholders for free. Almost every Go web vulnerability comes from
|
|
13
|
+
*opting out* of these: using `text/template` for HTML, building SQL with `fmt.Sprintf`,
|
|
14
|
+
running shell strings through `exec.Command("sh", "-c", ...)`, or enabling a wildcard CORS
|
|
15
|
+
policy alongside credentials. This profile focuses on those opt-out traps.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## XSS — Use `html/template`, Never `text/template`, for HTML
|
|
20
|
+
|
|
21
|
+
`html/template` escapes output based on context (HTML body, attribute, JS, URL, CSS).
|
|
22
|
+
`text/template` performs **zero** escaping and must never render attacker-influenced HTML.
|
|
23
|
+
|
|
24
|
+
```go
|
|
25
|
+
import (
|
|
26
|
+
"html/template" // ✓ context-aware auto-escaping
|
|
27
|
+
// "text/template" // ✗ NO escaping — XSS if used for HTML
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
// ✓ Safe — html/template escapes .Name in HTML context
|
|
31
|
+
var tmpl = template.Must(template.New("p").Parse(`<p>Hello {{ .Name }}</p>`))
|
|
32
|
+
tmpl.Execute(w, map[string]string{"Name": userInput}) // <script> becomes <script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```go
|
|
36
|
+
// ✗ Unsafe — template.HTML tells the engine "this is already safe", disabling escaping
|
|
37
|
+
tmpl.Execute(w, map[string]any{"Bio": template.HTML(user.Bio)}) // stored XSS
|
|
38
|
+
|
|
39
|
+
// ✓ Safe — sanitise untrusted HTML first, then mark trusted
|
|
40
|
+
import "github.com/microcosm-cc/bluemonday"
|
|
41
|
+
|
|
42
|
+
p := bluemonday.UGCPolicy()
|
|
43
|
+
safe := template.HTML(p.Sanitize(user.Bio)) // allow-list of tags/attrs only
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Other escaping bypasses to flag in review:** `template.JS`, `template.URL`,
|
|
47
|
+
`template.CSS`, `template.HTMLAttr` — each disables escaping for that context. They are
|
|
48
|
+
only safe with values your code fully controls, never with user input.
|
|
49
|
+
|
|
50
|
+
> Framework note: Gin's `c.HTML()`, Echo's `c.Render()`, and Fiber's `html/v2` engine all
|
|
51
|
+
> build on `html/template`, so context escaping applies. Setting `Content-Type: text/html`
|
|
52
|
+
> by hand and writing a `fmt.Sprintf`'d string with `c.String()`/`w.Write()` bypasses it.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## SQL Injection — Placeholders, Never `fmt.Sprintf`
|
|
57
|
+
|
|
58
|
+
### `database/sql`
|
|
59
|
+
|
|
60
|
+
```go
|
|
61
|
+
// ✗ SQL injection — string formatting builds the query
|
|
62
|
+
q := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
|
|
63
|
+
rows, err := db.Query(q)
|
|
64
|
+
|
|
65
|
+
// ✓ Parameterised — driver sends value separately from SQL
|
|
66
|
+
rows, err := db.Query("SELECT * FROM users WHERE email = $1", email) // pq / pgx
|
|
67
|
+
rows, err := db.Query("SELECT * FROM users WHERE email = ?", email) // MySQL / SQLite
|
|
68
|
+
err := db.QueryRow("SELECT id FROM users WHERE email = $1", email).Scan(&id)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Identifiers (table/column names, `ORDER BY`) **cannot** be parameterised — allow-list them:
|
|
72
|
+
|
|
73
|
+
```go
|
|
74
|
+
// ✗ Unsafe — attacker controls the ORDER BY clause
|
|
75
|
+
db.Query("SELECT * FROM users ORDER BY " + r.URL.Query().Get("sort"))
|
|
76
|
+
|
|
77
|
+
// ✓ Safe — validate against a fixed set before interpolating
|
|
78
|
+
var allowedSort = map[string]string{"name": "name", "created": "created_at"}
|
|
79
|
+
col, ok := allowedSort[r.URL.Query().Get("sort")]
|
|
80
|
+
if !ok {
|
|
81
|
+
col = "created_at"
|
|
82
|
+
}
|
|
83
|
+
db.Query("SELECT * FROM users ORDER BY " + col) // col is now a known-safe literal
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### GORM
|
|
87
|
+
|
|
88
|
+
```go
|
|
89
|
+
// ✓ Safe — ? placeholders are escaped by GORM
|
|
90
|
+
db.Where("email = ?", email).First(&user)
|
|
91
|
+
db.Raw("SELECT * FROM users WHERE email = ?", email).Scan(&user)
|
|
92
|
+
|
|
93
|
+
// ✗ Unsafe — Sprintf into the condition string defeats GORM's escaping
|
|
94
|
+
db.Where(fmt.Sprintf("email = '%s'", email)).First(&user)
|
|
95
|
+
|
|
96
|
+
// ✗ Unsafe — user-controlled column name in a struct/map update can corrupt other fields
|
|
97
|
+
db.Model(&user).Updates(userControlledMap) // allow-list keys, or use a typed struct
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## CORS — Never Wildcard Origin *with* Credentials
|
|
103
|
+
|
|
104
|
+
`Access-Control-Allow-Origin: *` combined with `Allow-Credentials: true` is rejected by
|
|
105
|
+
browsers, so the usual "fix" is reflecting the request `Origin` — which silently allows
|
|
106
|
+
**every** site. Always use an explicit allow-list.
|
|
107
|
+
|
|
108
|
+
```go
|
|
109
|
+
// stdlib via github.com/rs/cors
|
|
110
|
+
import "github.com/rs/cors"
|
|
111
|
+
|
|
112
|
+
// ✗ Unsafe — allows any origin to make credentialed requests
|
|
113
|
+
c := cors.New(cors.Options{
|
|
114
|
+
AllowedOrigins: []string{"*"},
|
|
115
|
+
AllowCredentials: true,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// ✓ Safe — explicit origins, credentials only for those
|
|
119
|
+
c := cors.New(cors.Options{
|
|
120
|
+
AllowedOrigins: []string{"https://app.example.com"},
|
|
121
|
+
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
|
122
|
+
AllowCredentials: true,
|
|
123
|
+
MaxAge: 300,
|
|
124
|
+
})
|
|
125
|
+
handler := c.Handler(mux)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```go
|
|
129
|
+
// Gin — github.com/gin-contrib/cors
|
|
130
|
+
import "github.com/gin-contrib/cors"
|
|
131
|
+
|
|
132
|
+
// ✗ Unsafe — AllowAllOrigins + credentials reflects every Origin
|
|
133
|
+
r.Use(cors.New(cors.Config{AllowAllOrigins: true, AllowCredentials: true}))
|
|
134
|
+
|
|
135
|
+
// ✓ Safe — explicit list
|
|
136
|
+
r.Use(cors.New(cors.Config{
|
|
137
|
+
AllowOrigins: []string{"https://app.example.com"},
|
|
138
|
+
AllowCredentials: true,
|
|
139
|
+
}))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```go
|
|
143
|
+
// Echo — middleware.CORSWithConfig (AllowOrigins) and
|
|
144
|
+
// Fiber — github.com/gofiber/fiber/v2/middleware/cors (AllowOrigins) follow the same rule:
|
|
145
|
+
// list real origins; never set AllowOrigins:"*" together with AllowCredentials:true.
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Security Headers — Stdlib Has None
|
|
151
|
+
|
|
152
|
+
`net/http` sends no security headers. Add them via middleware on every response.
|
|
153
|
+
|
|
154
|
+
```go
|
|
155
|
+
func secureHeaders(next http.Handler) http.Handler {
|
|
156
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
157
|
+
h := w.Header()
|
|
158
|
+
h.Set("Content-Security-Policy", "default-src 'self'")
|
|
159
|
+
h.Set("X-Content-Type-Options", "nosniff")
|
|
160
|
+
h.Set("X-Frame-Options", "DENY")
|
|
161
|
+
h.Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains; preload")
|
|
162
|
+
h.Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
|
163
|
+
next.ServeHTTP(w, r)
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
// Or use github.com/unrolled/secure for a configurable equivalent.
|
|
167
|
+
// Gin: gin-contrib/secure · Echo: middleware.Secure() · Fiber: middleware/helmet
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Authentication & Passwords
|
|
173
|
+
|
|
174
|
+
```go
|
|
175
|
+
// ✓ Password hashing — bcrypt (cost ≥ 12) or argon2id
|
|
176
|
+
import "golang.org/x/crypto/bcrypt"
|
|
177
|
+
|
|
178
|
+
hash, err := bcrypt.GenerateFromPassword([]byte(pw), 12)
|
|
179
|
+
err = bcrypt.CompareHashAndPassword(hash, []byte(pw)) // constant-time
|
|
180
|
+
|
|
181
|
+
// ✗ Never — fast/unsalted hashes for passwords
|
|
182
|
+
sum := sha256.Sum256([]byte(pw)) // brute-forceable
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```go
|
|
186
|
+
// ✓ Constant-time comparison for tokens/HMACs — avoid timing leaks
|
|
187
|
+
import "crypto/subtle"
|
|
188
|
+
|
|
189
|
+
if subtle.ConstantTimeCompare(got, want) == 1 { /* match */ }
|
|
190
|
+
|
|
191
|
+
// ✗ Unsafe — == short-circuits and leaks length/prefix via timing
|
|
192
|
+
if string(got) == string(want) { /* ... */ }
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
JWT: pin the expected algorithm in the keyfunc to defeat `alg: none` / algorithm-confusion:
|
|
196
|
+
|
|
197
|
+
```go
|
|
198
|
+
token, err := jwt.Parse(raw, func(t *jwt.Token) (any, error) {
|
|
199
|
+
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { // ✓ reject unexpected alg
|
|
200
|
+
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
|
|
201
|
+
}
|
|
202
|
+
return secret, nil
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Command Injection & SSRF
|
|
209
|
+
|
|
210
|
+
```go
|
|
211
|
+
import "os/exec"
|
|
212
|
+
|
|
213
|
+
// ✗ Command injection — user input in a shell string
|
|
214
|
+
exec.Command("sh", "-c", "convert "+userFile+" out.png").Run()
|
|
215
|
+
|
|
216
|
+
// ✓ Safe — pass args as a slice; no shell, no word-splitting
|
|
217
|
+
exec.Command("convert", userFile, "out.png").Run()
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```go
|
|
221
|
+
// ✗ SSRF — fetching a user-supplied URL lets attackers hit internal services / metadata
|
|
222
|
+
http.Get(r.FormValue("url"))
|
|
223
|
+
|
|
224
|
+
// ✓ Resolve + validate the host against an allow-list and block private/link-local IPs
|
|
225
|
+
// before dialing (deny 169.254.169.254, 10/8, 127/8, ::1, etc.).
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Secrets & Config
|
|
231
|
+
|
|
232
|
+
```go
|
|
233
|
+
// ✓ Read secrets from the environment / a secrets manager — never hardcode
|
|
234
|
+
dsn := os.Getenv("DATABASE_URL")
|
|
235
|
+
if dsn == "" {
|
|
236
|
+
log.Fatal("DATABASE_URL is required")
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ✗ Never commit credentials
|
|
240
|
+
const apiKey = "sk_live_8f2b..." // gitleaks/gosec will flag this
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Run `gitleaks` in CI and keep `.env` out of git. Use `errors.Is`/`%w` and return generic
|
|
244
|
+
client errors — never write `err.Error()` (which can leak DSNs, paths, SQL) into responses.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## ASVS Controls for Go Projects
|
|
249
|
+
|
|
250
|
+
| ASVS Ref | Control | Go Implementation |
|
|
251
|
+
|----------|---------|-------------------|
|
|
252
|
+
| V2.2.1 | Input validation | `go-playground/validator` on bound structs |
|
|
253
|
+
| V1.3.1 | Output encoding (XSS) | `html/template`; sanitise with `bluemonday` |
|
|
254
|
+
| V1.2.4 | No SQL injection | `database/sql` placeholders; GORM `?` params |
|
|
255
|
+
| V1.2.5 | No OS command injection | `exec.Command` with arg slices, no `sh -c` |
|
|
256
|
+
| V11.4.2 | Password storage | `bcrypt` (cost ≥ 12) or `argon2id` |
|
|
257
|
+
| V3.5.1 | CSRF | `gorilla/csrf` for cookie-based sessions |
|
|
258
|
+
| V4.1.1 | Security headers | `unrolled/secure` middleware |
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Recommended Security Tooling (2026)
|
|
263
|
+
|
|
264
|
+
| Category | Tool |
|
|
265
|
+
|----------|------|
|
|
266
|
+
| SAST | `gosec` (`securego/gosec`) |
|
|
267
|
+
| Vulnerable dependencies | `govulncheck` (official, stdlib-aware) |
|
|
268
|
+
| Secret scanning | `gitleaks` |
|
|
269
|
+
| Input validation | `go-playground/validator` |
|
|
270
|
+
| HTML sanitisation | `bluemonday` |
|
|
271
|
+
| Security headers | `unrolled/secure` |
|
|
272
|
+
| CSRF | `gorilla/csrf` |
|
|
273
|
+
| Password hashing | `golang.org/x/crypto/bcrypt`, `argon2` |
|
|
274
|
+
| CORS | `rs/cors`, `gin-contrib/cors` |
|
package/stacks/nextjs.md
CHANGED
|
@@ -164,12 +164,12 @@ export async function createPost(formData: FormData) {
|
|
|
164
164
|
|
|
165
165
|
| ASVS Ref | Control | Next.js Implementation |
|
|
166
166
|
|----------|---------|----------------------|
|
|
167
|
-
|
|
|
168
|
-
|
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
167
|
+
| V8.3.1 | Authentication on all endpoints | `getServerSession()` in every route handler and Server Action |
|
|
168
|
+
| V8.2.2 | Object-level authorisation | Check `resource.userId === session.user.id` before returning/modifying |
|
|
169
|
+
| V2.2.1 | Input validation | Zod schemas on all Server Action inputs |
|
|
170
|
+
| V16.2.5 | Log security events | Log auth events in NextAuth callbacks |
|
|
171
|
+
| V12.2.1 | TLS everywhere | Enforced by Vercel/host; add HSTS header |
|
|
172
|
+
| V4.1.1 | Security headers | `securityHeaders` in next.config.js |
|
|
173
173
|
|
|
174
174
|
---
|
|
175
175
|
|
package/stacks/rails.md
CHANGED
|
@@ -225,12 +225,12 @@ session fixation, and many other Rails-specific issues.
|
|
|
225
225
|
|
|
226
226
|
| ASVS Ref | Control | Rails Implementation |
|
|
227
227
|
|----------|---------|---------------------|
|
|
228
|
-
|
|
|
229
|
-
|
|
|
230
|
-
|
|
|
231
|
-
|
|
|
232
|
-
|
|
|
233
|
-
|
|
|
228
|
+
| V6.2.1 | Password complexity | Devise validates :password strength |
|
|
229
|
+
| V6.3.1 | Account lockout | Devise :lockable |
|
|
230
|
+
| V8.3.1 | Auth on all actions | `before_action :authenticate_user!` |
|
|
231
|
+
| V8.2.2 | Object-level auth | Pundit policies with `authorize @resource` |
|
|
232
|
+
| V1.2.4 | No SQL injection | ActiveRecord ORM; never string-interpolate in where() |
|
|
233
|
+
| V3.5.1 | CSRF | `protect_from_forgery` (default) |
|
|
234
234
|
|
|
235
235
|
---
|
|
236
236
|
|