Treat types as development support, not runtime validation
TypeScript can make assumptions visible during development, but its types disappear when code runs. Data from HTTP requests, webhooks, storage, environment variables, browser messages, and third-party APIs remains untrusted. Validate those values at the boundary with an explicit schema, reject unexpected properties where appropriate, and handle failures without revealing internals.
Model authorization separately from types. A correctly shaped object does not prove that the current user may read or change it. Perform permission checks on the server for the specific object and action, and test horizontal access between users or organizations.
Keep data out of executable contexts
Prefer framework-rendered text, textContent, and DOM construction over innerHTML, document.write, eval, new Function, or string-based timers. Validate URLs before assigning them to navigation or resource-loading attributes. If rich HTML is required, sanitize it with a maintained library and restrict the allowed elements, attributes, and URL schemes.
Do not assume a Content Security Policy fixes unsafe code. Use it to reduce the impact of mistakes, then address the unsafe sink itself. For suitable applications, Trusted Types can help prevent plain strings from reaching DOM injection sinks. Review the current MDN CSP guidance and browser support before enforcement.
Manage packages as production code
Every dependency expands the code and maintenance surface. Prefer packages with active maintenance, a clear purpose, and a proportionate dependency tree. Commit lockfiles, use deterministic installs in continuous integration, review changes to install scripts and transitive packages, and run dependency and secret scanning. Remove libraries no longer used.
Do not automatically accept every major upgrade or every automated remediation. Test changes, understand whether a vulnerability reaches your runtime path, and prioritize exploitable production risk. Protect publishing credentials and require multi-factor authentication for package registries and source control.
Separate public configuration from secrets
Anything delivered to a browser can be read by a visitor. Build-time variables prefixed for client exposure are configuration, not secrets. Keep API credentials, signing keys, private tokens, and privileged service calls on the server. Use a managed secret store or protected deployment variables, grant each service only the access it needs, and rotate credentials when staff, vendors, or incidents change the risk.
Avoid logging authentication tokens, full request bodies, payment details, or unnecessary personal information. Structured logs should support detection and investigation without becoming a second sensitive database.
Design secure network and message boundaries
Use TLS and validate certificate errors rather than bypassing them. Configure CORS for known origins and required methods; CORS is a browser sharing policy, not authentication. Validate postMessage origins and message schemas. Protect state-changing requests with suitable session, CSRF, and replay controls.
Apply size limits, timeouts, rate limits, and cancellation to expensive work. Those controls matter for API clients, uploads, regular expressions, parsers, and background tasks. Avoid prototype pollution by rejecting dangerous property names and using libraries that safely merge untrusted objects.
JavaScript and TypeScript review checklist
- Runtime validation exists at every external boundary.
- Authorization is enforced server-side for every protected action and object.
- No secret is bundled into public JavaScript or source maps.
- Dangerous DOM and code-evaluation APIs are absent or tightly reviewed.
- Dependencies are locked, scanned, owned, and routinely updated.
- Security headers, cookies, CORS, and CSP match the deployed architecture.
- Logs and alerts cover authentication, permission failures, and unusual activity.
The most effective review follows data from source to sink. It asks who controls the value, how it is transformed, where it is used, and which permission is required—not simply whether the project compiles without TypeScript errors.












