super-productivity/tools/load-env.js
Johannes Millan c4023b4f45 fix(security): address CodeQL security alerts
- Fix incomplete HTML sanitization in errors.ts (alerts #50-52)
  Apply regex repeatedly to handle nested inputs like <scri<script>pt>
- Add lgtm comment for intentional cert bypass in jira.ts (alert #40)
- Fix incomplete string escaping in load-env.js (alert #39)
  Escape backslashes before quotes
- Fix shell command injection in check-file.js (alerts #37-38)
  Use execFileSync with args array instead of string interpolation
2025-12-23 13:42:57 +01:00

110 lines
3.1 KiB
JavaScript
Executable file

#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const dotenv = require('dotenv');
// Define keys that should always be included in types for backwards compatibility
// These are checked with getEnv() which requires strict typing
const REQUIRED_ENV_KEYS = [
// Currently empty - add keys here that should always be in the type definition
];
// Define optional keys that might be provided
// These can be accessed with getEnvOptional() without strict typing
const OPTIONAL_ENV_KEYS = [
'UNSPLASH_KEY',
'UNSPLASH_CLIENT_ID',
'GOOGLE_DRIVE_TOKEN',
'DROPBOX_API_KEY',
'WEBDAV_URL',
'WEBDAV_USERNAME',
'WEBDAV_PASSWORD',
];
const ALL_KEYS = [...REQUIRED_ENV_KEYS, ...OPTIONAL_ENV_KEYS];
// Start with system environment variables
const env = {};
ALL_KEYS.forEach((key) => {
if (process.env[key]) {
env[key] = process.env[key];
}
});
// Load and override with .env file values if they exist
const envPath = path.join(process.cwd(), '.env');
const envConfig = dotenv.config({ path: envPath });
if (envConfig.parsed) {
// Add all values from .env file, not just the predefined keys
Object.assign(env, envConfig.parsed);
}
// Log what we found
const foundKeys = Object.keys(env);
if (foundKeys.length > 0) {
console.log(`✅ Found ${foundKeys.length} environment variable(s)`);
} else {
console.warn('⚠️ No environment variables found');
}
// Create ENV object with all found keys plus required keys (undefined if not set)
const allEnvKeys = new Set([...REQUIRED_ENV_KEYS, ...Object.keys(env)]);
const envEntries = Array.from(allEnvKeys)
.sort()
.map((key) => {
const value = env[key];
if (value !== undefined) {
// Escape backslashes first, then quotes
const escapedValue = value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
return ` ${key}: '${escapedValue}',`;
} else if (REQUIRED_ENV_KEYS.includes(key)) {
throw new Error(`Required env key ${key} not found`);
}
});
// Generate TypeScript content
const tsContent = `// This file is auto-generated by tools/load-env.js
// Do not modify directly - edit .env file instead
// Generated at: ${new Date().toISOString()}
/**
* Environment variables loaded from .env file
* Access these constants instead of process.env in your Angular app
*/
export const ENV = {
${envEntries.join('\n')}
} as const;
// Type-safe helper to ensure all expected env vars are defined
export type EnvVars = typeof ENV;
`;
// Ensure the config directory exists
const configDir = path.join(process.cwd(), 'src/app/config');
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
// Write the generated file
const outputPath = path.join(configDir, 'env.generated.ts');
fs.writeFileSync(outputPath, tsContent);
console.log(`✅ Generated ${outputPath}`);
// Pass through to the next command
if (process.argv.length > 2) {
// Execute the rest of the command
const { spawn } = require('child_process');
const command = process.argv[2];
const args = process.argv.slice(3);
const child = spawn(command, args, {
stdio: 'inherit',
shell: true,
});
child.on('exit', (code) => {
process.exit(code);
});
}