Skip to main content

Authoring zuit Plugins

A zuit plugin is a directory containing a zuit-plugin.toml manifest and an executable (any language). At scan time zuit spawns the executable, passes --project-root <abs> and --output-format <zuit-json|sarif>, and parses its stdout as findings. Plugins are global (per user) and run as ExternalTool analyzers alongside the built-in language frontends.


Install / remove / update / list​

zuit add-analyzer <PATH | GIT_URL> [--name NAME] # install
zuit remove-analyzer <NAME> # uninstall (idempotent)
zuit update-analyzer <NAME> # git pull --ff-only; no-op for local
zuit list plugins # show installed plugins + metadata

Source detection (in order): http:///https:///git:///ssh:// prefix, .git suffix, bare git@host:path form → git clone. Otherwise → local path (must exist).

Name resolution: --name wins, then manifest name, then slug from git URL (last-path-segment, .git stripped). Duplicate name → error; use update or remove first.


Manifest reference (zuit-plugin.toml)​

FieldReqDefaultDescription
nameyes—Install dir name. Pattern: ^[a-z0-9][a-z0-9-]{0,63}$
versionyes—Semver string (informational)
outputyes—"zuit-json" or "sarif"
commandyes—Argv array; zuit appends --project-root + --output-format
descriptionno—One-line summary
rule_id_prefixno"<name>/"Prefix prepended to rule IDs that don't already have it
extensionsno—Informational list (e.g. ["zig", "zon"]); does not gate execution
timeout_secondsno60Kill after N seconds
max_output_bytesno33554432Truncate stdout after 32 MiB
licenseno—SPDX expression
homepageno—URL

Output format: ndjson vs SARIF​

zuit-jsonsarif
FormatOne JSON object per lineSARIF 2.1.0 single-document
Best whenWriting a new plugin from scratchWrapping an existing SARIF-emitting tool
Span precisionbyte offsets or line/col (byte-offset authoritative)physicalLocation region only
Custom dimensionsdimension field — any v1 string or customNot supported (mapped to security)

Sample plugin: Bash + ndjson​

Layout​

my-plugin/
├── zuit-plugin.toml
└── run.sh

zuit-plugin.toml​

name = "my-plugin"
version = "0.1.0"
output = "zuit-json"
command = ["./run.sh"]
description = "Example Bash plugin"

run.sh​

#!/usr/bin/env bash
# zuit passes: --project-root <abs> --output-format zuit-json
set -euo pipefail
ROOT=""
while [[ $# -gt 0 ]]; do
case "$1" in
--project-root) ROOT="$2"; shift 2 ;;
*) shift ;;
esac
done

# Emit one finding per matching line; blank lines are tolerated.
grep -rn "TODO" "$ROOT" --include="*.rs" | while IFS=: read -r file line rest; do
printf '{"rule_id":"my-plugin/todo","severity":"info","file":"%s","line":%s,"message":"TODO comment","dimension":"maintainability"}\n' \
"$file" "$line"
done

Install: zuit add-analyzer ./my-plugin

Example output line:

{"rule_id":"my-plugin/todo","severity":"info","file":"src/main.rs","line":5,"message":"TODO comment","dimension":"maintainability"}

Sample plugin: Python + SARIF​

Layout​

sarif-plugin/
├── zuit-plugin.toml
└── check.py

zuit-plugin.toml​

name = "sarif-plugin"
version = "0.1.0"
output = "sarif"
command = ["python3", "./check.py"]

check.py​

#!/usr/bin/env python3
"""Minimal SARIF-emitting plugin. Flags files larger than 500 lines."""
import argparse, json, os, sys

parser = argparse.ArgumentParser()
parser.add_argument("--project-root", required=True)
parser.add_argument("--output-format") # consumed but unused — we always emit SARIF
args = parser.parse_args()

results = []
for dirpath, _, filenames in os.walk(args.project_root):
for name in filenames:
if not name.endswith(".py"):
continue
path = os.path.join(dirpath, name)
with open(path, errors="replace") as f:
lines = f.readlines()
if len(lines) > 500:
results.append({
"ruleId": "large-file",
"level": "warning",
"message": {"text": f"File has {len(lines)} lines (> 500)"},
"locations": [{"physicalLocation": {
"artifactLocation": {"uri": path},
"region": {"startLine": 1}
}}],
})

sarif = {"version": "2.1.0", "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json",
"runs": [{"tool": {"driver": {"name": "sarif-plugin", "rules": []}}, "results": results}]}
json.dump(sarif, sys.stdout)

Install: zuit add-analyzer ./sarif-plugin


Trust & safety​

Plugins run as the calling user with full OS privileges — equivalent to running pip install or cargo install. There is no sandboxing in v1. Read the plugin source before installing from an untrusted URL. Future work: per-plugin allowlist in ~/.zuit/plugins/policy.toml and first-run confirmation prompt.