Skip to content

Commit

Permalink
include rule caching in PyInstaller build process (#2097)
Browse files Browse the repository at this point in the history
* include rule caching in PyInstaller build process

The following commit introduces a new function that caches the capa
rule set, so that users don't have to manually run ./scripts/cache-
ruleset.py, before running pyinstaller.

* ci: omit Cache rule set step from build.yml workflow

* refactor: move cache generation to cache.py

* mkdir cache directory when it does not exist

---------

Co-authored-by: Soufiane Fariss <soufiane.fariss@um5s.net.ma>
Co-authored-by: Moritz <mr-tz@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 4, 2024
1 parent e517d7d commit 508a09e
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 5 deletions.
15 changes: 12 additions & 3 deletions .github/pyinstaller/pyinstaller.spec
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# -*- mode: python -*-
# Copyright (C) 2020 Mandiant, Inc. All Rights Reserved.
import os.path
import subprocess
import sys

import wcwidth
import capa.rules.cache

from pathlib import Path

# SPECPATH is a global variable which points to .spec file path
capa_dir = Path(SPECPATH).parent.parent
rules_dir = capa_dir / 'rules'
cache_dir = capa_dir / 'cache'

if not capa.rules.cache.generate_rule_cache(rules_dir, cache_dir):
sys.exit(-1)

a = Analysis(
# when invoking pyinstaller from the project root,
Expand All @@ -26,7 +35,7 @@ a = Analysis(
# so we manually embed the wcwidth resources here.
#
# ref: https://stackoverflow.com/a/62278462/87207
(os.path.dirname(wcwidth.__file__), "wcwidth"),
(Path(wcwidth.__file__).parent, "wcwidth"),
],
# when invoking pyinstaller from the project root,
# this gets run from the project root.
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ jobs:
run: python -m pip install --upgrade pip setuptools
- name: Install capa with build requirements
run: pip install -e .[build]
- name: Cache the rule set
run: python ./scripts/cache-ruleset.py ./rules/ ./cache/
- name: Build standalone executable
run: pyinstaller --log-level DEBUG .github/pyinstaller/pyinstaller.spec
- name: Does it run (PE)?
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
- ci: update github workflows to use latest version of actions that were using a deprecated version of node #1967 #2003 capa-rules#883 @sjha2048 @Ana06
- ci: update binja version to stable 4.0 #2016 @xusheng6
- ci: update github workflows to reflect the latest ghidrathon installation and bumped up jep, ghidra versions #2020 @psahithireddy
- ci: include rule caching in PyInstaller build process #2097 @s-ff
- add deptry support #1497 @s-ff

### Raw diffs
Expand Down
22 changes: 22 additions & 0 deletions capa/rules/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,25 @@ def load_cached_ruleset(cache_dir: Path, rule_contents: List[bytes]) -> Optional
return None
else:
return cache.ruleset


def generate_rule_cache(rules_dir: Path, cache_dir: Path) -> bool:
if not rules_dir.is_dir():
logger.error("rules directory %s does not exist", rules_dir)
return False

try:
cache_dir.mkdir(parents=True, exist_ok=True)
rules = capa.rules.get_rules([rules_dir], cache_dir)
except (IOError, capa.rules.InvalidRule, capa.rules.InvalidRuleSet) as e:
logger.error("%s", str(e))
return False

content = capa.rules.cache.get_ruleset_content(rules)
id = capa.rules.cache.compute_cache_identifier(content)
path = capa.rules.cache.get_cache_path(cache_dir, id)

assert path.exists()
logger.info("rules cache saved to: %s", path)

return True

0 comments on commit 508a09e

Please sign in to comment.