pw_fortifier#
This module provides tools and frameworks that both upstream Pigweed and downstream projects can use to enhance the security posture of their project.
find_core_owners#
find_core_owners.py is a tool to identify the primary owner(s) of specific
files or line ranges by analyzing git history.
What is a “core owner”?#
A “core owner” is defined as a user listed directly in the global OWNERS
file in the repository root. Users included from other files (such as
EXTENDED_OWNERS) or defined in nested OWNERS files are not considered
core owners.
Command-line usage#
The tool can be run as a standalone script. It takes one or more code snippets as arguments. Snippets can be file paths or file paths with a line range.
$ python3 pw_fortifier/py/pw_fortifier/find_core_owners.py \
-r /path/to/repo \
pw_status/public/pw_status/status.h:10-20
Arguments:
snippets: One or more arguments in the form<file>[:start-end].-r,--root: Root directory of the repository (defaults to.).-a,--any: Allow any owner, not just core team members.
Library usage#
You can also use CoreOwnerFinder as a Python library.
from pw_fortifier.find_core_owners import CoreOwnerFinder
# Initialize with repository root
finder = CoreOwnerFinder(root="/path/to/repo")
# Add snippets to analyze
finder.add("pw_status/public/pw_status/status.h", lines=(10, 20))
finder.add("pw_status/status.cc")
# Find the owner
owner = finder.find()
print(f"Primary core owner: {owner}")
# Find any owner (not restricted to core owners)
any_owner = finder.find(any_owner=True)
print(f"Primary owner: {any_owner}")
freshness_scan#
freshness_scan.py is a tool to scan project dependencies and report their
freshness. It identifies out-of-date third-party packages by comparing the
currently used version with newer versions available upstream.
Note
While freshness_scan.py is configured for upstream Pigweed
out-of-the-box, the underlying framework is generic. Downstream
projects can easily use it as a starting point by registering a
different set of scanners tailored to their specific dependency
types.
How it works#
The tool utilizes a registry-based architecture defined in
package_scanner.py:
Registry Initialization: A
PackageScannerRegistryis initialized with a root directory to scan.Scanner Registration: Concrete implementations of
PackageScannerare registered with the registry. Each scanner defines a filename pattern (e.g.,**/Cargo.tomlfor Cargo packages) it is interested in.Directory Walking: The registry walks the directory tree starting from the configured root directory, excluding pruned directories (such as build output or environment directories).
Pattern Matching: For each file found, the registry matches its path against the patterns of all registered scanners.
Scanning: When a match is found, the registry passes the relative path of the file to the matching scanner’s
scan()method. The scanner parses the file to extract dependency details (current version, package name), queries upstream sources (e.g., crates.io) for release history, and determines the earliest version that is still considered “fresh”.Reporting: The registry aggregates
FreshnessScanResultobjects from all scanners, andfreshness_scan.pyformats and outputs these results as CSV to stdout.
Included Scanners#
The following scanners are included in pw_fortifier:
BazelCipdScanner: Scans CIPD repository dependencies declared inMODULE.bazelfiles. It queries the CIPD registry API (using thecipdtool) to resolve package versions, release history, and timestamps.BazelDepScanner: Scans Bazel module dependencies declared inMODULE.bazelfiles. It queries the Bazel Central Registry (BCR) API and the upstream registry Git repository to determine dependency release history and timestamps.BazelMavenScanner: Scans Maven dependencies declared inMODULE.bazelfiles. It resolves artifacts to their group, name, and version, and queries the Maven Central registry (or other configured repositories) for release history and timestamps to determine freshness.CargoScanner: Scans Rust Cargo dependencies declared inCargo.tomlfiles. It extracts the package names and versions, resolves them against a correspondingCargo.lockfile to determine the exact versions in use, and queries the crates.io API to check for newer releases.CipdSetupScanner: Scans CIPD setup JSON files inpw_env_setup. It queries the CIPD registry API (using thecipdtool) to resolve package versions, release history, and timestamps.CopybaraScanner: Scans Copybara packages defined bycopy.bara.skyfiles underthird_party/. It extracts the upstream repository URL, determines the currently imported revision from the local Git history, and queries the upstream Git repository to check for newer commits.GoModScanner: Scans Go module dependencies declared ingo.modfiles. It queries the localgotoolchain (usinggo list) to resolve dependencies, their timestamps, and available newer versions.NpmScanner: Scans Node.js NPM dependencies declared inpackage.jsonfiles. It extracts package names and versions, resolves them against a correspondingpackage-lock.jsonfile to determine the exact versions in use, and queries the npm registry (usingnpm view) to check for newer releases.PipScanner: Scans Python Pip dependencies declared in requirements files underpw_env_setup/py/pw_env_setup/virtualenv_setup/. It extracts package names and versions, and queries the PyPI JSON API to check for newer releases. It resolves package ownership by searchingsetup.cfgfiles.
Extending the Scanner#
The framework is designed to be easily extendable to support new package managers or dependency definition formats. To add support for a new package type:
Create a Scanner Class: Inherit from
PackageScannerand implement the abstract methods:pattern(): Returns a glob-like pattern matching the dependency files this scanner handles.scan(pathname): Parses the file at the given relative path, queries upstream versions, and yieldsFreshnessScanResultobjects.
Register the Scanner: In
freshness_scan.py, import and register your new scanner class with the registry in themain()function:from pw_fortifier.my_new_scanner import MyNewScanner # ... registry.register(MyNewScanner())
Command-line usage#
The tool can be run as a standalone script. By default, it scans the current working directory.
$ python3 pw_fortifier/py/pw_fortifier/freshness_scan.py \
-r /path/to/project
Arguments:
-r,--root: Root directory of the project to scan (defaults to the current working directory).-o,--output: Output CSV file (defaults to stdout).