Home › Technical › A11y & security

Accessibility & security

JD calls out: WCAG-compliant interfaces, OWASP best practices, XSS prevention, CSP awareness, safe API integration. Both topics are senior-level expectations that juniors often fumble. Showing fluency here separates you.

01Accessibility (a11y)

What is WCAG and what levels matter?

Web Content Accessibility Guidelines — the international standard. Three levels:

  • A — minimum. If you fail this, the site is broken for many users.
  • AA — the practical industry target. What "WCAG-compliant" usually means in a job description and what most regulations (ADA, EAA) effectively require.
  • AAA — gold standard. Hard to hit fully across an app.

Current versions: 2.1 widely required, 2.2 published 2023 — adds focus visibility, target size, dragging alternatives. Some jurisdictions are starting to require 2.2.

The four principles (POUR): Perceivable, Operable, Understandable, Robust.

What are the basics every developer should get right?
  1. Semantic HTML. Buttons are <button>, links are <a>, headings are real <h1><h6> in document order.
  2. Labels for inputs. Every input has a <label> or aria-label. Placeholder text is not a label.
  3. Alt text for images. Decorative images get alt="", content images describe the content.
  4. Keyboard navigation. Tab order makes sense, focus is visible, no traps. Custom widgets need keyboard handlers.
  5. Color contrast. 4.5:1 for normal text, 3:1 for large text and UI components.
  6. Don't rely on color alone to convey meaning — pair with icons, text, or patterns.
  7. Form errors are programmatically associated with their fields and announced.
  8. Skip-to-content link at the top of the page for keyboard users.

If you ship a button that's actually a div with an onclick — you've created an inaccessible button, no matter how nicely it animates.

When do you reach for ARIA?

The first rule of ARIA: don't use ARIA. Use a real <button> instead of <div role="button" tabindex="0"> any day.

ARIA is for filling gaps where HTML doesn't have a native element — custom widgets like comboboxes, tabs, tree views. The patterns are documented in the WAI-ARIA Authoring Practices.

Common attributes:

  • aria-label / aria-labelledby — naming when there's no visible text
  • aria-describedby — extra description (error message tied to input)
  • aria-expanded / aria-controls — disclosure widgets
  • aria-live — announce dynamic changes (toasts, errors). Use polite by default; assertive only for genuinely urgent messages.
  • aria-current — mark the current page in nav
  • role="status" / role="alert" — for live regions

Bad ARIA is worse than no ARIA. Test with a screen reader if you're rolling something custom.

How do you make a complex data grid accessible?

This is directly relevant — AG Grid is the centerpiece of the Omnesoft frontend.

  • Use the grid role pattern (or let AG Grid do it — it has built-in accessibility but you have to use it correctly).
  • Column headers connected to cells via proper roles.
  • Keyboard navigation — arrow keys move between cells, Enter to edit, Escape to cancel. Standard AG Grid behavior; verify it's not broken by custom renderers.
  • Focus management — when editing, focus enters the editor; when committing, focus returns to the cell.
  • Custom cell renderers must be focusable and operable. A button-cell-renderer that's a div is a regression.
  • Announce status changes — when a sort changes, an aria-live region should mention it.
  • Headers should describe column purpose clearly — screen reader users can't see "Status" with a colored badge.

02Security (frontend angle)

What is XSS and how do you prevent it?

Cross-Site Scripting — attacker injects JavaScript into your page that runs in the user's session. Three types:

  • Reflected — payload in a URL, reflected back in the page (e.g. unfiltered search-result rendering).
  • Stored — payload saved on the server (a comment, a profile field) and rendered to other users.
  • DOM-based — entirely client-side, payload manipulates the DOM via vulnerable JS.

Prevention:

  • Don't insert untrusted strings as HTML. React already escapes everything you render via JSX — that's huge protection by default.
  • dangerouslySetInnerHTML is the danger zone. Sanitize with DOMPurify if you must.
  • URLs in href / src need protocol filtering — never allow javascript:.
  • Content Security Policy (CSP) as defense-in-depth — even if XSS slips in, CSP blocks the inline script execution.
  • Validate and sanitize on the backend too — never trust the frontend to be the only filter.
What is CSP and what does a basic policy look like?

Content Security Policy — an HTTP header (or meta tag) that tells the browser what sources of content are allowed. Mitigates XSS by blocking inline scripts, eval, and unauthorized origins.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';

Practical points:

  • Avoid 'unsafe-inline' for scripts — use nonces or hashes if you have inline scripts.
  • frame-ancestors 'none' prevents your app from being framed (clickjacking protection).
  • Start in Content-Security-Policy-Report-Only mode and watch reports before enforcing.
  • CSP is hard to retrofit. Consider it during architecture, not after.
What is CSRF? Does it apply to your SPA?

Cross-Site Request Forgery — attacker tricks a logged-in user's browser into making a state-changing request to your API. Classic example: user is logged into bank.com, visits attacker.com, attacker.com submits a hidden form to bank.com that transfers money.

It applies if:

  • You authenticate with cookies (the browser sends them automatically), AND
  • Your API accepts state-changing requests without an additional token.

Defenses:

  • SameSite cookies (SameSite=Lax or Strict) — modern default, blocks most CSRF.
  • CSRF tokens — a per-session token that must be in the request header. Server rejects requests without it.
  • Bearer tokens in headers (Authorization: Bearer ...) — sidesteps CSRF since browsers don't auto-send them.

If the SPA uses bearer tokens in headers, CSRF is largely a non-issue. If it uses cookies, set SameSite and use anti-CSRF tokens for mutations.

Where should auth tokens live in the frontend?

Trade-off, no perfect answer:

  • HTTP-only secure cookies — JavaScript can't read them, so XSS can't steal them. But subject to CSRF concerns (mitigated with SameSite + tokens). Best for same-domain SPAs.
  • localStorage — simple, works cross-domain. But any XSS reads it. Bad if your app has user-generated content or third-party scripts.
  • In-memory — survives only the page lifetime. Most secure, but the user logs out on every reload unless you pair with a refresh-token flow.
  • sessionStorage — same security profile as localStorage but cleared when the tab closes.

For an enterprise app like Omnesoft's, HTTP-only cookies with SameSite + CSRF tokens is the secure default if cross-domain isn't a concern. Worth probing what they do.

What's clickjacking?

Attacker iframes your page and overlays invisible UI to trick users into clicking. Defense: X-Frame-Options: DENY or the modern frame-ancestors 'none' CSP directive. For SaaS apps that should never be embedded, set this on every response.

What's prototype pollution?

An attack where untrusted input modifies Object.prototype, polluting every object in the runtime. Often happens via deep merge / clone functions that walk into __proto__ or constructor.prototype.

Prevention:

  • Use Object.create(null) for maps that take user keys.
  • Use Map instead of objects when keys are untrusted.
  • Vet deep-merge libs — newer versions are usually patched.
  • Don't accept arbitrary keys when validating input — use schema validation (zod) with strict shapes.
What about supply-chain risk in npm packages?

Real concern. Mitigations:

  • Lockfiles — committed, audited, and reviewed when they change.
  • Audit toolsnpm audit, Dependabot, Snyk, Socket.
  • Pin transitive deps when needed via overrides / resolutions.
  • Vet new dependencies — number of maintainers, recent activity, alternatives. A two-line library that pulls 30 transitive deps is a bad trade.
  • Subresource Integrity (SRI) for any scripts loaded from CDNs.

03OWASP Top 10 (frontend slice)

You don't need to memorize all 10, but know the ones that have a frontend angle:

OWASP riskFrontend angle
A01: Broken Access ControlDon't trust client-side route guards alone. Backend authorizes every request.
A02: Cryptographic FailuresHTTPS everywhere. Don't roll crypto in JS.
A03: InjectionXSS. Escape output. Sanitize anything you set as HTML.
A04: Insecure DesignThreat model. "What if a malicious user used this?"
A05: Security MisconfigurationCSP, security headers, framework defaults.
A06: Vulnerable ComponentsDependency hygiene. Audit. Update.
A07: Auth FailuresToken storage, session timeout, MFA flows.
A08: Software Integrity FailuresBuild pipeline integrity, SRI on third-party scripts.