Quick start
Install the app from the download page, or build the CLI from source, then point it at a repository:
$ cargo build --release $ ./target/release/onuri repo scan ~/code/my-project
Onuri walks the working tree (respecting .gitignore and .onuriignore), then every commit in history, then every dependency in your lockfiles.
CLI reference
onuri repo scan [PATH] scan a repo (default .) --json | --sarif | --format human|json|sarif -o, --output FILE write report to a file --no-history working tree only --offline no OSV.dev / registry lookups --max-commits N cap the history walk --fail-on low|medium|high|critical|never (default low) onuri rules list active rules (built-in + yours) onuri rules check FILE validate a rule file onuri config path | show where config lives / effective config
| Exit code | Meaning |
|---|---|
0 | Clean |
1 | Findings at or above --fail-on |
2 | Error |
Findings
Every finding carries:
id— a stableONR-xxxxxxxxxxxxidentifier that stays the same across runsseverity— critical high medium lowcategory,path,line/columnreasonandfix— what's wrong and what to do- a redacted
snippet— secrets are never printed in full - for history findings,
commit {id, author, email, date, summary}
Configuration
Configuration lives in your per-user config directory, never inside a project, so a scanned repo, or an AI agent working in it, can't change how it's scanned.
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/com.onuri.Onuri/config.toml |
| Linux | ~/.config/onuri/config.toml |
[limits] max_file_size = 209715200 # 200 MB max_archive_depth = 3 max_decompress_ratio = 100 [repo] scan_history = true max_commits = 0 # 0 = all trusted_authors = ["me@example.com", "*@mycompany.com"] warn_unsigned_commits = false entropy_base64 = 4.5 entropy_hex = 3.0 online = true # OSV.dev + registry existence checks disabled_rules = []
Writing rules
Add your own rules as *.toml files in <config dir>/rules/. A rule with the same id as a built-in replaces it.
version = 1 [[rule]] id = "acme-api-key" # unique, kebab-case name = "ACME API key" category = "secret" # secret | obfuscation | exfiltration | tamper | config severity = "high" # critical | high | medium | low regex = '''\b(acme_[a-z0-9]{32})\b''' secret_group = 1 # capture group holding the secret keywords = ["acme_"] # cheap lowercase prefilter min_entropy = 3.0 # optional allow = ['''acme_0{32}'''] # optional: placeholders to ignore paths = ["**/*.py"] # optional glob filter exclude_paths = ["**/tests/**"] # optional glob filter fix = "Revoke the key in the ACME dashboard and rotate."
Validate it before use:
$ onuri rules check my-rules.tomlBuilt-in checks
Alongside 30 pattern rules for common secrets, Onuri runs these structural checks:
| Area | Checks |
|---|---|
| Secrets | generic-high-entropy |
| Obfuscation | trojan-source-bidi invisible-unicode homoglyph-identifier minified-source |
| Hygiene | env-file-committed env-file-in-history gitignore-missing gitignore-missing-entry |
| History | untrusted-author unsigned-commit tamper-history |
| Tamper | npm-install-script workflow-pwn-request workflow-script-injection workflow-unpinned-action git-hook-present build-script-suspicious |
| Dependencies | osv-malicious-package osv-vulnerable typosquat package-not-found |
Suppressing findings
Put an .onuriignore file at the repo root. It uses gitignore-style globs, optionally scoped to one rule:
vendor/** generic-high-entropy:tests/fixtures/**
Or suppress inline, on the flagged line or the line above:
TEST_KEY = "..." # onuri:ignore generic-high-entropy
GitHub code scanning
Emit SARIF and upload it, and findings appear in the Security tab and inline on pull requests:
- run: onuri repo scan . --sarif -o onuri.sarif --fail-on never
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: onuri.sarif }