in parseRuleData.ts [39:220]
async function getPrebuiltDetectionRules(
ruleSummaries: RuleSummary[],
tagSummaries: Map<string, TagSummary>
) {
let count = 0;
type Technique = {
id: string;
name: string;
reference: string;
subtechnique?: { id: string; reference: string }[];
};
type Tactic = {
id: string;
name: string;
reference: string;
};
type Threat = {
framework: string;
technique?: Technique[];
tactic?: Tactic;
};
const convertHuntMitre = function (mitreData: string[]): Threat[] {
const threat: Threat[] = [];
mitreData.forEach((item) => {
if (item.startsWith('TA')) {
threat.push({
framework: "MITRE ATT&CK",
tactic: {
id: item,
name: "",
reference: `https://attack.mitre.org/tactics/${item}/`,
},
technique: [], // Ensure technique is an empty array if not present
});
} else if (item.startsWith('T')) {
const parts = item.split('.');
const techniqueId = parts[0];
const subtechniqueId = parts[1];
const technique: Technique = {
id: techniqueId,
name: "",
reference: `https://attack.mitre.org/techniques/${techniqueId}/`,
};
if (subtechniqueId) {
technique.subtechnique = [
{
id: `${techniqueId}.${subtechniqueId}`,
reference: `https://attack.mitre.org/techniques/${techniqueId}/${subtechniqueId}/`,
},
];
}
// Find the last added threat with a tactic to add the technique to it
const lastThreat = threat[threat.length - 1];
if (lastThreat && lastThreat.tactic && lastThreat.technique) {
lastThreat.technique.push(technique);
} else {
threat.push({
framework: "MITRE ATT&CK",
tactic: {
id: "",
name: "",
reference: "",
},
technique: [technique],
});
}
}
});
return threat;
};
const addRule = function (buffer) {
const ruleContent = toml.parse(buffer);
// Check if ruleContent.rule and ruleContent.hunt exist
const ruleId = ruleContent.rule?.rule_id || ruleContent.hunt?.uuid;
if (!ruleId) {
throw new Error('Neither rule_id nor hunt.uuid is available');
}
// Initialize ruleContent.rule and ruleContent.metadata if they are undefined
ruleContent.rule = ruleContent.rule || {};
ruleContent.metadata = ruleContent.metadata || {};
// Helper function to set default values if they do not exist
const setDefault = (obj, key, defaultValue) => {
if (!obj[key]) {
obj[key] = defaultValue;
}
};
// Use default tags if ruleContent.rule.tags does not exist
const tags = ruleContent.rule.tags || ["Hunt Type: Hunt"];
setDefault(ruleContent.rule, 'tags', ["Hunt Type: Hunt"]);
// Add a tag based on the language
const language = ruleContent.rule?.language;
if (language) {
tags.push(`Language: ${language}`);
}
// Add creation_date and updated_date if they do not exist
const defaultDate = new Date(0).toISOString();
setDefault(ruleContent.metadata, 'creation_date', defaultDate);
setDefault(ruleContent.metadata, 'updated_date', defaultDate);
// Use current date as default updated_date if it does not exist
const updatedDate = new Date(ruleContent.metadata.updated_date.replace(/\//g, '-'));
// Use hunt.name if rule.name does not exist
const ruleName = ruleContent.rule.name || ruleContent.hunt.name || 'Unknown Rule';
// Set other default values if they do not exist
setDefault(ruleContent.metadata, 'integration', ruleContent.hunt?.integration);
setDefault(ruleContent.rule, 'query', ruleContent.hunt?.query);
setDefault(ruleContent.rule, 'license', "Elastic License v2");
setDefault(ruleContent.rule, 'description', ruleContent.hunt?.description);
// Convert hunt.mitre to rule.threat if hunt.mitre exists
if (ruleContent.hunt?.mitre) {
ruleContent.rule.threat = convertHuntMitre(ruleContent.hunt.mitre);
}
ruleSummaries.push({
id: ruleId,
name: ruleName,
tags: tags,
updated_date: updatedDate,
});
for (const t of tags) {
addTagSummary(t, tagSummaries);
}
fs.writeFileSync(
`${RULES_OUTPUT_PATH}${ruleId}.json`,
JSON.stringify(ruleContent)
);
count++;
};
const githubRulesTarballUrl =
'https://api.github.com/repos/elastic/detection-rules/tarball';
const res = await axios.get(githubRulesTarballUrl, {
responseType: 'stream',
});
const parser = res.data.pipe(new tar.Parse());
parser.on('entry', entry => {
if (
(entry.path.match(/^elastic-detection-rules-.*\/rules\/.*\.toml$/) ||
entry.path.match(/^elastic-detection-rules-.*\/hunting\/.*\.toml$/) ||
entry.path.match(
/^elastic-detection-rules-.*\/rules_building_block\/.*\.toml$/
)) &&
!entry.path.match(/\/_deprecated\//)
) {
const contentStream = new PassThrough();
entry.pipe(contentStream);
let buf = Buffer.alloc(0);
contentStream.on('data', function (d) {
buf = Buffer.concat([buf, d]);
});
contentStream.on('end', () => {
addRule(buf);
});
} else {
entry.resume();
}
});
await new Promise(resolve => parser.on('finish', resolve));
console.log(`loaded ${count} rules from prebuilt repository`);
}