Safeguard
AI Security

GitHub Copilot code security: XSS vulnerabilities found in React

Copilot commonly suggests dangerouslySetInnerHTML and unsanitized DOM writes in React. Here's the data on AI-generated XSS risk and how to catch it.

Priya Mehta
DevSecOps Engineer
Updated 7 min read

GitHub Copilot writes a large share of the React code shipping to production today, and a measurable slice of it renders user input in ways that lead directly to cross-site scripting (XSS). In testing of common Copilot prompts — search-term highlighting, comment rendering, markdown previews, query-string-based redirects — the assistant repeatedly suggests dangerouslySetInnerHTML, raw ref.innerHTML writes, and unescaped href values built from URL parameters, all without a sanitization step. This isn't hypothetical or rare: the 2021 NYU/Stanford study "Asleep at the Keyboard?" found that 40% of Copilot completions across scenarios mapped to the MITRE CWE Top 25 contained exploitable vulnerabilities, and XSS-prone patterns show up disproportionately in front-end frameworks because JSX makes it trivially easy to skip an escaping step the model doesn't know is missing. The irony is that Copilot is trained in part on public repositories that already contain both vulnerable snippets and the XSS payloads GitHub issues and security advisories use to demonstrate them — so the model reproduces the vulnerable half of that pairing far more often than the fix. For AppSec teams reviewing AI-assisted commits, this is a repeatable, testable class of defect in React vulnerabilities, not an edge case reserved for careless developers.

What XSS vulnerabilities does GitHub Copilot actually suggest in React code?

Copilot's most common React XSS pattern is suggesting dangerouslySetInnerHTML={{ __html: value }} to solve routine UI problems — highlighting a search term, rendering a markdown preview, or displaying a rich-text comment — without wrapping value in a sanitizer first. A typical Copilot completion for "highlight the matched search term in the result snippet" looks like this — a compact XSS code example that's representative of what shows up across real pull requests, not a contrived one:

function ResultSnippet({ text, query }) {
  const highlighted = text.replace(
    new RegExp(query, 'gi'),
    (match) => `<mark>${match}</mark>`
  );
  return <div dangerouslySetInnerHTML={{ __html: highlighted }} />;
}

If query comes from a URL parameter (which it almost always does in a search UI), an attacker can submit a query like </mark><img src=x onerror=alert(document.cookie)> — this exact string is one of the most common xss example code payloads used in security testing — and it renders as live HTML in every user's browser who clicks the crafted link. The same class of suggestion shows up for markdown preview components built on react-markdown or html-react-parser without the corresponding rehype-sanitize plugin, for chat/comment renderers, and for ref.current.innerHTML = ... patterns Copilot suggests when a developer asks for "custom rendering" inside a useEffect. A related variant is unescaped href construction — Copilot will happily generate <a href={redirectUrl}> from a redirectUrl query parameter with no scheme check, opening the door to javascript: URI injection.

Why does Copilot recommend unsafe patterns instead of sanitizing input first?

Copilot recommends dangerouslySetInnerHTML and similar patterns because it is trained on public GitHub repositories where that exact snippet appears far more often than the sanitized alternative, and the model is optimized to produce the statistically most likely completion, not the most secure one. Copilot has no data-flow or taint-tracking model — it doesn't know that query traces back to useSearchParams() two lines above, and it has no concept of a "sink" that requires a "source" to be sanitized before reaching it. It is pattern-completing, not threat-modeling. This matches what the Stanford CCS 2023 study "Do Users Write More Insecure Code with AI Assistants?" (Perry et al., 2022) found: developers using an AI coding assistant produced measurably less secure code on tasks involving input handling, and were more likely to rate their own AI-assisted code as secure than a control group without the assistant. The tool's fluency creates a false sense of review completeness — the code looks clean, compiles, and renders correctly in a demo, which is exactly the condition under which reviewers skip a second look at how untrusted input reaches the DOM.

How common are AI-generated vulnerabilities across codebases beyond this one example?

AI-generated vulnerabilities are common enough to show up in double-digit percentages of tested completions, and the exposure scales with Copilot's adoption. Beyond the 40% figure from the 2021 NYU/Stanford study across CWE Top 25 scenarios, Microsoft has reported GitHub Copilot crossing 1.8 million paid subscribers with adoption inside more than 50,000 organizations as of its 2024 fiscal reporting — meaning any systemic bias in Copilot's suggestions doesn't stay contained to one team's codebase, it propagates across a large fraction of actively maintained repositories simultaneously. React remains one of the most common frameworks Copilot is invoked against for UI work, and CWE-79 (Improper Neutralization of Input During Web Page Generation, i.e., XSS) has held a top-5 spot in the OWASP and MITRE most-dangerous-weakness lists for over a decade — meaning the training corpus itself already contains a large volume of legacy, unsanitized rendering code for Copilot to learn from and reproduce.

Does GitHub's own vulnerability filtering catch these XSS suggestions?

No, not consistently for client-side XSS in JSX. GitHub's publicly documented Copilot vulnerability prevention system, introduced in 2023, is built to filter three specific insecure coding patterns at suggestion time: SQL injection, hardcoded credentials, and path traversal. It does not describe DOM-sink taint tracking for patterns like dangerouslySetInnerHTML, innerHTML assignment via refs, or unsanitized href/src construction — the exact sinks that produce XSS in React applications. Separately, GitHub Advanced Security's CodeQL can catch these patterns, but only as a post-hoc repository scan run in CI or on a schedule, not as an inline check while the suggestion is accepted. That gap matters operationally: a developer accepts the Copilot suggestion, the PR passes review because the code "looks right," and the vulnerability isn't flagged until (and unless) a separate SAST job runs against the merged branch — by which point the vulnerable component may already be in a release build or, worse, in a downstream package consumed by other teams.

Which coding patterns and CWEs show up most often in Copilot's React suggestions?

The dominant pattern is CWE-79 (XSS) delivered through four specific sinks: dangerouslySetInnerHTML with concatenated or templated strings, direct ref.current.innerHTML writes inside effects, markdown/rich-text renderers used without a sanitizer plugin, and URL-attribute construction (href, src, action) built from query parameters without scheme allow-listing. A secondary pattern is CWE-116 (Improper Encoding or Escaping of Output), which shows up when Copilot suggests manual string replacement (.replace(/</g, '')) as a "sanitization" step instead of a maintained library — a partial fix that blocks the obvious <script> payload but misses attribute-breakout vectors like " onmouseover=". Both patterns share a root cause: Copilot suggests the shortest code that satisfies the prompt's stated goal (highlight text, render markdown, redirect the user) and treats sanitization as an optional extra step rather than a required part of the task, because the prompt rarely asks for it explicitly.

How Safeguard Helps

Safeguard closes this gap by treating AI-generated code the same way it treats third-party dependencies: nothing ships without a reachability check. Griffin AI, Safeguard's detection engine, scans commits and pull requests for exactly these DOM-sink patterns — dangerouslySetInnerHTML, unsanitized innerHTML writes, unchecked URL attributes — and traces whether attacker-controlled input can actually reach them, so teams aren't stuck triaging every Copilot-authored render call by hand. Reachability analysis confirms whether the tainted query parameter or comment field is actually exposed on a live route before a finding is escalated, cutting false-positive volume from generic pattern matching. Safeguard also generates and ingests SBOMs to track which sanitization libraries (DOMPurify, rehype-sanitize, sanitize-html) are actually present and current across the codebase, since a missing or outdated sanitizer is often the real gap behind a Copilot-introduced XSS finding. When a vulnerable pattern is confirmed, Safeguard opens an auto-fix pull request that wraps the unsafe sink in the appropriate sanitizer call, so the fix ships as fast as the original AI-generated code did.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.