Summary
Vendor: LangChain / Microsoft Playwright
Vendor URLs: LangChain JS | Playwright
Versions Affected:
- LangChain JS 0.2.2
- LangChain Community 0.2.2
- Playwright (all versions — by design)
Advisory URL: https://huntr.com/bounties/23f45984-7336-48d8-a373-75b39bcd6367
CVE Identifier: N/A (LangChain) · Related: CVE-2026-44439 (PlaywrightCapture), CVE-2025-9611 (Playwright MCP)
Risk: High (vendor classified as Informative)
LangChain is an open-source framework designed to assist the development of applications powered by large language models (LLMs). The JavaScript library has more than 11,000 stars and 380,000+ weekly downloads.
I discovered an Arbitrary File Read (AFR) vulnerability in LangChain JS's PlaywrightWebBaseLoader component. This vulnerability allows an attacker to read arbitrary files on the server using file:// protocol URLs. The root cause is Playwright's design philosophy: Playwright does not act as a security boundary and allows all protocols including file:// by default.
The Playwright Problem
This vulnerability is not merely a LangChain implementation flaw — it stems from Playwright's architectural decisions.
Playwright's "Not a Security Boundary" Philosophy
The Microsoft/Playwright team explicitly states that users fully control where requests are sent when using Playwright. Playwright is designed as a browser automation tool, not a security filter. Consequently:
- Playwright does not block
file://,ftp://, or other protocols by default - URL validation responsibility is entirely delegated to the application developer
- Wrapper libraries like
PlaywrightWebBaseLoaderinherit this "unsafe by default" behavior
Pattern of Similar Vulnerabilities
This issue continuously reappears across the Playwright ecosystem:
- CVE-2026-44439 (PlaywrightCapture): The same
file://exploitation allowed arbitrary file reads viawindow.location.hrefmanipulation. Fixed by adding anonly_global_lookupparameter to restrict local file access. - GHSA-665w-mwrr-77q3 (url-to-png): A Playwright-based screenshot service was exploited via a
file://wrapper to read/etc/passwd. Mitigation: restrict to http/https only. - CVE-2025-9611 (Playwright MCP): Microsoft later acknowledged the risk in the Playwright MCP Server, adding a
PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESSenvironment variable to control file access.
The Trust Boundary Violation
APPLICATION (Express.js)
|
LangChain PlaywrightWebBaseLoader
(NO URL VALIDATION)
|
Playwright Browser
(ALLOWS file:// BY DESIGN)
|
OS File System (/etc/passwd)
Utilizing AFR using SSRF
The vulnerability is exploited by manipulating the URL to access local files. Playwright processes file:// URIs exactly like HTTP URLs, rendering them in the browser context.
Example
Imagine a web application that provides a URL preview feature using PlaywrightWebBaseLoader. Without proper validation, an attacker provides a file URL.
Vulnerable Code Snippet
import express from 'express';
import { PlaywrightWebBaseLoader } from "@langchain/community/document_loaders/web/playwright";
const app = express();
const PORT = 9000;
app.get('/', async (req, res) => {
const url = req.query.url;
if (!url) {
return res.status(400).send('URL query parameter is required');
}
try {
const loader = new PlaywrightWebBaseLoader(url);
const docs = await loader.load(); // Executes Playwright with user-controlled URL
console.log(docs);
res.send(docs);
} catch (error) {
console.error(error);
res.status(500).send('An error occurred');
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Exploitation
Request:
GET /?url=file:%2f%2f%2fetc%2fpasswd HTTP/1.1
Host: localhost:9000
The response contains the full contents of /etc/passwd rendered by Playwright as HTML, proving that Playwright processed the file:// URL without restriction.
You can access the PoC code at: Langchain AFR PoC
Mitigation
Since Playwright does not provide security boundaries, applications MUST implement their own.
1. Protocol Validation
function isValidUrl(string) {
try {
const parsedUrl = new URL(string);
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
throw new Error('Unsupported protocol');
}
return true;
} catch (error) {
return false;
}
}
2. Playwright Route Interception (Defense in Depth)
const { chromium } = require('playwright');
const browser = await chromium.launch();
const page = await browser.newPage();
// Block file:// requests at Playwright level
await page.route('**/*', route => {
const url = route.request().url();
if (url.startsWith('file://') || url.startsWith('ftp://')) {
route.abort();
} else {
route.continue();
}
});
3. Recommended Security Measures
- Input Validation: Strictly validate and sanitize URLs before passing to Playwright
- Allowed Domains List: Restrict fetching to trusted domains only
- Deny file:// Schema: Explicitly block file://, ftp://, and other sensitive protocols
- Network Segmentation: Run Playwright in isolated environments without access to sensitive files
- Use Alternatives: Consider Puppeteer with similar restrictions or pure HTTP clients
Disclosure
LangChain Response: Report marked as "Informative" with the statement that Playwright is responsible for URL handling.
Microsoft/Playwright Response: "The user fully controls where the requests are sent when using Playwright." Playwright is not classified as a security boundary.
My Position: While Playwright's design places validation responsibility on developers, LangChain's PlaywrightWebBaseLoader exposes this unsafe default to end users without documentation or warnings. It is worth noting that Microsoft has since addressed similar patterns in other Playwright-based products — such as introducing PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS in Playwright MCP Server (CVE-2025-9611) and implementing request routing restrictions in PlaywrightCapture (CVE-2026-44439) — suggesting the security community and the ecosystem increasingly recognize the importance of built-in safeguards for this attack vector.
The LangChain documentation contains no security warnings regarding URL input validation when receiving user-controlled URLs.