Write custom security checks
The 70+ services Noxen flags out of the box cover the common
homelab fleet. For in-house admin UIs, vendor-specific tools, or
anything not yet in AdminSurfaceCatalog, drop a JSON
file into the custom-checks directory and Noxen runs it on every
scan.
Where checks live
~/Library/Application Support/app.noxen/custom-checks/
Every *.json file in that directory is parsed as a
single check on Noxen launch and on demand via
Scan → Reload custom checks
(⌘⇧U). Subdirectories are searched recursively, so
you can group checks by team / service / client.
HTTP check schema
{
"id": "internal-grafana-no-auth",
"category": "exposedAdmin",
"severity": "high",
"title": "Internal Grafana with anonymous viewer",
"kind": "http",
"ports": [3000, 3001],
"paths": ["/api/health", "/login"],
"markers": [
{ "type": "header_present", "header": "Set-Cookie", "value": "grafana_session" },
{ "type": "body_contains", "value": "Anonymous Org" }
],
"remediation": "Set [auth.anonymous] enabled = false in grafana.ini and restart."
}
Field reference:
id- Unique string. Used in the finding identifier so duplicates from re-scans are deduplicated correctly.
category- One of
exposedAdmin,missingHeader,weakTLS,customCheck(the catch-all). Drives which UI tab the finding shows up in. severitycritical|high|medium|low|info.title- Human-readable finding title.
kind"http"for HTTP/HTTPS GET probes;"tcp"for raw-socket banner / send-and-match probes (see below).ports- Array of TCP port numbers to probe. Skipped if the host's port scan didn't find any of these open.
paths- Array of HTTP paths to GET. The first path that returns
2xx and matches every
markertriggers the finding. markers- Array of conditions, AND-ed. All must match for the finding
to fire. Marker types:
{"type":"status","value":200}— exact HTTP status code.{"type":"body_contains","value":"— case-insensitive substring match in response body."} {"type":"body_regex","pattern":"— Swift NSRegularExpression in response body."} {"type":"header_present","header":"X-Foo"}— header exists at all.{"type":"header_value","header":"Server","value":"nginx"}— exact header value match.{"type":"header_contains","header":"Set-Cookie","value":"grafana_session"}— substring match in header.
remediation- One-paragraph guidance shown in the finding. Use the same voice as the built-in remediation — concrete, command-level if possible.
TCP check schema
For non-HTTP services where you want to detect a banner or response from a specific send. Example: a custom Erlang admin port that responds with a known string:
{
"id": "erlang-cookie-disabled",
"category": "exposedAdmin",
"severity": "critical",
"title": "Erlang distribution port without cookie auth",
"kind": "tcp",
"ports": [4369, 25672],
"send_hex": "0000",
"expect_contains": "epmd_protocol",
"remediation": "Set ERL_AFLAGS to require cookie auth and bind to localhost."
}
send_hex(optional)- Hex-encoded bytes to send before reading. Omit for a pure banner-grab probe (just connect, read, match).
expect_contains- Substring expected in the response. Case-sensitive.
expect_regex(optional alternative)- Regex match against the response. Use this when the banner format varies by version.
Reload after editing
Scan → Reload custom checks in the menu bar, or ⌘⇧U. Noxen re-parses every JSON file, surfaces validation errors as banner notifications (with file path + line / field hint), and updates the in-memory check registry. Re-scans use the new checks immediately.
Validation errors
A malformed JSON file does NOT abort other checks — Noxen logs the parse error, skips that file, and continues. The Reload action surfaces the error as a banner with the offending file path so you can fix and reload again. Common errors:
- Missing required field —
id,kind,severityare required. - Unknown
kind— onlyhttpandtcpare supported in v1.0. - Invalid regex — Noxen uses Swift's
NSRegularExpression; PCRE-only constructs (lookbehind, etc.) won't compile. - Out-of-range port — must be 1–65535.
Sharing checks
The custom-checks directory is just JSON files — version-control
it, share via Git, drop into MSP-managed installs. Future
Noxen versions may ship a "community check pack" auto-installer;
for now, cp -r your-checks/ ~/Library/Application\ Support/app.noxen/custom-checks/
is enough.