Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.ruffPath": "/usr/local/py-utils/bin/ruff",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
Expand Down
7 changes: 3 additions & 4 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,9 @@ sign a new one.
All Python code must adhere to the style guide used by capa:

1. [PEP8](https://www.python.org/dev/peps/pep-0008/), with clarifications from
2. [Willi's style guide](https://docs.google.com/document/d/1iRpeg-w4DtibwytUyC_dDT7IGhNGBP25-nQfuBa-Fyk/edit?usp=sharing), formatted with
3. [isort](https://pypi.org/project/isort/) (with line width 120 and ordered by line length), and formatted with
4. [black](https://github.com/psf/black) (with line width 120), and formatted with
5. [dos2unix](https://linux.die.net/man/1/dos2unix)
2. [Willi's style guide](https://docs.google.com/document/d/1iRpeg-w4DtibwytUyC_dDT7IGhNGBP25-nQfuBa-Fyk/edit?usp=sharing), and checked/formatted with
3. [ruff](https://docs.astral.sh/ruff/) (with line length 120), and
4. [dos2unix](https://linux.die.net/man/1/dos2unix)

Our CI pipeline will reformat and enforce the Python styleguide.

Expand Down
41 changes: 0 additions & 41 deletions .github/flake8.ini

This file was deleted.

85 changes: 71 additions & 14 deletions .github/ruff.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
# Enable the pycodestyle (`E`) and Pyflakes (`F`) rules by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
lint.select = ["E", "F"]

# Allow autofix for all enabled rules (when `--fix`) is provided.
lint.fixable = ["ALL"]
lint.unfixable = []

# E402 module level import not at top of file
# E722 do not use bare 'except'
# E501 line too long
lint.ignore = ["E402", "E722", "E501"]

line-length = 120
preview = true # Required to enable pre-release copyright header checks (CPY001)

exclude = [
# Exclude a variety of commonly ignored directories.
Expand Down Expand Up @@ -41,3 +28,73 @@ exclude = [
"*_pb2.py",
"*_pb2.pyi"
]

lint.select = [
"E", # pycodestyle (base style rules)
"F", # Pyflakes (logical/syntax errors)
"I", # isort (import sorting)
"B", # flake8-bugbear (common bugs/design problems)
"C4", # flake8-comprehensions (simplify list/dict comprehensions)
"ISC", # flake8-implicit-str-concat (detect accidental multi-line string issues)
"T20", # flake8-print (prevent leftover print/pprint statements)
"SIM", # flake8-simplify (code simplification upgrades)
"CPY", # flake8-copyright (header requirement enforcement)
"G", # flake8-logging-format (logging statement validation)
"TD", # flake8-todos (TODO formatting requirements)
"PTH", # flake8-use-pathlib (migration from os.path to Pathlib)
"UP" # pyupgrade (modern Python syntax upgrades)
]

# Allow autofix for all enabled rules (when `--fix`) is provided.
lint.fixable = ["ALL"]
lint.unfixable = []

# Map existing flake8 ignores to maintain strict parity
lint.ignore = [
# Legacy flake8 ignores
"E402", # Module level import not at top of file
"E722", # Do not use bare except
"E501", # Line too long
"E203", # Whitespace before ':'
"E701", # Multiple statements on one line
"B010", # Do not call setattr with a constant attribute value
"SIM102", # Use a single if statement instead of nested if statements
"SIM114", # Combine if branches using logical or operator

# Newly surfaced Ruff strictness ignores
"B905", # zip() without an explicit strict= parameter
"UP032", # Use f-string instead of format call
"UP031", # Use format specifiers instead of percent format
"SIM300", # Yoda condition detected (constant before variable)
"SIM108", # Use ternary operator instead of if-else block
"ISC003", # Explicitly concatenated string should be implicitly concatenated
"UP035", # Deprecated typing alias usage
"UP006", # Use type instead of Type for type annotation
"SIM115", # Use a context manager for opening files
"SIM118", # Use key not in dict instead of key not in dict.keys()
"UP024", # Replace aliased errors with OSError
"UP045", # Use X | None for optional type annotations
"SIM103", # Return negated condition directly
"UP007", # Use X | Y for union type annotations
"B904", # Raise exceptions within except clause using raise from
"UP028", # Replace yield over for loop with yield from
"C409", # Unnecessary list comprehension passed to tuple()
# TODO(mike-hunhoff): address circular dependencies
# https://github.com/mandiant/capa/issues/2996
"F401", # Unused imports
]

[lint.per-file-ignores]
# T201 print found schemas for scripts and entrypoints
"scripts/*" = ["T201"]
"capa/main.py" = ["T201"]
"capa/features/extractors/binja/find_binja_api.py" = ["T201"]
"tests/conftest.py" = ["I001"] # Suppress import sorting to preserve explicit legacy fixture loading order
"*_pb2.py" = ["ALL"] # Completely disable all formatting for auto-generated protocol buffer files

[lint.flake8-copyright]
notice-rgx = "Copyright \\d{4} Google LLC"
min-file-size = 1

[lint.isort]
length-sort = true
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: black auto-format
name: ruff auto-format

on:
pull_request:
Expand All @@ -13,7 +13,7 @@ permissions:
contents: write

jobs:
black-format:
ruff-format:
# only run on dependabot PRs or manual trigger
if: github.actor == 'dependabot[bot]' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-22.04
Expand All @@ -35,13 +35,15 @@ jobs:
pip install -r requirements.txt
pip install -e .[dev,scripts]

- name: Run isort
run: pre-commit run isort --all-files
- name: Run ruff/continue
# ruff returns non-zero error code after formatting, which is what we expect
continue-on-error: true
run: pre-commit run ruff --all-files

- name: Run black/continue
# black returns non-zero error code after formatting, which is what we expect
- name: Run ruff format/continue
# ruff format returns non-zero error code after formatting, which is what we expect
continue-on-error: true
run: pre-commit run black --all-files
run: pre-commit run ruff-format --all-files

- name: Check for changes
id: changes
Expand All @@ -58,5 +60,5 @@ jobs:
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR_ID}+${GITHUB_ACTOR}@users.noreply.github.com"
git add -A
git commit -m "style: auto-format with black and isort"
git commit -m "style: auto-format with ruff"
git push
8 changes: 2 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,8 @@ jobs:
pip install -e .[dev,scripts]
- name: Lint with ruff
run: pre-commit run ruff
- name: Lint with isort
run: pre-commit run isort --show-diff-on-failure
- name: Lint with black
run: pre-commit run black --show-diff-on-failure
- name: Lint with flake8
run: pre-commit run flake8 --hook-stage manual
- name: Check formatting with ruff
run: pre-commit run ruff-format --show-diff-on-failure
- name: Check types with mypy
run: pre-commit run mypy --hook-stage manual
- name: Check imports against dependencies
Expand Down
14 changes: 3 additions & 11 deletions .justfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
@isort:
pre-commit run isort --show-diff-on-failure --all-files

@black:
pre-commit run black --show-diff-on-failure --all-files
@ruff-format:
pre-commit run ruff-format --show-diff-on-failure --all-files

@ruff:
pre-commit run ruff --all-files

@flake8:
pre-commit run flake8 --hook-stage manual --all-files

@mypy:
pre-commit run mypy --hook-stage manual --all-files

@deptry:
pre-commit run deptry --hook-stage manual --all-files

@lint:
-just isort
-just black
-just ruff-format
-just ruff
-just flake8
-just mypy
-just deptry
62 changes: 12 additions & 50 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,36 @@
# ❯ pre-commit install --hook-type pre-push
# pre-commit installed at .git/hooks/pre-push
#
# run all linters liks:
# run all linters like:
#
# ❯ pre-commit run --all-files
# isort....................................................................Passed
# black....................................................................Passed
# ruff-format..............................................................Passed
# ruff.....................................................................Passed
# flake8...................................................................Passed
# mypy.....................................................................Passed
#
# run a single linter like:
#
# ❯ pre-commit run --all-files isort
# isort....................................................................Passed
# ❯ pre-commit run --all-files ruff
# ruff.....................................................................Passed

repos:
- repo: local
hooks:
- id: isort
name: isort
stages: [pre-commit, pre-push, manual]
language: system
entry: isort
args:
- "--length-sort"
- "--profile"
- "black"
- "--line-length=120"
- "--skip-glob"
- "*_pb2.py"
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

- repo: local
hooks:
- id: black
name: black
- id: ruff-format
name: ruff format
stages: [pre-commit, pre-push, manual]
language: system
entry: black
entry: ruff
args:
- "--line-length=120"
- "--extend-exclude"
- ".*_pb2.py"
- "format"
- "--config"
- ".github/ruff.toml"
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
exclude: '.*_pb2\.py$'
always_run: true
pass_filenames: false

Expand All @@ -75,27 +54,10 @@ repos:
- "scripts/"
- "tests/"
- "web/rules/scripts/"
exclude: '.*_pb2\.py$'
always_run: true
pass_filenames: false

- repo: local
hooks:
- id: flake8
name: flake8
stages: [pre-push, manual]
language: system
entry: flake8
args:
- "--config"
- ".github/flake8.ini"
- "--extend-exclude"
- "capa/render/proto/capa_pb2.py,capa/features/extractors/binexport2/binexport2_pb2.py"
- "capa/"
- "scripts/"
- "tests/"
- "web/rules/scripts/"
always_run: true
pass_filenames: false

- repo: local
hooks:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
### capa Explorer IDA Pro plugin

### Development
- replace black/isort/flake8 with ruff @mike-hunhoff #2992

### Raw diffs
- [capa v9.4.0...master](https://github.com/mandiant/capa/compare/v9.4.0...master)
Expand Down
1 change: 0 additions & 1 deletion capa/capabilities/common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
1 change: 0 additions & 1 deletion capa/capabilities/dynamic.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
1 change: 0 additions & 1 deletion capa/capabilities/static.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
2 changes: 1 addition & 1 deletion capa/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def evaluate(self, features: FeatureSet, short_circuit=True):
# because we've overridden `__bool__` above.
#
# we can't use `if child is True` because the instance is not True.
success = sum([1 for child in results if bool(child) is True]) >= self.count
success = sum(1 for child in results if bool(child) is True) >= self.count
return Result(success, self, results)


Expand Down
4 changes: 2 additions & 2 deletions capa/features/extractors/binexport2/arch/intel/insn.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ def is_security_cookie(
basic_block_index: int = bbi.basic_block_index
bb: BinExport2.BasicBlock = be2.basic_block[basic_block_index]
if flow_graph.entry_basic_block_index == basic_block_index:
first_addr: int = min((idx.insn_address_by_index[ir.begin_index] for ir in bb.instruction_index))
first_addr: int = min(idx.insn_address_by_index[ir.begin_index] for ir in bb.instruction_index)
if instruction_address < first_addr + SECURITY_COOKIE_BYTES_DELTA:
return True
# or insn falls at the end before return in a terminal basic block.
if basic_block_index not in (e.source_basic_block_index for e in flow_graph.edge):
last_addr: int = max((idx.insn_address_by_index[ir.end_index - 1] for ir in bb.instruction_index))
last_addr: int = max(idx.insn_address_by_index[ir.end_index - 1] for ir in bb.instruction_index)
if instruction_address > last_addr - SECURITY_COOKIE_BYTES_DELTA:
return True
return False
Expand Down
Loading
Loading