| name | security-by-design |
|---|---|
| description | Defense-in-depth security principles — OWASP Top 10 prevention, input validation, secure error handling, encryption, least privilege |
| license | MIT |
Applies to all code handling user input, state persistence, external assets, audio, networking, authentication, authorization, or sensitive configuration. Also applies to infrastructure config, CI/CD pipelines, and security-critical changes.
Aligned with Hack23 AB's ISMS — see Information Security Policy and Secure Development Policy.
- Assume Breach — design so a single failure cannot cascade; limit blast radius
- Defense in Depth — multiple independent layers (validate → sanitize → encode → CSP)
- Least Privilege — minimum permissions for tokens, workflows, actions, agents
- Fail Securely — fail-closed; generic error to users, detailed log server-side
- Never Trust Input — validate, sanitize, and encode every external value (client and server)
- Secure by Default — disable unused features; explicit opt-in for risky ones
- Encrypt Everything Sensitive — AES-256 at rest, TLS 1.3+ in transit, SHA-256+ hashing
- No Secrets in Code — env vars and
secrets.*only; rotate on exposure - Audit Everything — log auth, authorization, security events; never log secrets/PII
- Minimize Attack Surface — remove dead features, unused endpoints, unused deps
- Test Security Controls — cover happy + failure paths (≥ 95 % coverage on security code)
- Patch Promptly — Critical ≤ 7 d, High ≤ 30 d, Medium ≤ 90 d (Vulnerability Management)
- Document Decisions — threat models, ADRs, ISMS policy citations in comments/PRs
| OWASP 2021 | Enforcement in this project |
|---|---|
| A01 Broken Access Control | No client-side authorization; render-based capabilities only |
| A02 Cryptographic Failures | TLS only, no custom crypto, no plaintext storage of sensitive data |
| A03 Injection | Validate + encode; never dangerouslySetInnerHTML without DOMPurify |
| A04 Insecure Design | Threat-model per Threat Modeling Policy; STRIDE for new features |
| A05 Security Misconfiguration | Security headers (SECURITY_HEADERS.md), secure defaults |
| A06 Vulnerable Components | npm audit, Dependabot, license gate, SBOMQS ≥ 7.0 |
| A07 Identification/Auth Failures | Not applicable (no user auth in this template) — document when added |
| A08 Software/Data Integrity | SLSA L3 attestations, SHA-pinned Actions, signed releases |
| A09 Logging/Monitoring Failures | GitHub audit + Scorecard + CodeQL alerts reviewed |
| A10 SSRF | Avoid arbitrary URL fetches from untrusted input |
- ISO 27001:2022 — A.8.25 secure development, A.8.28 secure coding, A.8.29 security testing
- NIST CSF 2.0 — PROTECT (PR.DS, PR.PS, PR.IR), DETECT (DE.CM), RESPOND (RS.AN)
- CIS Controls v8.1 — 2 (software inventory), 16 (app security), 18 (pen testing)
/**
* Validate plain-text user input.
* ISMS: Secure Development Policy §Phase 2 — Secure Coding
*/
function validatePlainText(input: unknown, max = 100): string {
if (typeof input !== 'string') throw new TypeError('Input must be a string');
const trimmed = input.trim();
if (trimmed.length === 0) throw new RangeError('Input is required');
if (trimmed.length > max) throw new RangeError('Input too long');
return trimmed;
}
/** Encode untrusted text before inserting it into HTML. */
function escapeHtml(s: string): string {
return s
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// For rich HTML use DOMPurify — never ad-hoc stripping.function handleRequest(): { success?: true; error?: string } {
try {
// do work
return { success: true };
} catch {
// In the browser, avoid console.error with raw details — users can see it.
// Send structured telemetry to restricted server logs instead.
return { error: 'An error occurred. Please try again.' };
}
}/** ISMS: SDP §Phase 2 — Input Validation */
export function saveHighScore(score: number): void {
if (!Number.isFinite(score) || score < 0 || score > 1_000_000) {
throw new RangeError('Invalid score value');
}
localStorage.setItem('highScore', String(Math.floor(score)));
}// BAD: hardcoded secret
const API_KEY = "<API_KEY>";
// BAD: leaking stack trace to user
function handleError(e: Error) { return { error: e.stack }; }
// BAD: no input validation — XSS
document.body.innerHTML = userContent;
// BAD: trusting typeof-less JSON.parse output
const data = JSON.parse(input) as User; // no validation/narrowing!- Every external input is validated + typed + bounded
- Every output to HTML is encoded (or DOMPurify-sanitized)
- No secrets/credentials/tokens in code, logs, or error messages
- Errors fail closed; user message is generic
- Security-sensitive paths have ≥ 95 % test coverage
- New dependencies passed
npm audit+ license check - Policy cited in comments/PR for security-relevant changes
- Threat model updated if attack surface changed