@samahlstrom/forge-cli 0.1.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 +175 -0
- package/bin/forge.js +2 -0
- package/dist/addons/index.d.ts +25 -0
- package/dist/addons/index.js +139 -0
- package/dist/addons/index.js.map +1 -0
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.js +61 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +177 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/ingest.d.ts +24 -0
- package/dist/commands/ingest.js +316 -0
- package/dist/commands/ingest.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +557 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/remove.d.ts +1 -0
- package/dist/commands/remove.js +42 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.js +190 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/detect/features.d.ts +10 -0
- package/dist/detect/features.js +33 -0
- package/dist/detect/features.js.map +1 -0
- package/dist/detect/go.d.ts +3 -0
- package/dist/detect/go.js +38 -0
- package/dist/detect/go.js.map +1 -0
- package/dist/detect/index.d.ts +25 -0
- package/dist/detect/index.js +32 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/detect/node.d.ts +3 -0
- package/dist/detect/node.js +99 -0
- package/dist/detect/node.js.map +1 -0
- package/dist/detect/python.d.ts +3 -0
- package/dist/detect/python.js +86 -0
- package/dist/detect/python.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/render/engine.d.ts +8 -0
- package/dist/render/engine.js +71 -0
- package/dist/render/engine.js.map +1 -0
- package/dist/render/merge.d.ts +5 -0
- package/dist/render/merge.js +33 -0
- package/dist/render/merge.js.map +1 -0
- package/dist/utils/fs.d.ts +8 -0
- package/dist/utils/fs.js +42 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +3 -0
- package/dist/utils/git.js +31 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/hash.d.ts +8 -0
- package/dist/utils/hash.js +22 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/yaml.d.ts +3 -0
- package/dist/utils/yaml.js +12 -0
- package/dist/utils/yaml.js.map +1 -0
- package/package.json +53 -0
- package/templates/addons/beads-dolt-backend/files/dolt-setup.sh +267 -0
- package/templates/addons/beads-dolt-backend/manifest.yaml +13 -0
- package/templates/addons/browser-testing/files/browser-smoke.sh +196 -0
- package/templates/addons/browser-testing/files/visual-qa.md +103 -0
- package/templates/addons/browser-testing/manifest.yaml +20 -0
- package/templates/addons/compliance-hipaa/files/hipaa-checks.sh +184 -0
- package/templates/addons/compliance-hipaa/files/hipaa-context.md +91 -0
- package/templates/addons/compliance-hipaa/manifest.yaml +15 -0
- package/templates/addons/compliance-soc2/files/soc2-checks.sh +232 -0
- package/templates/addons/compliance-soc2/files/soc2-context.md +147 -0
- package/templates/addons/compliance-soc2/manifest.yaml +15 -0
- package/templates/core/CLAUDE.md.hbs +70 -0
- package/templates/core/agents/architect.md.hbs +68 -0
- package/templates/core/agents/backend.md.hbs +27 -0
- package/templates/core/agents/frontend.md.hbs +25 -0
- package/templates/core/agents/quality.md.hbs +40 -0
- package/templates/core/agents/security.md.hbs +53 -0
- package/templates/core/context/project.md.hbs +60 -0
- package/templates/core/forge.yaml.hbs +69 -0
- package/templates/core/hooks/post-edit.sh.hbs +8 -0
- package/templates/core/hooks/pre-edit.sh.hbs +41 -0
- package/templates/core/hooks/session-start.sh.hbs +34 -0
- package/templates/core/pipeline/classify.sh.hbs +159 -0
- package/templates/core/pipeline/decompose.md.hbs +100 -0
- package/templates/core/pipeline/deliver.sh.hbs +171 -0
- package/templates/core/pipeline/execute.md.hbs +138 -0
- package/templates/core/pipeline/intake.sh.hbs +152 -0
- package/templates/core/pipeline/orchestrator.sh.hbs +361 -0
- package/templates/core/pipeline/verify.sh.hbs +160 -0
- package/templates/core/settings.json.hbs +55 -0
- package/templates/core/skill-creator.md.hbs +151 -0
- package/templates/core/skill-deliver.md.hbs +46 -0
- package/templates/core/skill-ingest.md.hbs +245 -0
- package/templates/presets/go/stack.md.hbs +133 -0
- package/templates/presets/python-fastapi/stack.md.hbs +101 -0
- package/templates/presets/react-next-ts/stack.md.hbs +77 -0
- package/templates/presets/sveltekit-ts/stack.md.hbs +116 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# hipaa-checks.sh — HIPAA compliance scanning for forge projects
|
|
5
|
+
# Checks for PHI leaks, auth gaps, and encryption issues
|
|
6
|
+
|
|
7
|
+
RESULTS_FILE=".forge/state/hipaa-results.json"
|
|
8
|
+
PASS=0
|
|
9
|
+
FAIL=0
|
|
10
|
+
WARN=0
|
|
11
|
+
FINDINGS=()
|
|
12
|
+
|
|
13
|
+
mkdir -p "$(dirname "$RESULTS_FILE")"
|
|
14
|
+
|
|
15
|
+
# --- Helpers ---
|
|
16
|
+
|
|
17
|
+
log() { echo "[hipaa] $*"; }
|
|
18
|
+
finding() {
|
|
19
|
+
local severity="$1" file="$2" line="$3" rule="$4" detail="$5"
|
|
20
|
+
FINDINGS+=("{\"severity\":\"${severity}\",\"file\":\"${file}\",\"line\":${line},\"rule\":\"${rule}\",\"detail\":\"${detail}\"}")
|
|
21
|
+
if [ "$severity" = "fail" ]; then
|
|
22
|
+
FAIL=$((FAIL + 1))
|
|
23
|
+
else
|
|
24
|
+
WARN=$((WARN + 1))
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# --- Check 1: PHI in log statements ---
|
|
29
|
+
|
|
30
|
+
log "Checking for PHI in log statements..."
|
|
31
|
+
|
|
32
|
+
# Patterns that suggest logging PHI fields
|
|
33
|
+
PHI_LOG_PATTERNS=(
|
|
34
|
+
'console\.\(log\|error\|warn\|info\|debug\).*\(patient\|ssn\|dob\|dateOfBirth\|diagnosis\|mrn\|medicalRecord\|socialSecurity\)'
|
|
35
|
+
'logger\.\(info\|warn\|error\|debug\).*\(patient\|ssn\|dob\|dateOfBirth\|diagnosis\|mrn\|medicalRecord\|socialSecurity\)'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
for pattern in "${PHI_LOG_PATTERNS[@]}"; do
|
|
39
|
+
while IFS=: read -r file line _match; do
|
|
40
|
+
[ -z "$file" ] && continue
|
|
41
|
+
finding "fail" "$file" "$line" "phi-in-logs" "Potential PHI logged — review this log statement"
|
|
42
|
+
done < <(grep -rn "$pattern" --include='*.ts' --include='*.js' --include='*.svelte' --include='*.py' . 2>/dev/null | head -50 || true)
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
# --- Check 2: PHI in URLs / route params ---
|
|
46
|
+
|
|
47
|
+
log "Checking for PHI in URL patterns..."
|
|
48
|
+
|
|
49
|
+
URL_PHI_PATTERNS=(
|
|
50
|
+
'patient[Ii]d.*params\|params.*patient[Ii]d'
|
|
51
|
+
'ssn.*query\|query.*ssn'
|
|
52
|
+
'dob.*searchParams\|searchParams.*dob'
|
|
53
|
+
'\$page\.params.*\(patient\|ssn\|dob\|mrn\)'
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
for pattern in "${URL_PHI_PATTERNS[@]}"; do
|
|
57
|
+
while IFS=: read -r file line _match; do
|
|
58
|
+
[ -z "$file" ] && continue
|
|
59
|
+
finding "fail" "$file" "$line" "phi-in-url" "Potential PHI in URL parameter"
|
|
60
|
+
done < <(grep -rn "$pattern" --include='*.ts' --include='*.js' --include='*.svelte' . 2>/dev/null | head -50 || true)
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
# --- Check 3: PHI in browser storage ---
|
|
64
|
+
|
|
65
|
+
log "Checking for PHI in browser storage usage..."
|
|
66
|
+
|
|
67
|
+
STORAGE_PATTERNS=(
|
|
68
|
+
'localStorage\.set.*\(patient\|ssn\|dob\|diagnosis\|mrn\)'
|
|
69
|
+
'sessionStorage\.set.*\(patient\|ssn\|dob\|diagnosis\|mrn\)'
|
|
70
|
+
'indexedDB.*\(patient\|ssn\|dob\|diagnosis\|mrn\)'
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
for pattern in "${STORAGE_PATTERNS[@]}"; do
|
|
74
|
+
while IFS=: read -r file line _match; do
|
|
75
|
+
[ -z "$file" ] && continue
|
|
76
|
+
finding "fail" "$file" "$line" "phi-in-storage" "Potential PHI in browser storage"
|
|
77
|
+
done < <(grep -rn "$pattern" --include='*.ts' --include='*.js' --include='*.svelte' . 2>/dev/null | head -50 || true)
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
# --- Check 4: Unprotected endpoints ---
|
|
81
|
+
|
|
82
|
+
log "Checking for endpoints without auth guards..."
|
|
83
|
+
|
|
84
|
+
# Look for API endpoints / server routes missing auth checks
|
|
85
|
+
if [ -d "src/routes" ]; then
|
|
86
|
+
while IFS= read -r file; do
|
|
87
|
+
if ! grep -q 'locals\.\(user\|session\|auth\)\|getSession\|requireAuth\|authenticate\|verifyToken\|event\.locals' "$file" 2>/dev/null; then
|
|
88
|
+
finding "warn" "$file" 0 "no-auth-guard" "Server endpoint may lack authentication — verify manually"
|
|
89
|
+
fi
|
|
90
|
+
done < <(find . -path '*/routes/*' \( -name '+server.ts' -o -name '+server.js' \) -not -path '*/node_modules/*' 2>/dev/null | head -100)
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# --- Check 5: Wildcard Firestore rules ---
|
|
94
|
+
|
|
95
|
+
log "Checking Firestore security rules..."
|
|
96
|
+
|
|
97
|
+
RULES_FILES=$(find . -name 'firestore.rules' -o -name '*.rules' -not -path '*/node_modules/*' 2>/dev/null || true)
|
|
98
|
+
for rules_file in $RULES_FILES; do
|
|
99
|
+
while IFS=: read -r _ line _match; do
|
|
100
|
+
[ -z "$line" ] && continue
|
|
101
|
+
finding "fail" "$rules_file" "$line" "wildcard-rules" "Overly permissive Firestore rule — never allow unrestricted access to PHI"
|
|
102
|
+
done < <(grep -n 'allow.*:.*if true\|allow.*:.*if request\.auth != null' "$rules_file" 2>/dev/null || true)
|
|
103
|
+
done
|
|
104
|
+
|
|
105
|
+
# --- Check 6: HTTP (non-TLS) API calls ---
|
|
106
|
+
|
|
107
|
+
log "Checking for non-HTTPS API calls..."
|
|
108
|
+
|
|
109
|
+
while IFS=: read -r file line _match; do
|
|
110
|
+
[ -z "$file" ] && continue
|
|
111
|
+
# Skip localhost and test files
|
|
112
|
+
if echo "$_match" | grep -q 'localhost\|127\.0\.0\.1\|0\.0\.0\.0'; then
|
|
113
|
+
continue
|
|
114
|
+
fi
|
|
115
|
+
finding "fail" "$file" "$line" "no-tls" "HTTP (non-TLS) API call detected — use HTTPS"
|
|
116
|
+
done < <(grep -rn "fetch(['\"]http:" --include='*.ts' --include='*.js' --include='*.svelte' . 2>/dev/null | grep -v node_modules | grep -v '\.test\.' | head -50 || true)
|
|
117
|
+
|
|
118
|
+
# --- Check 7: Secrets in code ---
|
|
119
|
+
|
|
120
|
+
log "Checking for hardcoded secrets..."
|
|
121
|
+
|
|
122
|
+
SECRET_PATTERNS=(
|
|
123
|
+
'password\s*=\s*["\x27][^"\x27]\{8,\}'
|
|
124
|
+
'api[_-]\?key\s*=\s*["\x27][A-Za-z0-9]\{20,\}'
|
|
125
|
+
'secret\s*=\s*["\x27][^"\x27]\{8,\}'
|
|
126
|
+
'PRIVATE.KEY'
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
for pattern in "${SECRET_PATTERNS[@]}"; do
|
|
130
|
+
while IFS=: read -r file line _match; do
|
|
131
|
+
[ -z "$file" ] && continue
|
|
132
|
+
finding "warn" "$file" "$line" "hardcoded-secret" "Possible hardcoded secret — use environment variables or secret manager"
|
|
133
|
+
done < <(grep -rn "$pattern" --include='*.ts' --include='*.js' --include='*.svelte' --include='*.py' --include='*.env*' . 2>/dev/null | grep -v node_modules | grep -v '\.test\.' | grep -v '\.example' | head -50 || true)
|
|
134
|
+
done
|
|
135
|
+
|
|
136
|
+
# --- Check 8: Semgrep HIPAA rules (if available) ---
|
|
137
|
+
|
|
138
|
+
if command -v semgrep > /dev/null 2>&1; then
|
|
139
|
+
log "Running semgrep HIPAA rules..."
|
|
140
|
+
semgrep --config "p/hipaa" --json --quiet . 2>/dev/null > /tmp/forge-semgrep-hipaa.json || true
|
|
141
|
+
|
|
142
|
+
SEMGREP_COUNT=$(jq '.results | length' /tmp/forge-semgrep-hipaa.json 2>/dev/null || echo "0")
|
|
143
|
+
if [ "$SEMGREP_COUNT" -gt 0 ]; then
|
|
144
|
+
log "Semgrep found ${SEMGREP_COUNT} HIPAA findings"
|
|
145
|
+
FAIL=$((FAIL + SEMGREP_COUNT))
|
|
146
|
+
# Merge semgrep findings
|
|
147
|
+
while IFS= read -r sg_finding; do
|
|
148
|
+
FINDINGS+=("$sg_finding")
|
|
149
|
+
done < <(jq -c '.results[] | {severity:"fail",file:.path,line:.start.line,rule:.check_id,detail:.extra.message}' /tmp/forge-semgrep-hipaa.json 2>/dev/null || true)
|
|
150
|
+
fi
|
|
151
|
+
else
|
|
152
|
+
log "Semgrep not installed — skipping advanced HIPAA rules (install with: pip install semgrep)"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# --- Passed checks ---
|
|
156
|
+
|
|
157
|
+
# Count passes for checks that found no issues
|
|
158
|
+
TOTAL_CHECKS=8
|
|
159
|
+
PASS=$((TOTAL_CHECKS - (FAIL > 0 ? 1 : 0) - (WARN > 0 ? 1 : 0)))
|
|
160
|
+
[ "$PASS" -lt 0 ] && PASS=0
|
|
161
|
+
|
|
162
|
+
# --- Write results ---
|
|
163
|
+
|
|
164
|
+
FINDINGS_JSON=$(printf '%s,' "${FINDINGS[@]}" 2>/dev/null | sed 's/,$//')
|
|
165
|
+
[ -z "$FINDINGS_JSON" ] && FINDINGS_JSON=""
|
|
166
|
+
|
|
167
|
+
cat > "$RESULTS_FILE" <<EOF
|
|
168
|
+
{
|
|
169
|
+
"addon": "compliance-hipaa",
|
|
170
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
171
|
+
"summary": {
|
|
172
|
+
"total_checks": ${TOTAL_CHECKS},
|
|
173
|
+
"pass": $((TOTAL_CHECKS - (FAIL > 0 ? 1 : 0) - (WARN > 0 ? 1 : 0))),
|
|
174
|
+
"fail": ${FAIL},
|
|
175
|
+
"warn": ${WARN}
|
|
176
|
+
},
|
|
177
|
+
"findings": [${FINDINGS_JSON}]
|
|
178
|
+
}
|
|
179
|
+
EOF
|
|
180
|
+
|
|
181
|
+
log "Results written to ${RESULTS_FILE}"
|
|
182
|
+
log "Summary: ${FAIL} failures, ${WARN} warnings"
|
|
183
|
+
|
|
184
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# HIPAA Compliance Context
|
|
2
|
+
|
|
3
|
+
This context overlay applies to all agents working on code in this project. The codebase handles Protected Health Information (PHI) and must comply with the HIPAA Security Rule and Privacy Rule.
|
|
4
|
+
|
|
5
|
+
## PHI Field Rules
|
|
6
|
+
|
|
7
|
+
PHI includes any individually identifiable health information: names, dates of birth, medical record numbers, diagnoses, treatment records, SSNs, email addresses, phone numbers, IP addresses (when linked to a patient), and any combination of fields that could identify a patient.
|
|
8
|
+
|
|
9
|
+
### Absolute Rules (Never Violate)
|
|
10
|
+
|
|
11
|
+
1. **Never log PHI.** No patient names, DOBs, MRNs, diagnoses, or identifiers in `console.log`, `console.error`, `logger.*`, or any logging output. Use opaque identifiers (e.g., hashed IDs) in logs.
|
|
12
|
+
|
|
13
|
+
2. **Never store PHI in client-side state** that persists beyond the session. No `localStorage`, `sessionStorage`, `IndexedDB`, or cookies containing PHI. In-memory state (e.g., Svelte stores) is acceptable only during an active authenticated session.
|
|
14
|
+
|
|
15
|
+
3. **Never include PHI in URLs.** No patient identifiers in route params, query strings, or URL fragments. Use opaque tokens that resolve server-side.
|
|
16
|
+
|
|
17
|
+
4. **Never expose PHI in browser storage.** This includes service worker caches, browser history entries, and `window.name`.
|
|
18
|
+
|
|
19
|
+
5. **Encrypt PHI at rest.** Firestore data containing PHI must use encrypted fields or be stored in collections with server-side encryption enabled. Backups must be encrypted.
|
|
20
|
+
|
|
21
|
+
6. **Encrypt PHI in transit.** All API calls must use HTTPS. Verify TLS configuration. No HTTP fallbacks.
|
|
22
|
+
|
|
23
|
+
7. **Minimum Necessary principle.** Only fetch and display the PHI fields required for the current operation. Do not load full patient records when only a name is needed.
|
|
24
|
+
|
|
25
|
+
8. **Audit all PHI access.** Every read/write of PHI must produce an audit log entry with: who accessed it, when, what was accessed, and why (purpose/context).
|
|
26
|
+
|
|
27
|
+
## Authentication Requirements
|
|
28
|
+
|
|
29
|
+
- All endpoints serving PHI require authentication
|
|
30
|
+
- Sessions must time out after 15 minutes of inactivity
|
|
31
|
+
- Re-authentication required for sensitive operations (viewing full records, exporting data)
|
|
32
|
+
- Multi-factor authentication required for administrative access
|
|
33
|
+
- Failed login attempts must be logged and rate-limited
|
|
34
|
+
- Password requirements: minimum 12 characters, complexity rules enforced
|
|
35
|
+
|
|
36
|
+
## Authorization Patterns
|
|
37
|
+
|
|
38
|
+
- Role-based access control (RBAC) for all PHI access
|
|
39
|
+
- Principle of least privilege: default deny, explicit grants
|
|
40
|
+
- Provider-patient relationships must be verified before granting access
|
|
41
|
+
- Administrative overrides must be logged and auditable
|
|
42
|
+
- Authorization checks happen server-side, never client-only
|
|
43
|
+
- Firestore security rules must enforce access control at the document level
|
|
44
|
+
|
|
45
|
+
## Session Security
|
|
46
|
+
|
|
47
|
+
- Session tokens must be cryptographically random, minimum 128 bits
|
|
48
|
+
- Bind sessions to client fingerprint (IP + user-agent) where feasible
|
|
49
|
+
- Invalidate sessions on password change
|
|
50
|
+
- Provide explicit logout that clears all session artifacts
|
|
51
|
+
- No session data in URLs
|
|
52
|
+
|
|
53
|
+
## Secret Management
|
|
54
|
+
|
|
55
|
+
- No secrets in source code, environment files committed to VCS, or client bundles
|
|
56
|
+
- Use environment variables or a secret manager (e.g., GCP Secret Manager)
|
|
57
|
+
- Rotate secrets on a schedule and after any suspected compromise
|
|
58
|
+
- API keys for PHI-serving endpoints must be scoped and rotatable
|
|
59
|
+
|
|
60
|
+
## Firestore Security Rules
|
|
61
|
+
|
|
62
|
+
When writing or modifying Firestore security rules for PHI collections:
|
|
63
|
+
|
|
64
|
+
- Require `request.auth != null` on all PHI documents
|
|
65
|
+
- Validate `request.auth.token` claims for role-based access
|
|
66
|
+
- Use `resource.data` checks to enforce ownership/relationship constraints
|
|
67
|
+
- Never use wildcard rules (`allow read, write: if true`) on PHI collections
|
|
68
|
+
- Log rule denials for security monitoring
|
|
69
|
+
|
|
70
|
+
## Encryption Requirements
|
|
71
|
+
|
|
72
|
+
- AES-256 for data at rest
|
|
73
|
+
- TLS 1.2+ for data in transit
|
|
74
|
+
- Field-level encryption for highly sensitive PHI (SSN, full medical records)
|
|
75
|
+
- Key management via cloud KMS; never store encryption keys alongside encrypted data
|
|
76
|
+
- Key rotation policy: at least annually, immediately after suspected compromise
|
|
77
|
+
|
|
78
|
+
## Code Review Checklist
|
|
79
|
+
|
|
80
|
+
When reviewing code that touches PHI, verify:
|
|
81
|
+
|
|
82
|
+
- [ ] No PHI in log statements
|
|
83
|
+
- [ ] No PHI in URLs or query parameters
|
|
84
|
+
- [ ] No PHI in client-side persistent storage
|
|
85
|
+
- [ ] Authentication required on all PHI endpoints
|
|
86
|
+
- [ ] Authorization checks are server-side
|
|
87
|
+
- [ ] Minimum necessary data fetched
|
|
88
|
+
- [ ] Audit logging present for PHI access
|
|
89
|
+
- [ ] Error responses do not leak PHI
|
|
90
|
+
- [ ] Input validation prevents injection attacks
|
|
91
|
+
- [ ] HTTPS enforced, no mixed content
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: compliance-hipaa
|
|
2
|
+
description: "HIPAA compliance checks, PHI detection, and security controls"
|
|
3
|
+
version: 1
|
|
4
|
+
patches:
|
|
5
|
+
forge_yaml:
|
|
6
|
+
verification.security.enabled: true
|
|
7
|
+
addons:
|
|
8
|
+
- "+compliance-hipaa"
|
|
9
|
+
files:
|
|
10
|
+
- source: hipaa-context.md
|
|
11
|
+
target: .forge/addons/hipaa-context.md
|
|
12
|
+
- source: hipaa-checks.sh
|
|
13
|
+
target: .forge/addons/hipaa-checks.sh
|
|
14
|
+
post_install:
|
|
15
|
+
- "pip install semgrep 2>/dev/null || echo 'Install semgrep for full HIPAA scanning: pip install semgrep'"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# soc2-checks.sh — SOC2 compliance scanning for forge projects
|
|
5
|
+
# Verifies audit logging, access controls, and change management practices
|
|
6
|
+
|
|
7
|
+
RESULTS_FILE=".forge/state/soc2-results.json"
|
|
8
|
+
PASS=0
|
|
9
|
+
FAIL=0
|
|
10
|
+
WARN=0
|
|
11
|
+
FINDINGS=()
|
|
12
|
+
|
|
13
|
+
mkdir -p "$(dirname "$RESULTS_FILE")"
|
|
14
|
+
|
|
15
|
+
# --- Helpers ---
|
|
16
|
+
|
|
17
|
+
log() { echo "[soc2] $*"; }
|
|
18
|
+
finding() {
|
|
19
|
+
local severity="$1" file="$2" line="$3" rule="$4" detail="$5"
|
|
20
|
+
FINDINGS+=("{\"severity\":\"${severity}\",\"file\":\"${file}\",\"line\":${line},\"rule\":\"${rule}\",\"detail\":\"${detail}\"}")
|
|
21
|
+
if [ "$severity" = "fail" ]; then
|
|
22
|
+
FAIL=$((FAIL + 1))
|
|
23
|
+
else
|
|
24
|
+
WARN=$((WARN + 1))
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# --- Check 1: Audit logging presence ---
|
|
29
|
+
|
|
30
|
+
log "Checking for audit logging infrastructure..."
|
|
31
|
+
|
|
32
|
+
HAS_AUDIT=false
|
|
33
|
+
AUDIT_PATTERNS=("auditLog\|audit_log\|AuditLog" "createAuditEntry\|logAudit\|writeAuditLog" "audit.*collection\|audit.*table")
|
|
34
|
+
|
|
35
|
+
for pattern in "${AUDIT_PATTERNS[@]}"; do
|
|
36
|
+
if grep -rq "$pattern" --include='*.ts' --include='*.js' --include='*.py' . 2>/dev/null; then
|
|
37
|
+
HAS_AUDIT=true
|
|
38
|
+
break
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if [ "$HAS_AUDIT" = false ]; then
|
|
43
|
+
finding "fail" "project" 0 "no-audit-logging" "No audit logging infrastructure detected — SOC2 requires comprehensive audit trails"
|
|
44
|
+
else
|
|
45
|
+
PASS=$((PASS + 1))
|
|
46
|
+
log " Audit logging infrastructure found"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# --- Check 2: Auth on server endpoints ---
|
|
50
|
+
|
|
51
|
+
log "Checking server endpoints for authentication..."
|
|
52
|
+
|
|
53
|
+
UNPROTECTED=0
|
|
54
|
+
if [ -d "src/routes" ]; then
|
|
55
|
+
while IFS= read -r file; do
|
|
56
|
+
if ! grep -q 'locals\.\(user\|session\|auth\)\|getSession\|requireAuth\|authenticate\|verifyToken\|event\.locals\|isAuthenticated' "$file" 2>/dev/null; then
|
|
57
|
+
finding "warn" "$file" 0 "endpoint-no-auth" "Server endpoint may lack authentication guard"
|
|
58
|
+
UNPROTECTED=$((UNPROTECTED + 1))
|
|
59
|
+
fi
|
|
60
|
+
done < <(find . -path '*/routes/*' \( -name '+server.ts' -o -name '+server.js' \) -not -path '*/node_modules/*' 2>/dev/null | head -100)
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [ "$UNPROTECTED" -eq 0 ]; then
|
|
64
|
+
PASS=$((PASS + 1))
|
|
65
|
+
log " All server endpoints appear to have auth guards"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# --- Check 3: No secrets in code ---
|
|
69
|
+
|
|
70
|
+
log "Checking for hardcoded secrets..."
|
|
71
|
+
|
|
72
|
+
SECRET_HITS=0
|
|
73
|
+
SECRET_PATTERNS=(
|
|
74
|
+
'password\s*=\s*["\x27][^"\x27]\{8,\}'
|
|
75
|
+
'api[_-]\?key\s*=\s*["\x27][A-Za-z0-9]\{20,\}'
|
|
76
|
+
'secret\s*=\s*["\x27][^"\x27]\{8,\}'
|
|
77
|
+
'PRIVATE.KEY.*BEGIN'
|
|
78
|
+
'token\s*=\s*["\x27][A-Za-z0-9]\{30,\}'
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
for pattern in "${SECRET_PATTERNS[@]}"; do
|
|
82
|
+
while IFS=: read -r file line _match; do
|
|
83
|
+
[ -z "$file" ] && continue
|
|
84
|
+
finding "fail" "$file" "$line" "hardcoded-secret" "Possible hardcoded secret — use environment variables or secret manager"
|
|
85
|
+
SECRET_HITS=$((SECRET_HITS + 1))
|
|
86
|
+
done < <(grep -rn "$pattern" --include='*.ts' --include='*.js' --include='*.py' --include='*.env*' . 2>/dev/null | grep -v node_modules | grep -v '\.test\.\|\.spec\.\|\.example\|\.sample' | head -50 || true)
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
if [ "$SECRET_HITS" -eq 0 ]; then
|
|
90
|
+
PASS=$((PASS + 1))
|
|
91
|
+
log " No hardcoded secrets detected"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# --- Check 4: Change management — branch protection evidence ---
|
|
95
|
+
|
|
96
|
+
log "Checking change management practices..."
|
|
97
|
+
|
|
98
|
+
if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
|
|
99
|
+
# Check if there are direct commits to main (not merge commits)
|
|
100
|
+
DIRECT_COMMITS=$(git log --first-parent --no-merges --oneline main 2>/dev/null | head -10 | wc -l | tr -d ' ')
|
|
101
|
+
if [ "$DIRECT_COMMITS" -gt 3 ]; then
|
|
102
|
+
finding "warn" ".git" 0 "direct-commits-to-main" "Found ${DIRECT_COMMITS} direct (non-merge) commits on main — use PRs for all changes"
|
|
103
|
+
else
|
|
104
|
+
PASS=$((PASS + 1))
|
|
105
|
+
log " Change management: main branch uses merge commits (PRs)"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Check for unsigned commits (optional but recommended)
|
|
109
|
+
UNSIGNED=$(git log --format='%G?' -10 2>/dev/null | grep -c 'N' || true)
|
|
110
|
+
if [ "$UNSIGNED" -gt 5 ]; then
|
|
111
|
+
finding "warn" ".git" 0 "unsigned-commits" "Most recent commits are unsigned — consider requiring commit signing"
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
finding "warn" "." 0 "no-git" "Not a git repository — cannot verify change management"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# --- Check 5: Error handling — no internal details leaked ---
|
|
118
|
+
|
|
119
|
+
log "Checking for leaked internal details in error responses..."
|
|
120
|
+
|
|
121
|
+
ERROR_LEAK_PATTERNS=(
|
|
122
|
+
'res\.\(status\|json\).*stack\|stack.*res\.\(status\|json\)'
|
|
123
|
+
'response.*error\.message\|error\.message.*response'
|
|
124
|
+
'catch.*res\..*500.*err\.\(message\|stack\)'
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
LEAK_HITS=0
|
|
128
|
+
for pattern in "${ERROR_LEAK_PATTERNS[@]}"; do
|
|
129
|
+
while IFS=: read -r file line _match; do
|
|
130
|
+
[ -z "$file" ] && continue
|
|
131
|
+
finding "warn" "$file" "$line" "error-info-leak" "Error response may expose internal details — sanitize before sending to client"
|
|
132
|
+
LEAK_HITS=$((LEAK_HITS + 1))
|
|
133
|
+
done < <(grep -rn "$pattern" --include='*.ts' --include='*.js' . 2>/dev/null | grep -v node_modules | grep -v '\.test\.\|\.spec\.' | head -30 || true)
|
|
134
|
+
done
|
|
135
|
+
|
|
136
|
+
if [ "$LEAK_HITS" -eq 0 ]; then
|
|
137
|
+
PASS=$((PASS + 1))
|
|
138
|
+
log " No obvious error info leaks detected"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# --- Check 6: HTTPS enforcement ---
|
|
142
|
+
|
|
143
|
+
log "Checking for non-HTTPS API calls..."
|
|
144
|
+
|
|
145
|
+
HTTP_HITS=0
|
|
146
|
+
while IFS=: read -r file line _match; do
|
|
147
|
+
[ -z "$file" ] && continue
|
|
148
|
+
if echo "$_match" | grep -q 'localhost\|127\.0\.0\.1\|0\.0\.0\.0'; then
|
|
149
|
+
continue
|
|
150
|
+
fi
|
|
151
|
+
finding "fail" "$file" "$line" "no-tls" "HTTP (non-TLS) API call — use HTTPS"
|
|
152
|
+
HTTP_HITS=$((HTTP_HITS + 1))
|
|
153
|
+
done < <(grep -rn "fetch(['\"]http:" --include='*.ts' --include='*.js' --include='*.svelte' . 2>/dev/null | grep -v node_modules | grep -v '\.test\.\|\.spec\.' | head -50 || true)
|
|
154
|
+
|
|
155
|
+
if [ "$HTTP_HITS" -eq 0 ]; then
|
|
156
|
+
PASS=$((PASS + 1))
|
|
157
|
+
log " All API calls use HTTPS"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# --- Check 7: Dependency vulnerabilities ---
|
|
161
|
+
|
|
162
|
+
log "Checking for known dependency vulnerabilities..."
|
|
163
|
+
|
|
164
|
+
if [ -f "package-lock.json" ] || [ -f "yarn.lock" ]; then
|
|
165
|
+
AUDIT_OUTPUT=$(npm audit --json 2>/dev/null || true)
|
|
166
|
+
VULN_COUNT=$(echo "$AUDIT_OUTPUT" | jq '.metadata.vulnerabilities.high + .metadata.vulnerabilities.critical' 2>/dev/null || echo "0")
|
|
167
|
+
if [ "$VULN_COUNT" -gt 0 ] 2>/dev/null; then
|
|
168
|
+
finding "fail" "package.json" 0 "vulnerable-deps" "${VULN_COUNT} high/critical dependency vulnerabilities found — run npm audit fix"
|
|
169
|
+
else
|
|
170
|
+
PASS=$((PASS + 1))
|
|
171
|
+
log " No high/critical dependency vulnerabilities"
|
|
172
|
+
fi
|
|
173
|
+
elif [ -f "requirements.txt" ]; then
|
|
174
|
+
if command -v pip-audit > /dev/null 2>&1; then
|
|
175
|
+
pip-audit -r requirements.txt --format json 2>/dev/null > /tmp/forge-pip-audit.json || true
|
|
176
|
+
PY_VULNS=$(jq 'length' /tmp/forge-pip-audit.json 2>/dev/null || echo "0")
|
|
177
|
+
if [ "$PY_VULNS" -gt 0 ]; then
|
|
178
|
+
finding "fail" "requirements.txt" 0 "vulnerable-deps" "${PY_VULNS} Python dependency vulnerabilities found"
|
|
179
|
+
else
|
|
180
|
+
PASS=$((PASS + 1))
|
|
181
|
+
fi
|
|
182
|
+
else
|
|
183
|
+
finding "warn" "requirements.txt" 0 "no-dep-audit" "pip-audit not installed — cannot check Python dependencies"
|
|
184
|
+
fi
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
# --- Check 8: Semgrep SOC2-relevant rules ---
|
|
188
|
+
|
|
189
|
+
if command -v semgrep > /dev/null 2>&1; then
|
|
190
|
+
log "Running semgrep security rules..."
|
|
191
|
+
semgrep --config "p/security-audit" --json --quiet . 2>/dev/null > /tmp/forge-semgrep-soc2.json || true
|
|
192
|
+
|
|
193
|
+
SEMGREP_COUNT=$(jq '.results | length' /tmp/forge-semgrep-soc2.json 2>/dev/null || echo "0")
|
|
194
|
+
if [ "$SEMGREP_COUNT" -gt 0 ]; then
|
|
195
|
+
log "Semgrep found ${SEMGREP_COUNT} security findings"
|
|
196
|
+
while IFS= read -r sg_finding; do
|
|
197
|
+
FINDINGS+=("$sg_finding")
|
|
198
|
+
FAIL=$((FAIL + 1))
|
|
199
|
+
done < <(jq -c '.results[] | {severity:"fail",file:.path,line:.start.line,rule:.check_id,detail:.extra.message}' /tmp/forge-semgrep-soc2.json 2>/dev/null || true)
|
|
200
|
+
else
|
|
201
|
+
PASS=$((PASS + 1))
|
|
202
|
+
log " Semgrep: no security findings"
|
|
203
|
+
fi
|
|
204
|
+
else
|
|
205
|
+
log "Semgrep not installed — skipping advanced security rules (install with: pip install semgrep)"
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# --- Write results ---
|
|
209
|
+
|
|
210
|
+
FINDINGS_JSON=$(printf '%s,' "${FINDINGS[@]}" 2>/dev/null | sed 's/,$//')
|
|
211
|
+
[ -z "$FINDINGS_JSON" ] && FINDINGS_JSON=""
|
|
212
|
+
|
|
213
|
+
TOTAL=$((PASS + FAIL + WARN))
|
|
214
|
+
|
|
215
|
+
cat > "$RESULTS_FILE" <<EOF
|
|
216
|
+
{
|
|
217
|
+
"addon": "compliance-soc2",
|
|
218
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
219
|
+
"summary": {
|
|
220
|
+
"total_checks": ${TOTAL},
|
|
221
|
+
"pass": ${PASS},
|
|
222
|
+
"fail": ${FAIL},
|
|
223
|
+
"warn": ${WARN}
|
|
224
|
+
},
|
|
225
|
+
"findings": [${FINDINGS_JSON}]
|
|
226
|
+
}
|
|
227
|
+
EOF
|
|
228
|
+
|
|
229
|
+
log "Results written to ${RESULTS_FILE}"
|
|
230
|
+
log "Summary: ${PASS} passed, ${FAIL} failures, ${WARN} warnings"
|
|
231
|
+
|
|
232
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# SOC2 Compliance Context
|
|
2
|
+
|
|
3
|
+
This context overlay applies to all agents working on code in this project. The codebase must meet SOC2 Type II Trust Services Criteria across Security, Availability, Processing Integrity, Confidentiality, and Privacy.
|
|
4
|
+
|
|
5
|
+
## Access Control (CC6)
|
|
6
|
+
|
|
7
|
+
### Authentication
|
|
8
|
+
|
|
9
|
+
- All user-facing endpoints require authentication
|
|
10
|
+
- Service-to-service calls use short-lived tokens or mutual TLS
|
|
11
|
+
- No shared accounts or credentials
|
|
12
|
+
- Password policy: minimum 12 characters, complexity enforced, no reuse of last 12 passwords
|
|
13
|
+
- MFA required for all administrative and production access
|
|
14
|
+
- SSO integration required for enterprise users where available
|
|
15
|
+
|
|
16
|
+
### Authorization
|
|
17
|
+
|
|
18
|
+
- Role-based access control (RBAC) enforced server-side
|
|
19
|
+
- Principle of least privilege: users get minimum permissions for their role
|
|
20
|
+
- Privilege escalation requires approval and is logged
|
|
21
|
+
- API keys are scoped to specific resources and operations
|
|
22
|
+
- Regularly review and revoke unused access grants
|
|
23
|
+
- Document all roles and their permission boundaries
|
|
24
|
+
|
|
25
|
+
### Session Management
|
|
26
|
+
|
|
27
|
+
- Session timeout after 30 minutes of inactivity (15 minutes for admin sessions)
|
|
28
|
+
- Sessions invalidated on logout, password change, and role change
|
|
29
|
+
- Concurrent session limits enforced where appropriate
|
|
30
|
+
- Session tokens are cryptographically random, not guessable
|
|
31
|
+
|
|
32
|
+
## Audit Logging (CC7)
|
|
33
|
+
|
|
34
|
+
### What to Log
|
|
35
|
+
|
|
36
|
+
Every security-relevant event must produce a structured audit log entry:
|
|
37
|
+
|
|
38
|
+
- **Authentication events**: login, logout, failed login, MFA challenge, password change
|
|
39
|
+
- **Authorization events**: access granted, access denied, privilege escalation
|
|
40
|
+
- **Data events**: create, read, update, delete of sensitive records
|
|
41
|
+
- **Admin events**: configuration changes, user management, role changes
|
|
42
|
+
- **System events**: deployments, restarts, error spikes, health check failures
|
|
43
|
+
|
|
44
|
+
### Log Format
|
|
45
|
+
|
|
46
|
+
Each audit log entry must include:
|
|
47
|
+
|
|
48
|
+
- `timestamp` (ISO 8601, UTC)
|
|
49
|
+
- `actor` (user ID or service identity)
|
|
50
|
+
- `action` (what was done)
|
|
51
|
+
- `resource` (what was affected)
|
|
52
|
+
- `result` (success/failure)
|
|
53
|
+
- `ip_address` (source IP)
|
|
54
|
+
- `context` (additional relevant metadata)
|
|
55
|
+
|
|
56
|
+
### Log Protection
|
|
57
|
+
|
|
58
|
+
- Audit logs are append-only; never delete or modify in place
|
|
59
|
+
- Logs stored in a separate system/collection from application data
|
|
60
|
+
- Log retention: minimum 1 year
|
|
61
|
+
- Logs must not contain secrets, passwords, tokens, or PII beyond user IDs
|
|
62
|
+
- Alerting on log tampering or gaps
|
|
63
|
+
|
|
64
|
+
## Change Management (CC8)
|
|
65
|
+
|
|
66
|
+
### Code Changes
|
|
67
|
+
|
|
68
|
+
- All changes go through pull requests with at least one reviewer
|
|
69
|
+
- CI/CD pipeline must pass before merge (tests, lint, type checks)
|
|
70
|
+
- No direct commits to main/production branches
|
|
71
|
+
- Commit messages reference the ticket/issue being addressed
|
|
72
|
+
- Changes to security-sensitive code require security-aware reviewer
|
|
73
|
+
|
|
74
|
+
### Deployment
|
|
75
|
+
|
|
76
|
+
- All deployments are automated via CI/CD
|
|
77
|
+
- No manual production deployments
|
|
78
|
+
- Deployment artifacts are immutable and versioned
|
|
79
|
+
- Rollback procedure documented and tested
|
|
80
|
+
- Deployment logs captured for audit trail
|
|
81
|
+
|
|
82
|
+
### Infrastructure Changes
|
|
83
|
+
|
|
84
|
+
- Infrastructure as Code (IaC) for all environments
|
|
85
|
+
- Infrastructure changes go through the same review process as code
|
|
86
|
+
- Environment parity: staging mirrors production configuration
|
|
87
|
+
- Secrets rotated on schedule and after incidents
|
|
88
|
+
|
|
89
|
+
## Data Protection (CC6.1)
|
|
90
|
+
|
|
91
|
+
### Encryption
|
|
92
|
+
|
|
93
|
+
- Data at rest: AES-256 or equivalent
|
|
94
|
+
- Data in transit: TLS 1.2+ for all connections
|
|
95
|
+
- Database connections use TLS
|
|
96
|
+
- Backup encryption required
|
|
97
|
+
- Key management via cloud KMS
|
|
98
|
+
|
|
99
|
+
### Data Classification
|
|
100
|
+
|
|
101
|
+
- Classify data by sensitivity: public, internal, confidential, restricted
|
|
102
|
+
- Apply controls proportional to classification
|
|
103
|
+
- Document data flows showing where each classification level is stored and transmitted
|
|
104
|
+
- Data retention policies enforced per classification
|
|
105
|
+
|
|
106
|
+
### Data Disposal
|
|
107
|
+
|
|
108
|
+
- Secure deletion when data retention period expires
|
|
109
|
+
- Cryptographic erasure acceptable for encrypted data
|
|
110
|
+
- Document disposal procedures and evidence
|
|
111
|
+
|
|
112
|
+
## Availability (CC9)
|
|
113
|
+
|
|
114
|
+
### Uptime
|
|
115
|
+
|
|
116
|
+
- Define and monitor SLA targets
|
|
117
|
+
- Health check endpoints for all services
|
|
118
|
+
- Automated alerting on downtime or degradation
|
|
119
|
+
- Incident response runbook documented
|
|
120
|
+
|
|
121
|
+
### Disaster Recovery
|
|
122
|
+
|
|
123
|
+
- Backup frequency: at least daily for critical data
|
|
124
|
+
- Backup restoration tested quarterly
|
|
125
|
+
- Recovery Time Objective (RTO) and Recovery Point Objective (RPO) documented
|
|
126
|
+
- Multi-region or multi-zone deployment for critical services
|
|
127
|
+
|
|
128
|
+
### Capacity
|
|
129
|
+
|
|
130
|
+
- Monitor resource utilization (CPU, memory, storage, network)
|
|
131
|
+
- Auto-scaling configured where applicable
|
|
132
|
+
- Capacity planning reviewed quarterly
|
|
133
|
+
|
|
134
|
+
## Code Review Checklist
|
|
135
|
+
|
|
136
|
+
When reviewing code for SOC2 compliance, verify:
|
|
137
|
+
|
|
138
|
+
- [ ] Authentication required on all endpoints
|
|
139
|
+
- [ ] Authorization checks are server-side with least privilege
|
|
140
|
+
- [ ] Audit logging present for security-relevant operations
|
|
141
|
+
- [ ] Audit logs do not contain secrets or excessive PII
|
|
142
|
+
- [ ] Error responses do not leak internal details
|
|
143
|
+
- [ ] Input validation prevents injection attacks
|
|
144
|
+
- [ ] HTTPS enforced, no mixed content
|
|
145
|
+
- [ ] Secrets managed via environment variables or secret manager
|
|
146
|
+
- [ ] Change goes through standard PR review process
|
|
147
|
+
- [ ] Tests cover the new functionality
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: compliance-soc2
|
|
2
|
+
description: "SOC2 compliance checks — audit logging, access control, and change management evidence"
|
|
3
|
+
version: 1
|
|
4
|
+
patches:
|
|
5
|
+
forge_yaml:
|
|
6
|
+
verification.security.enabled: true
|
|
7
|
+
addons:
|
|
8
|
+
- "+compliance-soc2"
|
|
9
|
+
files:
|
|
10
|
+
- source: soc2-context.md
|
|
11
|
+
target: .forge/addons/soc2-context.md
|
|
12
|
+
- source: soc2-checks.sh
|
|
13
|
+
target: .forge/addons/soc2-checks.sh
|
|
14
|
+
post_install:
|
|
15
|
+
- "pip install semgrep 2>/dev/null || echo 'Install semgrep for full SOC2 scanning: pip install semgrep'"
|