← dashboard

spaceship

model: opus-4-8 python static analysis off 0 PR(s) seen
workspaceninjarmm jira projectNJ review targetsmain · master · develop · release/* · *_release thresholdsseverity ≥ minor · confidence ≥ medium · cap 12 max diff0.4 MB pollevery 300s

What this is

AppSec SBOM-ingestion backend: ingests / scans (Grype/CVE) / stores / reports on SBOMs submitted by product build pipelines, tracks in-scope CVEs as Jira issues, notifies Slack/email, ingests SAST/SCA (QWiet). Python/Flask/SQLAlchemy/Postgres/Redis + esbuild web UI. Handles vulnerability data + many credentialed integrations.

Project primer

Durable orientation injected into every review as .ai-review/project.md.

spaceship — AppSec SBOM-ingestion backend (review primer)

NinjaOne's AppSec backend: it ingests, scans, stores, and reports on SBOMs (Software
Bills of Materials, CycloneDX/SPDX) submitted by the products' build pipelines, scans them for
CVEs with Grype, tracks in-scope vulnerabilities as Jira issues, and notifies via
Slack / email. Also ingests SAST/SCA findings (QWiet/ShiftLeft) and serves a web UI. A
security-sensitive service handling vulnerability data and many credentialed integrations —
review with a security lens.

Stack: Python 3.10 + Flask 3 + SQLAlchemy 2 (automap/DB reflection) + Alembic migrations;
PostgreSQL + Redis; Grype scanner; a vanilla-JS/esbuild frontend built into Flask static;
Poetry; Docker Compose (backend + postgres + redis + nginx/certbot). It reviews as a Python
repo (the small JS frontend is secondary — apply general judgment there).

Architecture (durable map)

web UI, integration webhooks/callbacks). SQLAlchemy models are **reflected from the live DB
(automap)** — so the DB schema and the code's assumptions about it must stay in lockstep.

scan (locally-cached vuln DB) → findings persisted → periodic auto-rescan, report,
SLO and roll-up generation.

roll-up, Jira delta-sync). Long-running work runs as tracked background tasks with logs.

(create/transition CVE issues), Slack + Microsoft Graph (notifications), QWiet/ShiftLeft
(SAST/SCA), AWS (boto3), ZAP (DAST).

findings are filed and who is notified — a data table that steers real outbound actions.

What matters most when reviewing here (durable risk areas)

formats — malformed/oversized/malicious documents, zip/entity bombs, path traversal in file
handling, and resource exhaustion on parse are the highest-scrutiny area.

missing/incorrect auth check, a token that grants too much, or an unauthenticated ingest/admin
endpoint is a serious finding. Verify each new/changed route is gated.

AWS/QWiet — never logged, never returned in responses, scoped minimally. Flag any secret that
reaches logs, error bodies, or the UI.

and outbound calls (SharePoint/Jira/HTTP) whose URL/host is influenced by untrusted data.

ordering, a migration that diverges from what the automap code expects, or one that locks a big
table. A bad migration is a production-data risk.

dedup, scope mapping, or SLO math mislabels real risk (false all-clear or false alarm). Scan
parsing, severity thresholds, and the mapping config deserve care.

(SharePoint / Jira / external APIs — the config deliberately keeps worker counts low), plus
concurrency on shared state. A task that double-files Jira issues or spams notifications is a
real bug.

partial state — a dropped CVE is a security miss, not just a glitch.

Security-sensitive zones

The SBOM/SAST ingest endpoints (untrusted parsing); auth/token verification on API + UI; the
credential-bearing integration clients (SharePoint/Jira/Slack/Graph/AWS/QWiet); the Grype
scan + CVE severity/dedup logic; the project/component/env→Jira/notification mapping; and the
Alembic migrations.

Compatibility surfaces (contracts that must not silently break)

contract change can silently drop incoming SBOMs.

migrations must apply cleanly and in order on existing production data.

and the env-var config contract (Compose defaults) that operators rely on.

email recipients, Jira projects).

Conventions & gotchas

frontend is bundled with esbuild into the Flask static dir at image build.

UTC hours and 0 disables them. Secrets live only in .env — never commit or log them.

Grype. This repo has its OWN CLAUDE.md — consult it (and README.md) for current specifics.

_Orientation only — verify current specifics in the code; this describes durable architecture &
risk areas, not a snapshot._

Ecosystem map

Shared cross-project relationships, injected as .ai-review/ecosystem.md.

NinjaRemote / NinjaOne project ecosystem (relationship map)

How the monitored repos relate — so a change in one repo is reviewed with its blast
radius in mind (a change upstream of ncpeer can break ncpeer even if it looks local).
Orientation only; verify specifics in code.

NinjaRemote product

speak the NCRP wire protocol (protocol/format changes must stay compatible across
client and server versions).

Cloud-side relays of the ninja-remote system (AWS-hosted, Python/Go)

URLs, it pairs them and forwards frames verbatim. Signed join URLs are minted by the
Ninja backend — the signature scheme is a backend⇄relay contract, and the frame
semantics are a peer⇄peer contract.

a signed upload leg streams to a signed download leg through memory (never stored).
Two parallel implementations (Python ftsv2/ + Go fts_go/) must stay behaviorally
identical; the URL/signature scheme is shared-family with websocket-switch.
NinjaRemote file-transfer features ride on these relays when peers can't go direct.

Pulled INTO ncpeer (and sometimes nmsnj) — upstream of the products

or behaviour change here affects both consumers.

build against**. A dependency bump changes what those products link (security/ABI/build).

through the ncpeer_vcpkg overlay.

Largely independent

shares nccommon and ncpeer_vcpkg with ncpeer.

part of the remote-desktop family). Runs as a child process of the NinjaRMM agent (the
parent owns install/upgrade/policy/launch); NinjaFlow owns the CLI contract, config schema,
credential precedence, DB schema/migrations, and exit-code meanings the parent consumes
those are cross-process contracts. Submits telemetry to the Ninja Backend. Shares no code with
ncpeer/nmsnj; adjacent to nmsnj only in domain (network), not in implementation.

not part of any product's build. It consumes the SBOMs the other products' pipelines emit
(Ninja-Remote/ncpeer, NMS/nmsnj, NinjaFlow all appear in its scope maps), scans them for CVEs
and files Jira issues — a downstream *security-scanning* relationship, not a code dependency.
Shares no code with the others; has its own DB/schema and a toolbox submodule.

authentication component: its pam_ninja.so gates SSH reverse-tunnel access into
Ninja infrastructure with a time-based OTP. Shares no code with the other repos; its
blast radius is operational (who can SSH-tunnel into hosts), not code-level. The client OTP
generators and the module must agree on the OTP scheme + shared secret.

Review implications

these are *upstream* of ncpeer (and nccommon/vcpkg also of nmsnj) — weigh the consumer
side: API/ABI compatibility, build impact, and cross-platform reach.

contract.

Also in every review's context

The reviewer instructions CLAUDE.md (rules, house C++ style, output schema); the PR/ticket block pr.md (title, description, linked Jira + acceptance criteria, commits, changed files); the full diff.patch.