main: learn to save off the path to each rule

This commit is contained in:
William Ballenthin
2020-06-26 18:09:51 -06:00
parent d32c20d140
commit 5de0884dd2
4 changed files with 42 additions and 28 deletions

View File

@@ -583,18 +583,9 @@ def get_rules(rule_path):
if not os.path.exists(rule_path): if not os.path.exists(rule_path):
raise IOError('%s does not exist or cannot be accessed' % rule_path) raise IOError('%s does not exist or cannot be accessed' % rule_path)
rules = [] rule_paths = []
if os.path.isfile(rule_path): if os.path.isfile(rule_path):
logger.info('reading rule file: %s', rule_path) rule_paths.append(rule_path)
with open(rule_path, 'rb') as f:
rule = capa.rules.Rule.from_yaml(f.read().decode('utf-8'))
if is_nursery_rule_path(rule_path):
rule.meta['nursery'] = True
rules.append(rule)
logger.debug('rule: %s scope: %s', rule.name, rule.scope)
elif os.path.isdir(rule_path): elif os.path.isdir(rule_path):
logger.info('reading rules from directory %s', rule_path) logger.info('reading rules from directory %s', rule_path)
for root, dirs, files in os.walk(rule_path): for root, dirs, files in os.walk(rule_path):
@@ -603,18 +594,24 @@ def get_rules(rule_path):
logger.warning('skipping non-.yml file: %s', file) logger.warning('skipping non-.yml file: %s', file)
continue continue
path = os.path.join(root, file) rule_path = os.path.join(root, file)
logger.debug('reading rule file: %s', path) rule_paths.append(rule_path)
try:
rule = capa.rules.Rule.from_yaml_file(path) rules = []
except capa.rules.InvalidRule: for rule_path in rule_paths:
raise logger.info('reading rule file: %s', rule_path)
else: try:
if is_nursery_rule_path(root): rule = capa.rules.Rule.from_yaml_file(rule_path)
rule.meta['nursery'] = True except capa.rules.InvalidRule:
raise
else:
rule.meta['capa/path'] = rule_path
if is_nursery_rule_path(rule_path):
rule.meta['capa/nursery'] = True
rules.append(rule)
logger.debug('rule: %s scope: %s', rule.name, rule.scope)
rules.append(rule)
logger.debug('rule: %s scope: %s', rule.name, rule.scope)
return rules return rules

View File

@@ -566,8 +566,27 @@ class Rule(object):
continue continue
move_to_end(meta, key) move_to_end(meta, key)
# these are meta fields that are internal to capa,
# and added during rule reading/construction.
# they may help use manipulate or index rules,
# but should not be exposed to clients.
hidden_meta_keys = ("capa/nursery", "capa/path")
hidden_meta = {
key: meta.get(key)
for key in hidden_meta_keys
}
for key in hidden_meta.keys():
del meta[key]
ostream = six.BytesIO() ostream = six.BytesIO()
yaml.dump(definition, ostream) yaml.dump(definition, ostream)
for key, value in hidden_meta.items():
if value is None:
continue
meta[key] = value
return ostream.getvalue().decode('utf-8').rstrip("\n") + "\n" return ostream.getvalue().decode('utf-8').rstrip("\n") + "\n"

View File

@@ -45,7 +45,7 @@ class MissingNamespace(Lint):
def check_rule(self, ctx, rule): def check_rule(self, ctx, rule):
return ('namespace' not in rule.meta and return ('namespace' not in rule.meta and
'nursery' not in rule.meta and not is_nursery_rule(rule) and
'maec/malware-category' not in rule.meta and 'maec/malware-category' not in rule.meta and
'lib' not in rule.meta) 'lib' not in rule.meta)
@@ -250,7 +250,7 @@ def is_nursery_rule(rule):
For example, they may not have references to public example of a technique. For example, they may not have references to public example of a technique.
Yet, we still want to capture and report on their matches. Yet, we still want to capture and report on their matches.
''' '''
return rule.meta.get('nursery') return rule.meta.get('capa/nursery')
def lint_rule(ctx, rule): def lint_rule(ctx, rule):

View File

@@ -49,7 +49,7 @@ def read_rules(rule_directory):
rules[rule.name] = rule rules[rule.name] = rule
if "nursery" in path: if "nursery" in path:
rule.meta["nursery"] = True rule.meta["capa/nursery"] = True
return rules return rules
@@ -132,10 +132,8 @@ def main(argv=None):
filename = filename + ".yml" filename = filename + ".yml"
try: try:
if rule.meta.get("nursery"): if rule.meta.get("capa/nursery"):
directory = os.path.join(args.destination, "nursery") directory = os.path.join(args.destination, "nursery")
# this isn't meant to be written into the rule
del rule.meta["nursery"]
elif rule.meta.get("lib"): elif rule.meta.get("lib"):
directory = os.path.join(args.destination, "lib") directory = os.path.join(args.destination, "lib")
else: else: