This repository contains the configuration, specialized agent definitions, and infrastructure for Mjolnir, an AI driven security auditing framework designed for open-source Root-of-Trust (RoT) projects like Caliptra, OpenTitan, and OpenPRoT.
Mjolnir leverages advanced AI foundation models and adversarial review pipelines to provide continuous security assurance through periodic, incremental scanning of firmware and RTL.
Mjolnir is built using Nix to facilitate reproducibility, flexibility, and ease of deployment in CI environments.
Mjolnir performs its analyses in phases, described below.
- Extract: The tool prepares a unique checkout directory within your workspace and uses the Target component to clone and checkout the code.
- Transform: It prepares the Prompts and executes the Backend model. The backend model analyzes the code in the checkout directory and writes a report to a unique, timestamped run directory.
- Load: It delegates the results handling to the Storage backend, which may move or upload the entire run directory.
The Mjolnir infrastructure consists of the follow components:
- Job Files (
jobs/*.nix): Pure Nix files that assemble a specific run by plugging in Target, Backend, Prompt, Storage, and Hooks. - Targets (
target/*.nix): Modules that define how to retrieve source code (e.g.,git.nix). - Backends (
backends/*.nix): Modules that describe how to run a specific AI model or analysis tool. (Note:claude.nixis currently a placeholder and not yet working). - Prompts (
prompts/*.nix): Modules that handle the resolution and preparation of instructions and context to feed the backend model. - Storage Backends (
storage/*.nix): Modules that define how to persist or upload the final run artifacts. - Hooks: Bash script snippets that can be injected at various stages (e.g.,
preExtract,postTransform) to customize behavior (e.g., deleting files, running linters).
Below we elaborate on the interfaces to some of the key components described above.
Targets are responsible for retrieving the code to be analyzed.
- Factory Function:
{ pkgs, ... } -> { ... } - Attributes:
repoName: String, used for directory naming.checkout { checkoutDir }: Returns a Bash snippet to clone/checkout the code into the provided directory.
Backends are typically LLMs that perform the actual security analyses.
- Factory Function:
{ pkgs, ... } -> { ... } - Attributes:
name: String, used for naming run artifacts.run { systemPrompt, src, output }: Returns a Bash snippet.systemPrompt: Path to a file with the system instruction.src: Path to the directory containing the source code.output: Path where the agent should write its report.
The Prompts component prepares text and context to prime the backend models to perform their analyses.
- Factory Function:
{ pkgs, systemPrompt } -> { ... } - Attributes:
backendArgs: An attribute set containing pre-processed Nix store paths forsystemPrompt.
The Storage component handles the outputs of the backend component, e.g. the vulnerability reports produced by the LLM.
- Factory Function:
{ pkgs, ... } -> { ... } - Attributes:
name: String, for logging purposes.upload { runDir }: Returns a Bash snippet to handle the results in the provided directory.
The tool uses Nix Flakes. Ensure you have Nix installed with flakes enabled.
Audits are executed using nix run .#<target>. You can run individual component scans, predefined job groups, or test targets.
These targets clone the respective repository, filter for relevant source files, perform per-file analysis using the Gemini backend, and generate a reviewed report and interactive dashboard.
-
Caliptra SW 2.1 Audit: Filters for Rust files in ROM and Runtime.
nix run .#caliptra-sw-2p1-latestThis job is configured to clone
chipsalliance/caliptra-sw, filter for.rsfiles inrom/dev/srcandruntime/src, and analyze them. -
Caliptra MCU SW 2.0 Audit:
nix run .#caliptra-mcu-sw-2p0-latest -
OpenTitan SW Subjob Audits: Individual scans targeting specific components of OpenTitan software.
nix run .#opentitan-rom nix run .#opentitan-rom-ext nix run .#opentitan-manuf nix run .#opentitan-lib nix run .#opentitan-crypto
Job groups allow executing multiple audits sequentially. This is useful for full regressions or CI environments.
scan-all: Runs all main component scans sequentially (caliptra-sw-2p1-latest,caliptra-mcu-sw-2p0-latest, and all 4opentitansubjobs).nix run .#scan-allopentitan-all: Runs all 4 OpenTitan subjobs sequentially.nix run .#opentitan-allscan-all-test: Runs test/smoke scans (smoke-test).nix run .#scan-all-test
Used for verifying the infrastructure and authentication.
smoke-test: Runs a quick analysis using a mock backend.nix run .#smoke-test
Mjolnir includes a script to aggregate vulnerability scan results from multiple codebase scans into a centralized, interactive HTML dashboard. This is useful for getting a high-level overview of all security audits.
The script looks for the latest scan results for the following components (expected to be present in the repository root):
- Caliptra MCU SW 2.0 (source:
caliptra-mcu-sw-2p0-output) - Caliptra SW 2.1 (source:
caliptra-sw-2p1-output) - OpenTitan ROM (source:
opentitan-sw-output/rom) - OpenTitan ROM EXT (source:
opentitan-sw-output/rom_ext) - OpenTitan Manuf (source:
opentitan-sw-output/manuf) - OpenTitan Lib (source:
opentitan-sw-output/lib)
To aggregate the results, run the aggregate_results.py script, providing a
target directory where the aggregated dashboard should be generated. By default,
it will use the components.toml configuration file located in the scripts directory:
python3 scripts/aggregate_results.py <target_dir> [options]--components <file>: Path to TOML file containing component definitions. Can be specified multiple times to merge different configurations.--regen-html: Only regenerate theindex.htmllanding page from existing results in the target directory, without copying new files.-j,--jobs <job1> ...: Aggregate the specified job and regenerate theindex.htmllanding page.
The script will create the target directory and generate the following:
<target_dir>/index.html: The main landing page dashboard linking to all component reports.<target_dir>/<component_key>/: Subdirectories for each component containing:dashboard.html: The interactive HTML dashboard for that specific component scan.main_report.toml/main_report.md: The full vulnerability report.reviewed_report.md: The agent-filtered vulnerability report in Markdown.
The generated index.html uses a shared dark theme styling and provides quick
access to both HTML dashboards and Markdown reports.
Mjolnir also includes a script to aggregate results directly from a GCS bucket without downloading them. This script scans the bucket for the latest results of each component and generates an index.html dashboard with links pointing directly to the objects in GCS.
To aggregate results from GCS, run the aggregate_gcs_results.py script:
python3 scripts/aggregate_gcs_results.py --bucket <bucket_name> [options]--bucket <name>: (Required) The name of the GCS bucket.--prefix <prefix>: The prefix in the bucket where reports are stored (default:v0).--components <file>: Path to the TOML file containing component definitions. Defaults togcs_components.tomlin the script directory.--output <file>: The name of the generated HTML file (default:index.html).--upload: Automatically upload the generatedindex.htmlto the root of the specified GCS bucket.
The script generates a single HTML file (default: index.html) containing the dashboard. If the --upload flag is used, it will be uploaded to gs://<bucket_name>/index.html and the public URL will be printed.
There are two ways to assemble a new auditing job:
For standard threat analysis audits that use the default agents and backends, you can use the shared job builder in jobs/default_job.nix to create a concise job definition:
{ pkgs }:
import ./default_job.nix { inherit pkgs; } {
name = "My Custom Job";
workspaceDir = "/tmp/my-workspace";
outputDir = "./my-results";
parallel = 50;
target = {
repoUrl = "https://github.com/example/repo.git";
repoName = "my-repo";
commit = "latest";
fileCommand = "${pkgs.fd}/bin/fd -t f -e rs";
};
postExtract = ''
echo "Custom cleanup logic after code extraction goes here..."
'';
}If you need a custom workflow, different backend, or multiple custom hooks, create a file in jobs/my-audit.nix:
{ pkgs }:
let
# 1. Initialize Components
gitTarget = import ../target/git.nix {
inherit pkgs;
repoUrl = "https://github.com/example/repo.git";
repoName = "my-project";
commit = "latest";
};
prompt = import ../agents/load.nix {
inherit pkgs;
agentDir = ../agents/rust_auditor;
backendName = geminiBackend.name;
};
geminiBackend = import ../backends/gemini.nix { inherit pkgs; };
localStorage = import ../storage/local.nix {
inherit pkgs;
path = "../audits";
};
in
{
# 2. General Configuration
config = {
workspaceDir = "/tmp/audit-workspace";
outputDir = "./results";
};
# 3. Component Assembly
target = gitTarget;
backend = geminiBackend;
prompt = prompt;
storage = localStorage;
# 4. Custom Hooks (Optional)
hooks = {
postExtract = ''
echo "Cleaning up non-source files with Python..."
${pkgs.python3}/bin/python3 <<EOF
import os
code_dir = os.environ.get('CODE_DIR')
for root, dirs, files in os.walk(code_dir):
for file in files:
if not file.endswith('.c'):
os.remove(os.path.join(root, file))
EOF
'';
};
}Mjolnir uses Application Default Credentials (ADC) to authenticate with Google Cloud Vertex AI:
- Install gcloud: Ensure you have the Google Cloud CLI installed.
- Login: Run the following command on your host machine to generate ADC:
gcloud auth application-default login
- Project ID: Ensure your job configuration (or standard environment) specifies the correct billing GCP project.
The orchestrator will look for the credentials file at the standard location:
$HOME/.config/gcloud/application_default_credentials.json
To verify the Nix infrastructure builds:
nix build .#smoke-test --no-link
To verify the tool with mocks:
nix run .#smoke-testResults will be in the test-output directory.