TRUSTSIGHT

TRUSTSIGHT

Audits AUR PKGBUILDs before you update: catches careless malice and structural risk, and tells you what it can’t verify.

Fully open source under MIT. The report points at where to look; it does not replace reading the PKGBUILD.

Install from source
Install using https: git clone https://github.com/emiliano-go/trustsight.git cd trustsight/packaging/aur && makepkg -si trustsight review

TrustSight audits AUR package updates before you install them.

Whenever an installed AUR package has a newer version available, TrustSight clones the repository, diffs the new PKGBUILD against the one you have, and runs a published set of rules over the change. The output is a report: what changed, which rules fired, and what the analysis could not see. The report does not replace reading the diff: every finding names the line or URL it fired on, so you know exactly where to look, and you do the looking.

Everything runs locally. Nothing is executed (no PKGBUILD, no extracted command) and nothing is installed or modified. The report is deterministic: the same diff, the same configuration, and the same database produce the same verdict, so re-running a review gives the same answer. Verdicts are template-based plain English, e.g. Version bump. modified PKGBUILD. Signals: checksum set to SKIP; novel dependency 'pyfoo' added in depends.

  1. It reports evidence, and the absence of evidence.

    Every finding is traceable to a diff line, a URL, or a novelty record, and anything the run could not examine is stated in the report.

  2. Absence is not proof of safety.

    An UNFLAGGED verdict means no published rule matched what was examined. It does not mean the package is safe, that the ruleset is complete, or that anything was executed.

  3. The report is input to a decision, not the decision.

    TrustSight does not authorize an update. You do.

  4. Unknowns surface.

    Errors, truncated diffs, and unexamined content are part of the report, never hidden by default.

Documentation · the quickstart ↗

What a review looks like

Default output from trustsight review for two outdated packages. The first is a routine version bump with updated checksums; the second is a change worth a look before you build it, and it carries at least one finding from every rule namespace, so each kind of signal appears exactly as a real report shows it. The dependency example shows the mini-cards nested in the output. Without flags the report shows the findings and the verdict, no score column.

Current renderer excerpts from trustsight review. A clean verdict means no known signal fired, not that the package is safe.

Documentation · reading a report ↗

What it detects

The rules are pattern-based and published in full, so the precise statement of what the tool catches is the rules reference. The short version, from the README:

Attack / riskHow TrustSight catches it
Piped shell scripts (curl | bash, base64 | sh)Scans every new or changed line for command-to-shell pipelines (R001).
Obfuscated commands (encoded strings, LD_PRELOAD environment subversion)Resolves variables, decodes known encodings, and flags build-environment tampering (R003, R070).
Checksum disabled or removedCompares the old and new sha256sums / md5sums arrays (R004, R005).
Source URL typosquatting (githab.com for github.com)The source-bucket prior classifies every new URL; a domain written with confusable characters is labelled homograph_attack (+30). A prior, not a rule.
Package-name typosquatting (libuvc resembling libuv)Edit-distance comparison against more popular packages in the seed database (R074).
A risky AUR dependency (novel, typosquatted, or hijacked)Walks AUR dependencies to a configurable depth (default: direct ones; --depth to go further) and analyses each as a package in its own right: novel (D001), typosquatted (D002), network-using makedepends (D003), and provides hijacks (D004).
Source URL swapped without a version bumpTracks source URL changes that do not come with a new version (C003).
Novel, never-before-seen URLs or maintainersCompares against the signed release seed: about 180,000 known source URLs and 35,587 hashed maintainer identities, in the novelty tier.
Known-bad indicatorsMatches package URLs and strings against signed, federated IOC baselines; reported outside the heuristic score (IOC tier).
Unicode bidi override attacks (invisible characters that change how text displays)Detects directionality overrides and homoglyph codepoints in PKGBUILD content (R013, FATAL).
Prompt injection in package metadataPattern-matches common injection templates; the primary defence is structural (R012).
GPG verification removedDetects when validpgpkeys was populated and is now empty (R069).
Untrusted maintainer takeoverA maintainer change to someone never seen before (R071).
A stale package suddenly revivedA package with no updates for over a year suddenly gets one (R067).
Sabotage payloads (fork bombs, rm -rf /, disk wiping, coin miners)Command-position matching separates build-sandbox housekeeping (rm -rf "$srcdir/x") from system damage (S001–S008).
Orphan hijacking (adopted from orphan, then rewritten with no upstream change)The maintainer field against its last recorded state, plus a recipe-only-change signature (R141R142R143).
Build steps that fetch unpinned code (npm install in a build function)Not scored. Reported as the unpinned_build_deps coverage gap and as W002, because what the build will run is not in the analysed text and no checksum covers it.

Every finding is reported with the rule that fired and the line or URL it matched, so nothing on this page has to be taken on faith. The full catalogue (pattern, severity, and measured fire rate for each rule) is in the rules reference.

Documentation · the rules reference ↗

Four kinds of evidence

Every result reduces to one of four tiers, which says what kind of information a signal is. Nothing subtracts: no signal ever lowers a score.

TierWhat it isExampleWeight
A: StructuralDirect structural facts from the diff.curl | bash (R001); checksum set to SKIP (R004)The strongest evidence
B: Priors / ContextSource-bucket classification of every new URL.an unknown domain adds weightModerate: a prior, not proof
C: History / NoveltyFirst-seen history for URLs and maintainers, maturity-gated.URL first seen globally; maintainer first seenScales with observation history
D: VerificationWhat the recipe declares: checksums, PGP keys, pins.P001–P0080: reported, never scored
Reported, not scoredAnalysis boundaries: bytes the package will run that this run could not read.W001–W0060: always shown

A declared checksum or PGP key costs an attacker nothing to claim, so tier D is reported at weight zero, for you to check against the file. TrustSight does not verify these claims; it reports that the recipe makes them.

Documentation · evidence tiers ↗

The weights are measured, not asserted

Against the locked 3,739-diff benign corpus, 68.3% of benign diffs score 0. Benign diffs reach a 95th percentile of 35; the CRITICAL-class corpus has a 5th percentile of 60. The 20-point threshold is not the benign p95; it sits at the 86.9th percentile, so about 13% of benign diffs land above it. What matters is that the two distributions do not overlap.

The separation is enforced, not described. CI re-measures both distributions against the shipped configuration on every push and fails the build if benign p95 is no longer below malicious p5. A change that narrows the gap is rejected.

Documentation · measured fire rates ↗

How the claims are tested, and how you change them

Testing. The test suite covers 3,617 tests across 57 files. CI enforces separate security and calibration gate suites on every push and pull request. Among them: CRITICAL recall stays at 100% (every labelled malicious sample must fire the rules it is labelled for); the separation gate requires benign p95 to stay below malicious p5; and any scoring rule that fires on more than 30% of the benign corpus is demoted to INFO, because a rule that fires on a third of ordinary updates is not a signal. The rules are checked against their documentation on every test run, so a documented pattern cannot drift from the one that runs.

Configuration. The rules live in ~/.config/trustsight/rules.toml and are loaded at runtime, so you can change a severity, disable a rule, or add your own pattern. config.toml controls review behaviour:

# ~/.config/trustsight/config.toml
[depth]
levels = 1            # 0 off, 1 direct deps, n levels, -1 all

[rules]
experimental = false  # rules whose fire rate is not yet measured

[seed]
auto_import = true    # fetch the signed novelty seed on first run

Documentation · the configuration reference ↗

What it cannot see

A PKGBUILD is a recipe, not a meal. A signed, version-bumped PKGBUILD whose checksum matches a backdoored tarball is invisible to this tool. The reasons are structural, and they are documented rather than hidden:

Malicious upstream tarballs. TrustSight audits the PKGBUILD, not the binaries it downloads. A clean build file can point to a compromised tarball.

Deliberately unremarkable attacks. With no commands added, no URLs changed, and no checksums disabled, there is no diff signal. The rules detect patterns associated with compromise, not compromise itself.

Dependencies past the configured depth. Past --depth, and past the hard ceilings on an exhaustive walk, the closure is unread, and the report says so with a deps_not_scanned coverage gap.

Runtime behaviour. Nothing is executed, sandboxed or otherwise, because executing a PKGBUILD would let hostile input detect the review and change its behaviour. Static analysis by design.

Zero-day structural attacks. The rules are pattern-based. A novel attack that leaves no matching pattern will not fire.

Documentation · what TrustSight cannot see ↗

184 documented rules, in seven namespaces

Every shipped rule (its pattern, its severity, and its measured fire rate) is published in the rules reference. Rules are grouped into seven namespaces; the categories under each namespace are the kinds of claims its rules make.

Detection rules

127 rules

127 pattern rules read the diff and the variable-resolved command text. Each is a published pattern with a severity from INFO to FATAL, and a FATAL finding pins the verdict to 100: a bidi-override attack cannot be weighted away. The rules group by the kind of claim they make, from fetch and execution and integrity through obfuscation, deception, and temporal context, and every match is reported with the line or URL it fired on.

Structural rules

9 rules

Nine context rules reason about the diff as a whole rather than any single line, comparing the old and new states of the same field: a source URL that changed without a version bump, a checksum list that shrank, metadata that contradicts itself, unread content moved under a stable version. Where the pattern rules ask what a line does, the structural rules ask whether the change is internally consistent, across integrity, metadata, and fetch facts.

Dependency-graph rules

4 rules

Four rules treat the dependency graph as first-class evidence. A review walks a package's AUR dependencies, direct ones by default and deeper with --depth, analysing each as a package in its own right: has it been seen before, does its name resemble a more popular package, does it fetch over the network during build, does it hijack a provides others rely on. Novelty is judged against the same signed seed the URL rules use.

Sabotage rules

8 rules

Eight rules look for payloads aimed at the operator's machine rather than at getting something out of it: resource exhaustion, deletion, permission sabotage, service disruption, and resource theft. What unites them is that the machine running the build is the target. Each rule is written against a distinction, not a command: rm -rf inside the sandbox is housekeeping, rm -rf / is an attack, and a mention is not an invocation.

Crossfire rules

23 rules

The evasion technique, not the payload it hides. Every other family fires on what a diff does; these fire on how it was written. Partial quoting, array routing, command substitution and other tokenizer-defeating shapes assemble an executable name no pattern ever sees, so a word the tokenizer could not reduce to a literal is itself the signal. One rule covers the evasion surface of every payload rule at once, and a defeated tokenizer produces a CRITICAL finding rather than silence.

Declared practice

7 findings, weight 0

What the recipe declares, not what the analysis found. These findings read the claims a PKGBUILD makes about itself: checksums declared for every non-VCS source, validpgpkeys present, signatures sourced, sources pinned to a commit hash or tag, downloads over HTTPS, or a branch/unpinned ref tracked. Every one is INFO and checkable by the reader against the file itself. They are reported, never credited: a signal an attacker can assert for free must not move a score.

Unverifiable

6 findings, weight 0

Not a claim about the recipe but about the analysis: something the package will run that this run could not read. A build script that executes a file not declared in source=(), a patch not committed to the repo, a manifest generated at build time, or a dependency resolved from a registry. Each is reported so the reader knows the boundary of what was examined; none contributes to the score.

Counts are per the generated rules index; the 14 category pages are closed, so every rule has exactly one page. trustsight lint-rules checks the shipped rules against their documentation on every test run. The rule files themselves are part of the open source repository: read a pattern, change a severity, or rebuild the tool, and nothing hides behind a binary.

Documentation · the rule system ↗

Install once, review every update after

Install from source
Install using https: git clone https://github.com/emiliano-go/trustsight.git cd trustsight/packaging/aur && makepkg -si trustsight review
  • Not on the AUR yet. aur.archlinux.org/trustsight.git does not exist; build from the PKGBUILD in the repository, which is MIT licensed and fully open source. The PKGBUILD runs the test suite during build, and makepkg -si pulls the dependencies in as proper pacman packages.
  • Requires Python 3.11+ and Arch Linux; packages are discovered via pacman.

The first run: a seed, then your own baseline

Novelty detection needs a baseline of what is normal in the AUR. On an empty database every URL looks first-seen and every maintainer looks new, which turns the novelty signal into noise. So the tool ships a seed of prior knowledge built from the AUR git mirror: about 180,000 normalised source URLs and salted hashes of maintainer identities. It is published as a signed release asset rather than shipped inside the package, because the AUR is exactly the channel under review. The first time you run trustsight review, the tool downloads the seed and imports it only after its ed25519 signature verifies against a key pinned in the package. Offline, the attempt is skipped and the run starts cold: the honest fallback for a tool that refuses unverified data.

Installing now is what makes the seed useful. Every review you run records observations in your local database, and novelty signals scale with that history: the maturity factor is observation_count / 50, so a package reaches full novelty weight after about 50 analyses. The seed supplies the bootstrap: it recognises about 86% of the source URLs in a package’s most recent update, and your own runs take over as soon as they outnumber it. Install today, review the next update, and by the time a real anomaly shows up the tool has already seen what normal looks like for your package set.

Documentation · installing TrustSight ↗

Frequently asked questions

Does this replace reading the PKGBUILD?

No. TrustSight is a second pair of eyes, not a substitute for reading the diff. Every finding points at the line or URL it fired on, so the report tells you where to look, and you still do the looking. The tool’s own principle is that the report is input to a decision, not the decision: it does not authorize an update, you do.

Is installing TrustSight safe?

The tool never runs the PKGBUILD, never executes extracted commands, and never installs or modifies anything it reviews; it reads diffs and runs pattern rules over the text. The package itself is MIT licensed and open source, and the PKGBUILD in the repository runs the test suite during build. Reviewing a package is not the same as trusting it, and the same applies to the reviewer.

What does an UNFLAGGED verdict mean?

It means the score stayed at or below 20: no published rule matched the evidence that was examined. It does not mean the package is safe, that the ruleset is complete, or that anything was executed. Absence of signals is a statement about detection, not about the update.

Why does the tool never run the PKGBUILD?

Executing a recipe written by the party under review would let hostile input detect the review and change its behaviour. Static analysis is a deliberate boundary: the tool reads what the diff says, not what a sandbox would run. Runtime behaviour is listed as a structural limitation, not an accident.

Why is my package flagged when the update looks normal?

About 13% of benign diffs score above the threshold. The tool reports evidence first and the score on request, so a flag is a reason to look, not a verdict to accept. Open the report, check which rules fired and where, and decide from the diff. That is the intended workflow, not a false-positive problem.

How does novelty detection work before I have any history?

The first run imports a signed seed of about 180,000 normalised source URLs and 35,587 hashed maintainer identities, verified against a key pinned in the package. Novelty signals are maturity-gated: they scale with your own observation count (observation_count / 50), so a cold database contributes nothing and your own reviews take over as history accumulates.

Does TrustSight phone home?

The analysis is local. The only two declared network hosts are aur.archlinux.org (the RPC, the metadata dump, the git clone, and cgit) and the GitHub releases channel, used only for verified baseline assets such as the seed and the IOC lists, on explicit commands or first-run auto-import. The tool never connects to a host named by the package under review.

Why does it review my AUR dependencies too?

makepkg builds a package’s depends on your machine in the same run, so a dependency is part of what actually executes. A default review analyses direct AUR dependencies and summarises them; trustsight review --deps reviews each as a package in its own right, and --depth n walks deeper. The walk is bounded, and a closure cut short is reported, never hidden.

Can it stop a malicious package from being installed?

No. TrustSight is a review tool, not an execution gate. It produces evidence about a diff and a verdict in plain English; you decide whether to build and install. Nothing the tool does blocks makepkg, and nothing it says is permission.

Why is there no score in the default output?

The default output is findings, the change summary, and the verdict, because a number invites a decision the tool is not entitled to make. The score exists, is deterministic, and is available with --score (and in JSON with --score or --risk). The evidence is the product; the score is a summary of it.

What happens when the tool cannot see everything?

It says so. Coverage gaps such as deps_not_scanned (dependencies past the configured depth) and unpinned_build_deps (build steps that fetch unpinned code) are part of the report, and an analysis with a coverage gap is never issued as UNFLAGGED. A report that hid what it could not look at would be indistinguishable from one that was switched off.

Documentation · the security model ↗