mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-24 20:53:06 +00:00
147 lines
5.0 KiB
JavaScript
147 lines
5.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Combines individual rule JSON files into dist/ array files and generates
|
|
* TypeScript guardrails from rules/guardrails.json.
|
|
*
|
|
* Run: node rules/scripts/combine.js
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const rulesRoot = path.resolve(__dirname, '..');
|
|
|
|
// ── Rule combination ────────────────────────────────────────────────────────
|
|
|
|
const configs = [
|
|
{
|
|
dir: path.join(rulesRoot, 'gather'),
|
|
output: path.join(rulesRoot, 'dist', 'gather-rules.json'),
|
|
schema: '../schema/gather-rule.schema.json',
|
|
idField: 'ruleId'
|
|
},
|
|
{
|
|
dir: path.join(rulesRoot, 'analyze'),
|
|
output: path.join(rulesRoot, 'dist', 'analyze-rules.json'),
|
|
schema: '../schema/analyze-rule.schema.json',
|
|
idField: 'ruleId'
|
|
},
|
|
{
|
|
dir: path.join(rulesRoot, 'ime-log-patterns'),
|
|
output: path.join(rulesRoot, 'dist', 'ime-log-patterns.json'),
|
|
schema: '../schema/ime-log-pattern.schema.json',
|
|
idField: 'patternId'
|
|
}
|
|
];
|
|
|
|
for (const config of configs) {
|
|
const files = fs.readdirSync(config.dir).filter(f => f.endsWith('.json')).sort();
|
|
const rules = [];
|
|
|
|
for (const file of files) {
|
|
const content = fs.readFileSync(path.join(config.dir, file), 'utf8');
|
|
const rule = JSON.parse(content);
|
|
// Remove $schema from individual entries (it's on the wrapper)
|
|
delete rule['$schema'];
|
|
rules.push(rule);
|
|
}
|
|
|
|
// Sort by ID for deterministic output
|
|
rules.sort((a, b) => (a[config.idField] || '').localeCompare(b[config.idField] || ''));
|
|
|
|
const wrapper = {
|
|
$schema: config.schema,
|
|
rules: rules
|
|
};
|
|
|
|
fs.mkdirSync(path.dirname(config.output), { recursive: true });
|
|
fs.writeFileSync(config.output, JSON.stringify(wrapper, null, 2) + '\n', 'utf8');
|
|
|
|
console.log(`${path.basename(config.output)}: ${rules.length} rules combined`);
|
|
}
|
|
|
|
// ── Guardrails generation ───────────────────────────────────────────────────
|
|
|
|
const guardrailsPath = path.join(rulesRoot, 'guardrails.json');
|
|
const guardrailsOutput = path.resolve(
|
|
rulesRoot, '..', 'src', 'Web', 'autopilot-monitor-web', 'lib', 'guardrails.generated.ts'
|
|
);
|
|
|
|
if (fs.existsSync(guardrailsPath)) {
|
|
const guardrails = JSON.parse(fs.readFileSync(guardrailsPath, 'utf8'));
|
|
|
|
/** Escape a string for use inside a TS string literal (double-quoted). */
|
|
const esc = (s) => s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
|
|
/** Render an array of strings as a TS readonly array. */
|
|
const flatArray = (items) =>
|
|
items.map((s) => ` "${esc(s)}",`).join('\n');
|
|
|
|
/** Render a categorized list as a TS readonly array of { category, items }. */
|
|
const categorizedArray = (groups, itemsKey) =>
|
|
groups.map((g) => {
|
|
const items = g[itemsKey].map((s) => ` "${esc(s)}",`).join('\n');
|
|
return ` {\n category: "${esc(g.category)}",\n items: [\n${items}\n ],\n },`;
|
|
}).join('\n');
|
|
|
|
// Flatten categorized lists for validation
|
|
const flatRegistryPrefixes = guardrails.registryPrefixes.flatMap((g) => g.prefixes);
|
|
const flatCommands = guardrails.allowedCommands.flatMap((g) => g.commands);
|
|
|
|
const ts = `/**
|
|
* AUTO-GENERATED from rules/guardrails.json — DO NOT EDIT.
|
|
* Run: node rules/scripts/combine.js
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Categorized data (for documentation / UI display)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface GuardrailCategory {
|
|
readonly category: string;
|
|
readonly items: readonly string[];
|
|
}
|
|
|
|
export const REGISTRY_PREFIX_CATEGORIES: readonly GuardrailCategory[] = [
|
|
${categorizedArray(guardrails.registryPrefixes, 'prefixes')}
|
|
];
|
|
|
|
export const COMMAND_CATEGORIES: readonly GuardrailCategory[] = [
|
|
${categorizedArray(guardrails.allowedCommands, 'commands')}
|
|
];
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Flat arrays (for validation logic)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const ALLOWED_REGISTRY_PREFIXES: readonly string[] = [
|
|
${flatArray(flatRegistryPrefixes)}
|
|
];
|
|
|
|
export const ALLOWED_FILE_PREFIXES: readonly string[] = [
|
|
${flatArray(guardrails.filePrefixes)}
|
|
];
|
|
|
|
export const ALLOWED_WMI_QUERY_PREFIXES: readonly string[] = [
|
|
${flatArray(guardrails.wmiQueryPrefixes)}
|
|
];
|
|
|
|
export const ALLOWED_COMMANDS_LIST: readonly string[] = [
|
|
${flatArray(flatCommands)}
|
|
];
|
|
|
|
export const ALLOWED_DIAGNOSTICS_PATH_PREFIXES: readonly string[] = [
|
|
${flatArray(guardrails.diagnosticsPathPrefixes)}
|
|
];
|
|
|
|
export const BLOCKED_FILE_PREFIXES: readonly string[] = [
|
|
${flatArray(guardrails.blockedFilePrefixes)}
|
|
];
|
|
`;
|
|
|
|
fs.writeFileSync(guardrailsOutput, ts, 'utf8');
|
|
console.log(`guardrails.generated.ts: ${flatRegistryPrefixes.length} registry, ${flatCommands.length} commands, ${guardrails.filePrefixes.length} file prefixes`);
|
|
} else {
|
|
console.warn('guardrails.json not found — skipping guardrails generation');
|
|
}
|