diff --git a/web/rules/scripts/build_root.py b/web/rules/scripts/build_root.py
index 0b891c3a..67f1f6f3 100644
--- a/web/rules/scripts/build_root.py
+++ b/web/rules/scripts/build_root.py
@@ -1,343 +1,343 @@
-"""
-Copyright (C) 2024 Mandiant, Inc. All Rights Reserved.
-Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-You may obtain a copy of the License at: [package root]/LICENSE.txt
-Unless required by applicable law or agreed to in writing, software distributed under the License
- is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and limitations under the License.
-"""
-
-import sys
-import random
-import logging
-from typing import Dict, List
-from pathlib import Path
-
-import capa.rules
-
-logger = logging.getLogger(__name__)
-
-start_dir = Path(sys.argv[1])
-txt_file_path = Path(sys.argv[2])
-out_dir = Path(sys.argv[3])
-output_html_path = out_dir / "index.html"
-
-assert start_dir.exists(), "input directory must exist"
-assert txt_file_path.exists(), "file-modification txt file must exist"
-assert out_dir.exists(), "output directory must exist"
-
-predefined_colors = [
- "#9CAFAA",
- "#577590",
- "#a98467",
- "#D6DAC8",
- "#adc178",
- "#f4d35e",
- "#85182a",
- "#d6c399",
- "#dde5b6",
- "#8da9c4",
- "#fcd5ce",
- "#706993",
- "#FBF3D5",
- "#1a659e",
- "#c71f37",
- "#EFBC9B",
- "#7e7f9a",
-]
-
-
-def read_file_paths(txt_file_path: Path):
- categorized_files: Dict[str, List[Path]] = {
- "modified in the last day": [],
- "modified in the last week": [],
- "modified in the last month": [],
- "modified in the last three months": [],
- "modified in the last year": [],
- "older": [],
- }
-
- lines = txt_file_path.read_text(encoding="utf-8").splitlines()
-
- current_category = None
- for line in lines:
- line = line.strip()
- if not line:
- continue
- if "===" in line:
- category = line.strip("=").strip()
- if category in categorized_files:
- current_category = category
- else:
- logger.warning("Unrecognized category '%s'", category)
- current_category = None
- elif current_category:
- parts = line.split(" ", 1)
- if len(parts) == 2:
- file_path, last_modified_date_str = parts
- categorized_files[current_category].append(Path(file_path))
- else:
- logger.warning("Skipping line due to unexpected format: %s", line)
-
- return categorized_files
-
-
-def parse_rule(file_path: Path):
- rule = capa.rules.Rule.from_yaml_file(file_path)
-
- return {
- "name": rule.name,
- "namespace": rule.meta.get("namespace", ""),
- "authors": rule.meta.get("authors", []),
- "path": file_path,
- "filename": file_path.name,
- }
-
-
-def generate_color():
- return "#{:06x}".format(random.randint(0, 0xFFFFFF))
-
-
-def get_first_word(namespace):
- return namespace.split("/")[0] if "/" in namespace else namespace
-
-
-def generate_html(categories_data, color_map):
- html_content = """
-
-
+
+
+ """
+
+ output_dir.mkdir(parents=True, exist_ok=True)
+ output_file_path = output_dir / (filename + ".html")
+ output_file_path.write_text(html_content, encoding="utf-8")
+
+
+yaml_files = glob(os.path.join(input_directory, "**/*.yml"), recursive=True)
+
+timestamps = {}
+for line in txt_file_path.read_text(encoding="utf-8").splitlines():
+ if not line:
continue
-
- path, _, timestamp = line.partition(" ")
- timestamps[path] = timestamp
-
-for yaml_file in yaml_files:
- convert_yaml_to_html(timestamps, Path(yaml_file), output_directory)
+ if line.startswith("==="):
+ continue
+
+ path, _, timestamp = line.partition(" ")
+ timestamps[path] = timestamp
+
+for yaml_file in yaml_files:
+ convert_yaml_to_html(timestamps, Path(yaml_file), output_directory)
diff --git a/web/rules/scripts/modified-dates.py b/web/rules/scripts/modified-dates.py
index 38ca38de..66a2af9d 100644
--- a/web/rules/scripts/modified-dates.py
+++ b/web/rules/scripts/modified-dates.py
@@ -1,92 +1,92 @@
-"""
-Copyright (C) 2024 Mandiant, Inc. All Rights Reserved.
-Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
-You may obtain a copy of the License at: [package root]/LICENSE.txt
-Unless required by applicable law or agreed to in writing, software distributed under the License
- is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and limitations under the License.
-"""
-
-import os
-import sys
-import logging
-import subprocess
-from pathlib import Path
-from datetime import datetime, timedelta
-
-logger = logging.getLogger(__name__)
-
-start_dir = Path(sys.argv[1])
-output_file = Path(sys.argv[2])
-
-assert start_dir.exists(), "start directory must exist"
-
-
-def get_yml_files_and_dates(start_dir: Path):
- yml_files = []
- for root, _, files in os.walk(start_dir):
- for file in files:
- if file.endswith(".yml") or file.endswith(".yaml"):
- file_path = Path(root) / file
-
- proc = subprocess.run(
- [
- "git",
- "log",
- "-1", # only show most recent commit
- '--pretty="%ct"', # unix timestmp, https://git-scm.com/docs/pretty-formats#Documentation/pretty-formats.txt-emctem
- file, # just the filename, will run from the containing directory
- ],
- cwd=root, # the directory with the file we're inspecting
- check=True,
- capture_output=True,
- )
-
- last_modified_date = int(proc.stdout.decode("utf-8").partition("\n")[0].strip('"'))
-
- yml_files.append((file_path, last_modified_date))
- return yml_files
-
-
-yml_files_and_dates = get_yml_files_and_dates(start_dir)
-
-yml_files_and_dates.sort(key=lambda x: x[1], reverse=True)
-
-
-current_date = datetime.now()
-
-categories = [
- ("modified in the last day", current_date - timedelta(days=1)),
- ("modified in the last week", current_date - timedelta(days=7)),
- ("modified in the last month", current_date - timedelta(days=30)),
- ("modified in the last three months", current_date - timedelta(days=90)),
- ("modified in the last year", current_date - timedelta(days=365)),
-]
-
-
-def write_category(f, category_name, files):
- f.write(f"=== {category_name} ===\n")
- for file_path, last_modified_date in files:
- last_modified_date_str = datetime.fromtimestamp(last_modified_date).strftime("%Y-%m-%d %H:%M:%S")
- f.write(f"{file_path} {last_modified_date_str}\n")
- f.write("\n")
-
-
-with output_file.open("wt", encoding="utf-8") as f:
- for title, delta in categories:
- current_files = []
- for file_path, last_modified_date in yml_files_and_dates:
- last_modified_date_dt = datetime.fromtimestamp(last_modified_date)
- if last_modified_date_dt > delta:
- current_files.append((file_path, last_modified_date))
-
- write_category(f, title, current_files)
-
- for item in current_files:
- yml_files_and_dates.remove(item)
-
- write_category(f, "older", yml_files_and_dates)
-
-
-logger.info("File names and modification dates have been written to %s", output_file)
+"""
+Copyright (C) 2024 Mandiant, Inc. All Rights Reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+You may obtain a copy of the License at: [package root]/LICENSE.txt
+Unless required by applicable law or agreed to in writing, software distributed under the License
+ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and limitations under the License.
+"""
+
+import os
+import sys
+import logging
+import subprocess
+from pathlib import Path
+from datetime import datetime, timedelta
+
+logger = logging.getLogger(__name__)
+
+start_dir = Path(sys.argv[1])
+output_file = Path(sys.argv[2])
+
+assert start_dir.exists(), "start directory must exist"
+
+
+def get_yml_files_and_dates(start_dir: Path):
+ yml_files = []
+ for root, _, files in os.walk(start_dir):
+ for file in files:
+ if file.endswith(".yml") or file.endswith(".yaml"):
+ file_path = Path(root) / file
+
+ proc = subprocess.run(
+ [
+ "git",
+ "log",
+ "-1", # only show most recent commit
+ '--pretty="%ct"', # unix timestmp, https://git-scm.com/docs/pretty-formats#Documentation/pretty-formats.txt-emctem
+ file, # just the filename, will run from the containing directory
+ ],
+ cwd=root, # the directory with the file we're inspecting
+ check=True,
+ capture_output=True,
+ )
+
+ last_modified_date = int(proc.stdout.decode("utf-8").partition("\n")[0].strip('"'))
+
+ yml_files.append((file_path, last_modified_date))
+ return yml_files
+
+
+yml_files_and_dates = get_yml_files_and_dates(start_dir)
+
+yml_files_and_dates.sort(key=lambda x: x[1], reverse=True)
+
+
+current_date = datetime.now()
+
+categories = [
+ ("modified in the last day", current_date - timedelta(days=1)),
+ ("modified in the last week", current_date - timedelta(days=7)),
+ ("modified in the last month", current_date - timedelta(days=30)),
+ ("modified in the last three months", current_date - timedelta(days=90)),
+ ("modified in the last year", current_date - timedelta(days=365)),
+]
+
+
+def write_category(f, category_name, files):
+ f.write(f"=== {category_name} ===\n")
+ for file_path, last_modified_date in files:
+ last_modified_date_str = datetime.fromtimestamp(last_modified_date).strftime("%Y-%m-%d %H:%M:%S")
+ f.write(f"{file_path} {last_modified_date_str}\n")
+ f.write("\n")
+
+
+with output_file.open("wt", encoding="utf-8") as f:
+ for title, delta in categories:
+ current_files = []
+ for file_path, last_modified_date in yml_files_and_dates:
+ last_modified_date_dt = datetime.fromtimestamp(last_modified_date)
+ if last_modified_date_dt > delta:
+ current_files.append((file_path, last_modified_date))
+
+ write_category(f, title, current_files)
+
+ for item in current_files:
+ yml_files_and_dates.remove(item)
+
+ write_category(f, "older", yml_files_and_dates)
+
+
+logger.info("File names and modification dates have been written to %s", output_file)