Skip to content

Commit 7871093

Browse files
alan-agius4pkozlowski-opensource
authored andcommitted
fix(localize): validate locale in getOutputPathFn to prevent path traversal
The `localize-translate` CLI tool uses the `locale` field from translation files to expand the `{{LOCALE}}` placeholder in the output directory. It failed to sanitize `locale` input, allowing malicious translations to write files outside of the configured output directory. This change mitigates this issue by combining. Closes angular#67906
1 parent 687e75c commit 7871093

File tree

2 files changed

+22
-1
lines changed

2 files changed

+22
-1
lines changed

packages/localize/tools/src/translate/output_path.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,19 @@ export function getOutputPathFn(fs: PathManipulation, outputFolder: AbsoluteFsPa
2626
const [pre, post] = outputFolder.split('{{LOCALE}}');
2727
return post === undefined
2828
? (_locale, relativePath) => fs.join(pre, relativePath)
29-
: (locale, relativePath) => fs.join(pre + locale + post, relativePath);
29+
: (locale, relativePath) => {
30+
if (/[\/\\]|\.\./.test(locale)) {
31+
throw new Error(`Invalid Locale: '${locale}' is not a valid locale.`);
32+
}
33+
34+
const outputPath = fs.join(pre + locale + post, relativePath);
35+
const resolvedOutputPath = fs.resolve(outputPath);
36+
const resolvedPre = fs.resolve(pre);
37+
38+
if (!resolvedOutputPath.startsWith(resolvedPre)) {
39+
throw new Error(`Invalid Locale: '${locale}' would cause path traversal.`);
40+
}
41+
42+
return outputPath;
43+
};
3044
}

packages/localize/tools/test/translate/output_path_spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,12 @@ runInEachFileSystem(() => {
4242
expect(fn('en', 'relative/path')).toEqual(absoluteFrom('/output/en/relative/path'));
4343
expect(fn('fr', 'relative/path')).toEqual(absoluteFrom('/output/fr/relative/path'));
4444
});
45+
46+
it('should throw if the locale contains path traversal characters', () => {
47+
const fn = getOutputPathFn(fs, absoluteFrom('/output/{{LOCALE}}'));
48+
expect(() => fn('../escaped', 'relative/path')).toThrowError(/Invalid Locale/);
49+
expect(() => fn('foo/bar', 'relative/path')).toThrowError(/Invalid Locale/);
50+
expect(() => fn('foo\\bar', 'relative/path')).toThrowError(/Invalid Locale/);
51+
});
4552
});
4653
});

0 commit comments

Comments
 (0)