Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions packages/foundations/assets/fonts/generate-eu-fonts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { glob } from 'glob';
import { exec } from 'node:child_process';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

import { dirname } from 'path';
Expand All @@ -8,12 +8,16 @@ import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename).replaceAll('\\', '/');

const execAsync = promisify(exec);
// Security: Using execFile instead of exec to eliminate shell injection risks
// execFile directly executes the binary without involving a shell
const execFileAsync = promisify(execFile);

const generateFonts = async () => {
console.log('Generating EU fonts...');
try {
await execAsync('pyftsubset --help');
// Security: Using array arguments instead of concatenated string
// This prevents shell interpretation of special characters
await execFileAsync('pyftsubset', ['--help']);
} catch (e) {
console.warn(
'You need to install pyftsubset. Check packages/foundations/assets/fonts/README.md for more information.'
Expand All @@ -22,19 +26,28 @@ const generateFonts = async () => {

try {
const files = await glob(`${__dirname}/*.ttf`);
const commands = files.map((file) =>
[
'pyftsubset',

for (const file of files) {
// Security: Validate that the file is within the expected directory
// and has the expected extension to prevent path traversal attacks
if (!file.startsWith(__dirname) || !file.endsWith('.ttf')) {
console.warn(`Skipping potentially unsafe file path: ${file}`);
continue;
}

// Security: Arguments are passed as separate array elements
// No shell concatenation means no risk of command injection
const args = [
file,
'--layout-features=*',
'--flavor=woff2',
`--unicodes-file=${__dirname}/unicode-eu.txt`,
`--output-file=${file.replace('.ttf', '-EU.woff2')}`
].join(' ')
);
];

for (const command of commands) {
const { stdout, stderr } = await execAsync(command);
// Security: execFile provides better performance and type safety
// as it doesn't spawn a shell process
const { stdout, stderr } = await execFileAsync('pyftsubset', args);
if (stdout) console.log(`stdout: ${stdout}`);
if (stderr) console.error(`stderr: ${stderr}`);
}
Expand Down
Loading