From 26fef7c6157c6588e6891c05a566c1db30160949 Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Fri, 26 Jun 2020 18:44:19 -0600 Subject: [PATCH] *: formatting --- capa/rules.py | 32 +++++++++--- scripts/lint.py | 18 +++---- scripts/migrate-rules.py | 104 +++++++++++++++++++-------------------- 3 files changed, 85 insertions(+), 69 deletions(-) diff --git a/capa/rules.py b/capa/rules.py index 15b2d7a5..160eba90 100644 --- a/capa/rules.py +++ b/capa/rules.py @@ -21,12 +21,28 @@ logger = logging.getLogger(__name__) # these are the standard metadata fields, in the preferred order. # when reformatted, any custom keys will come after these. -META_KEYS = ("name", "namespace", "rule-category", "maec/malware-category", "author", "description", "lib", "scope", "att&ck", "mbc", "references", "examples") +META_KEYS = ( + 'name', + 'namespace', + 'rule-category', + 'maec/analysis-conclusion', + 'maec/analysis-conclusion-ov', + 'maec/malware-category', + 'maec/malware-category-ov', + 'author', + 'description', + 'lib', + 'scope', + 'att&ck', + 'mbc', + 'references', + 'examples' +) # 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_KEYS = ('capa/nursery', 'capa/path') FILE_SCOPE = 'file' @@ -545,11 +561,11 @@ class Rule(object): definition = yaml.load(self.definition) # definition retains a reference to `meta`, # so we're updating that in place. - definition["rule"]["meta"] = self.meta + definition['rule']['meta'] = self.meta meta = self.meta - meta["name"] = self.name - meta["scope"] = self.scope + meta['name'] = self.name + meta['scope'] = self.scope def move_to_end(m, k): # ruamel.yaml uses an ordereddict-like structure to track maps (CommentedMap). @@ -559,8 +575,8 @@ class Rule(object): del m[k] m[k] = v - move_to_end(definition["rule"], "meta") - move_to_end(definition["rule"], "features") + move_to_end(definition['rule'], 'meta') + move_to_end(definition['rule'], 'features') for key in META_KEYS: if key in meta: @@ -590,7 +606,7 @@ class Rule(object): continue meta[key] = value - return ostream.getvalue().decode('utf-8').rstrip("\n") + "\n" + return ostream.getvalue().decode('utf-8').rstrip('\n') + '\n' def get_rules_with_scope(rules, scope): diff --git a/scripts/lint.py b/scripts/lint.py index 5c1701ce..def5aa0e 100644 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -47,12 +47,12 @@ class FilenameDoesntMatchRuleName(Lint): def check_rule(self, ctx, rule): expected = rule.name expected = expected.lower() - expected = expected.replace(" ", "-") - expected = expected.replace("(", "") - expected = expected.replace(")", "") - expected = expected.replace("+", "") - expected = expected.replace("/", "") - expected = expected + ".yml" + expected = expected.replace(' ', '-') + expected = expected.replace('(', '') + expected = expected.replace(')', '') + expected = expected.replace('+', '') + expected = expected.replace('/', '') + expected = expected + '.yml' found = os.path.basename(rule.meta['capa/path']) @@ -85,7 +85,7 @@ class NamespaceDoesntMatchRulePath(Lint): if 'lib' in rule.meta: return False - return rule.meta["namespace"] not in rule.meta['capa/path'].replace('\\', '/') + return rule.meta['namespace'] not in rule.meta['capa/path'].replace('\\', '/') class MissingScope(Lint): @@ -185,7 +185,7 @@ class DoesntMatchExample(Lint): class UnusualMetaField(Lint): name = 'unusual meta field' - recommendation = 'Remove the unusual meta field' + recommendation = 'Remove the meta field: "{:s}"' def check_rule(self, ctx, rule): for key in rule.meta.keys(): @@ -193,7 +193,7 @@ class UnusualMetaField(Lint): continue if key in capa.rules.HIDDEN_META_KEYS: continue - logger.debug("unusual meta field: %s", key) + self.recommendation = self.recommendation.format(key) return True return False diff --git a/scripts/migrate-rules.py b/scripts/migrate-rules.py index c8de7d2c..1697c41a 100644 --- a/scripts/migrate-rules.py +++ b/scripts/migrate-rules.py @@ -24,15 +24,15 @@ logger = logging.getLogger('migrate-rules') def read_plan(plan_path): with open(plan_path, 'rb') as f: - return list(csv.DictReader(f, restkey="other", fieldnames=( - "existing path", - "existing name", - "existing rule-category", - "proposed name", - "proposed namespace", - "ATT&CK", - "MBC", - "comment1", + return list(csv.DictReader(f, restkey='other', fieldnames=( + 'existing path', + 'existing name', + 'existing rule-category', + 'proposed name', + 'proposed namespace', + 'ATT&CK', + 'MBC', + 'comment1', ))) @@ -48,8 +48,8 @@ def read_rules(rule_directory): rule = capa.rules.Rule.from_yaml_file(path) rules[rule.name] = rule - if "nursery" in path: - rule.meta["capa/nursery"] = True + if 'nursery' in path: + rule.meta['capa/nursery'] = True return rules @@ -70,89 +70,89 @@ def main(argv=None): logging.getLogger().setLevel(logging.INFO) plan = read_plan(args.plan) - logger.info("read %d plan entries", len(plan)) + logger.info('read %d plan entries', len(plan)) rules = read_rules(args.source) - logger.info("read %d rules", len(rules)) + logger.info('read %d rules', len(rules)) - planned_rules = set([row["existing name"] for row in plan]) + planned_rules = set([row['existing name'] for row in plan]) unplanned_rules = [rule for (name, rule) in rules.items() if name not in planned_rules] if unplanned_rules: - logger.error("plan does not account for %d rules:" % (len(unplanned_rules))) + logger.error('plan does not account for %d rules:' % (len(unplanned_rules))) for rule in unplanned_rules: - logger.error(" " + rule.name) + logger.error(' ' + rule.name) return -1 # pairs of strings (needle, replacement) match_translations = [] for row in plan: - if not row["existing name"]: + if not row['existing name']: continue - rule = rules[row["existing name"]] + rule = rules[row['existing name']] - if rule.meta["name"] != row["proposed name"]: - logger.info("renaming rule '%s' -> '%s'", rule.meta["name"], row["proposed name"]) + if rule.meta['name'] != row['proposed name']: + logger.info("renaming rule '%s' -> '%s'", rule.meta['name'], row['proposed name']) # assume the yaml is formatted like `- match: $rule-name`. # but since its been linted, this should be ok. match_translations.append( - ("- match: " + rule.meta["name"], - "- match: " + row["proposed name"])) + ('- match: ' + rule.meta['name'], + '- match: ' + row['proposed name'])) - rule.meta["name"] = row["proposed name"] - rule.name = row["proposed name"] + rule.meta['name'] = row['proposed name'] + rule.name = row['proposed name'] - if "rule-category" in rule.meta: - logger.info("deleting rule category '%s'", rule.meta["rule-category"]) - del rule.meta["rule-category"] + if 'rule-category' in rule.meta: + logger.info("deleting rule category '%s'", rule.meta['rule-category']) + del rule.meta['rule-category'] - rule.meta["namespace"] = row["proposed namespace"] + rule.meta['namespace'] = row['proposed namespace'] - if row["ATT&CK"] != 'n/a' and row["ATT&CK"] != "": - tag = row["ATT&CK"] - name, _, id = tag.rpartition(" ") - tag = "%s [%s]" % (name, id) - rule.meta["att&ck"] = [tag] + if row['ATT&CK'] != 'n/a' and row['ATT&CK'] != '': + tag = row['ATT&CK'] + name, _, id = tag.rpartition(' ') + tag = '%s [%s]' % (name, id) + rule.meta['att&ck'] = [tag] - if row["MBC"] != 'n/a' and row["MBC"] != "": - tag = row["MBC"] - rule.meta["mbc"] = [tag] + if row['MBC'] != 'n/a' and row['MBC'] != '': + tag = row['MBC'] + rule.meta['mbc'] = [tag] for rule in rules.values(): filename = rule.name filename = filename.lower() - filename = filename.replace(" ", "-") - filename = filename.replace("(", "") - filename = filename.replace(")", "") - filename = filename.replace("+", "") - filename = filename.replace("/", "") - filename = filename + ".yml" + filename = filename.replace(' ', '-') + filename = filename.replace('(', '') + filename = filename.replace(')', '') + filename = filename.replace('+', '') + filename = filename.replace('/', '') + filename = filename + '.yml' try: - if rule.meta.get("capa/nursery"): - directory = os.path.join(args.destination, "nursery") - elif rule.meta.get("lib"): - directory = os.path.join(args.destination, "lib") + if rule.meta.get('capa/nursery'): + directory = os.path.join(args.destination, 'nursery') + elif rule.meta.get('lib'): + directory = os.path.join(args.destination, 'lib') else: - directory = os.path.join(args.destination, rule.meta.get("namespace")) + directory = os.path.join(args.destination, rule.meta.get('namespace')) os.makedirs(directory) except OSError: pass else: - logger.info("created namespace: %s", directory) + logger.info('created namespace: %s', directory) path = os.path.join(directory, filename) - logger.info("writing rule %s", path) + logger.info('writing rule %s', path) - doc = rule.to_yaml().decode("utf-8") + doc = rule.to_yaml().decode('utf-8') for (needle, replacement) in match_translations: doc = doc.replace(needle, replacement) - with open(path, "wb") as f: - f.write(doc.encode("utf-8")) + with open(path, 'wb') as f: + f.write(doc.encode('utf-8')) return 0