High-level security principles and enforcement rules for building secure applications from the ground up with defense-in-depth
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.
secrets.* only; rotate on exposure| 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 |
/**
* 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!
npm audit + license checkSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer