AngularJS reached end of life on January 1, 2022, when Google stopped shipping security patches for a framework that once powered a large share of the enterprise web. Years later, AngularJS 1.x is still running in production behind banking portals, healthcare intake forms, and internal admin tools that nobody scheduled for a rewrite. That combination — a dead framework with a documented history of serious, hard-to-fix vulnerability classes — makes AngularJS one of the more persistent supply chain risks security teams inherit rather than choose. Its expression sandbox was bypassed so many times between 2013 and 2016 that Google's own engineering team removed the feature entirely rather than keep patching it. Its built-in sanitizer shipped a cross-site scripting bypass as recently as CVE-2020-7676, fixed in version 1.8.0. This post walks through the concrete vulnerability classes in AngularJS, the CVEs and dates that matter, and how to find and fix exposure before an attacker does.
Why is AngularJS still a security risk in 2026?
AngularJS is still a security risk because Google ended all security support for it on January 1, 2022, yet the framework remains embedded in thousands of production applications that were never migrated to Angular 2+, React, or Vue. Google announced a three-year Long Term Support window back in 2018 specifically to give enterprises runway to migrate off AngularJS 1.x, and when that window closed, community and vendor patching stopped along with it. In practice, migration didn't happen for the applications that needed it most: large, revenue-critical monoliths where a framework rewrite competes for budget against feature work and usually loses. The risk also hides in places dependency scanners don't always look — AngularJS is frequently vendored directly into a vendor/ or libs/ folder, pulled in transitively by an older UI component library, or served straight from a CDN URL with no corresponding entry in package.json. Any newly discovered vulnerability in AngularJS 1.x will never receive an official patch, which means every instance found today is a permanent finding unless the code itself is changed.
What was the AngularJS expression sandbox, and why did Google remove it?
The AngularJS expression sandbox was a runtime guard inside the $parse service that tried to stop Angular expressions — the {{ }} interpolation syntax — from reaching JavaScript's global objects and functions, and Google removed it entirely in AngularJS 1.6, released in December 2016, after concluding it could never be made airtight. The sandbox existed because AngularJS expressions are evaluated by Angular's own parser rather than treated as inert strings, so if an attacker could get user-controlled input rendered as an expression — for example through a template built with string concatenation instead of proper data binding — they could potentially execute arbitrary JavaScript. This class of bug is called client-side template injection (CSTI), and it is specific to frameworks like AngularJS that parse and evaluate template expressions client-side. Security researchers, notably the team at PortSwigger, published a steady stream of sandbox bypasses between 2013 and 2016, using tricks like walking through constructor.constructor to reach the global Function constructor. After the fourth or fifth round of "sandbox bypass, patch, bypass again," the Angular team wrote publicly that the sandbox gave developers a false sense of security and recommended Content Security Policy (CSP) and strict output encoding instead — advice that still applies to any AngularJS 1.x codebase running today.
What is CVE-2020-7676 and how does it enable XSS?
CVE-2020-7676 is a cross-site scripting vulnerability in AngularJS's built-in HTML sanitizer that let attackers bypass the sanitizer's URL checks on a[href] and img[src] attributes using specially crafted markup, and it was fixed in AngularJS 1.8.0. AngularJS ships a sanitization layer — the $sce (Strict Contextual Escaping) service plus the optional ngSanitize module — specifically so developers can safely render user-supplied HTML through directives like ng-bind-html without manually stripping dangerous tags. CVE-2020-7676 mattered because it broke that guarantee: an attacker could submit a URL-bearing attribute crafted to slip past the sanitizer's allow-list logic, and any application that trusted the sanitizer to make ng-bind-html output safe was exposed to stored or reflected XSS. The practical impact fell hardest on comment sections, rich-text CMS fields, and any user profile or messaging feature where HTML content moved from an API response into ng-bind-html. Teams still running pre-1.8.0 AngularJS should treat this as a confirmed, exploitable finding rather than a theoretical one — the fix is a single version bump, but only if the application isn't relying on other now-unsupported 1.x behavior that breaks on upgrade, which is precisely why many teams never made the jump.
How does client-side template injection differ from traditional XSS in AngularJS apps?
Client-side template injection differs from traditional XSS because the payload is executed by AngularJS's own expression parser inside double curly braces rather than by injecting a <script> tag or an onerror handler, which means standard XSS filters and WAF signatures built to catch script tags often miss it entirely. A penetration tester probing for CSTI typically submits a value like {{7*7}} into an input field; if the rendered page shows 49 instead of the literal string, the application is echoing user input directly into an Angular template context, and the tester escalates from there toward sandbox-bypass payloads on any AngularJS version before 1.6. This detection gap is the reason CSTI shows up repeatedly in bug bounty writeups on pre-2017 AngularJS applications years after the underlying sandbox-removal advisory was published — the vulnerable pattern (building template strings from request data with $compile or $parse instead of using Angular's built-in data binding) keeps getting reintroduced by new code even in otherwise-patched applications. Static analysis tools that only pattern-match for innerHTML or document.write typically don't flag this because the dangerous call is $compile() or $parse(), not a browser DOM API.
Which AngularJS APIs are most commonly the source of real-world vulnerabilities?
The most common sources of real-world AngularJS vulnerabilities are ng-bind-html combined with $sce.trustAsHtml() on user-controlled data, custom directives that call $compile() on strings assembled from request parameters, and calls to $sce.trustAsUrl() or $sce.trustAsResourceUrl() that whitelist an attacker-influenced URL. $sce.trustAsHtml() exists to let a developer explicitly tell Angular "trust this string as safe HTML," and it is safe only when the string genuinely comes from a trusted source — the vulnerability pattern is a developer wrapping API response data in trustAsHtml() to make an "Unsafe: {{ }} interpolation failed" console warning go away, without verifying where that data originated. $compile() misuse follows the same shape as the CSTI issue above but appears in custom directive code rather than templates, which makes it easier to miss in a code review that only checks .html files. trustAsResourceUrl() misuse is less common but higher severity: it's typically found in dynamic <iframe> or script-loading directives, where an attacker-controlled value reaching that call can lead to loading attacker-hosted JavaScript in the application's own origin.
How Safeguard Helps
Safeguard finds AngularJS exposure that keyword-based scanners miss, including vendored copies bundled inside other npm packages or pulled from a CDN URL with no entry in package.json. Our SBOM generation and ingest pipeline fingerprints every AngularJS version across your dependency tree, container images, and build artifacts, then cross-references it against known issues like CVE-2020-7676 and the pre-1.6 sandbox-bypass class described above. Reachability analysis traces whether a vulnerable pattern — an ng-bind-html binding fed by an API response, or a $compile() call built from query parameters — is actually invoked with attacker-influenced data, so your team triages the handful of instances that matter instead of every regex match in the codebase. Griffin AI reviews the surrounding code to confirm exploitability and explains the exact request path an attacker would use to reach it. Where a fix exists, Safeguard opens an auto-fix pull request that upgrades the dependency or replaces the unsafe API call with its $sce-safe equivalent, so the patch ships without a security engineer hand-writing the diff.