LibreQoS Security Audit
Use this skill for recurring LibreQoS security audit passes in this repo. It
covers the Rust dependency baseline, network control-plane exposure review,
bridged-interface/eBPF malformed-traffic review, and panic/error-handling/type
loss review, node_manager privacy/auth/XSS review, and final executive-summary
wrap-up. Use additional focused checks for Python, packaging, live-host
configuration, secrets, and
authentication flows outside these scopes.
Scope
- Keep the audit repo-local. Do not install or update global skills.
- Start from the current repo checkout and respect dirty worktree boundaries.
- Write results into the requested audit file. If no file is named, ask before creating one.
- Distinguish security findings from maintenance warnings. "Not maintained" is not a security issue by itself.
- Distinguish reachable vulnerabilities from unused-feature or irrelevant-feature advisories. A vulnerability in a feature LibreQoS does not use is not a security finding by itself.
- Treat an open listener as evidence, not a vulnerability by itself. Record a
security finding only when the listener exposes exploitability, credential or
session leakage, unauthenticated access beyond the intended demo/public mode,
authorization bypass, brute-force exposure, remotely triggerable panic/DoS, or
a control action reachable without the expected protection.
Step 1: Rust Dependency Audit
Run from src/rust/ unless the user asks for a narrower crate:
cargo audit
cargo machete
cargo tree --locked --workspace --depth 1
Use the depth-1 tree only for a quick workspace dependency summary. For every
advisory or suspicious dependency, run an inverse tree query for the exact crate
and version when Cargo can resolve it, such as cargo tree -i rand --locked or
cargo tree -i rand@0.8.5 --locked. If Cargo cannot resolve the package ID,
use cargo audit's dependency tree plus rg searches through manifests and
source files.
If sandboxing blocks Cargo registry or advisory-db access, rerun the blocked command with approval instead of switching to stale data.
Reachability Rubric
Treat an advisory as a security finding when at least one of these is true:
- LibreQoS directly imports the vulnerable crate and calls the affected API.
- A current workspace crate exposes the vulnerable behavior through a LibreQoS
runtime surface:
lqosd node-manager HTTP/websocket handlers, lqos_bus message handling,
lqos_config parsing for network.json / ShapedDevices.csv / config
files, lqos_network_devices runtime access to shaped-device and topology
state, lqos_topology / lqos_topology_compile topology projection,
lqos_probe active probing, lqos_overrides, uisp_integration,
lqos_python, lqos_setup, lqos_netplan_helper, lqos_sys
TC/XDP/eBPF interaction, queue/bakery crates, or support-tool archive/input
handling.
- The advisory applies to default features or enabled features in
Cargo.toml / Cargo.lock, and no code-level usage check is needed for the
vulnerable behavior to be present.
Expected evidence searches include the affected crate name in Cargo.toml
files, affected function/type names in src/rust/**, relevant feature names in
manifests and Cargo.lock, and logger/auth/network/file-parsing entry points
when the advisory depends on runtime conditions.
Treat an advisory as not currently security-relevant when all of these are true:
- The affected crate is present only through an unused optional feature,
build-only path, test-only path, or API surface LibreQoS does not call.
- Feature flags and source search support that conclusion.
- The audit note records what was checked.
If the evidence is incomplete, record it as "reachability unknown" and list the
specific follow-up needed. Do not call it safe.
Triage Rules
For every cargo audit result:
- Record CVE/RUSTSEC/GHSA security advisories as findings only when they meet the reachability rubric above.
- For each security finding, include:
- the advisory ID
- the repo-relative path to the
Cargo.toml that imports the vulnerable crate
- repo-relative source paths that call the affected API or make the dependency reachable, when applicable
- a short vulnerability description
- recommended actions
- Do not list maintenance-only advisories as security findings. Put them in a separate non-security notes section.
- If an advisory depends on specific feature flags, runtime configuration, or API calls, verify those paths with
rg before writing the conclusion.
- If reachability is unclear, use "reachability unknown" and say exactly what was checked and what remains unknown.
For cargo machete results:
- Record unused dependencies separately from security findings.
- Include the repo-relative manifest path and dependency name.
- Recommend removal plus the smallest focused validation command. Use a real
crate name in the audit note, for example
cargo check -p lqos_network_devices.
For cargo tree:
- Use depth-1 output only for a quick workspace summary.
- Use inverse tree queries to identify direct and transitive import paths for findings.
- Keep dependency-surface summaries short and tied to an audit decision.
Audit Section Checklist
The audit-file section must include:
- Heading:
Rust dependency audit.
- Date, scope, and exact commands run.
- Summary bullets covering dependency count, security findings, unused
dependencies, and non-security warnings.
- One subsection per security finding with advisory ID, affected crate, import
paths, source-use paths where applicable, short description, reachability
decision, and recommended actions.
- A separate unused-dependencies subsection for
cargo machete output.
- A separate non-security warnings subsection for unmaintained or informational
advisories.
- No placeholders, no
..., and no unresolved <angle-bracket> tokens.
Step 2: Network Control-Plane Exposure Audit
Use this step when the audit turns to external threats over the LibreQoS control
plane.
Scope Assumptions
- Assume the operator installed the Caddy / SSL / TLS option.
- Treat Linux, Ubuntu, kernel, and distribution package vulnerabilities as out
of scope because LibreQoS cannot fix them in-repo.
- Treat the control interface as in scope. Anything listening or reachable on
the control interface should be reviewed.
- Treat the two bridge interfaces, whether XDP or Linux bridge backed by eBPF,
as out of scope for this section.
- Include the sibling
../../lqos_api/ repo as read-only audit context when it
is present, because the API is exposed behind Caddy.
- Do not edit
../../lqos_api/ unless the user explicitly authorizes
cross-repo changes.
Evidence To Gather
Review these surfaces first:
src/rust/lqos_setup/src/ssl.rs
src/rust/lqos_setup/src/web.rs
src/rust/lqosd/src/node_manager/
docs/v2.0/https-caddy.md
docs/v2.0/api.md
../../lqos_api/src/
../../lqos_api/README.md
Use rg searches for listener addresses, routes, middleware, auth checks,
cookie settings, CORS, Caddy upstreams, and panic-prone request handling:
rg "bind\\(|TcpListener|listen|reverse_proxy|Caddy|CorsLayer|very_permissive|allow_anonymous|SameSite|Cookie|x-bearer|route_layer|unwrap\\(" src/rust docs ../../lqos_api
For each reachable service or route, identify:
- listener address and port
- whether Caddy proxies it and whether the direct port remains reachable
- authentication and authorization mechanism
- unauthenticated routes and whether they expose control, data, or only health/docs
- state-changing routes and their protection
- cookie flags, CORS policy, CSRF/origin checks, and session behavior
- rate limits or backoff for login/API authentication attempts
- request paths where malformed unauthenticated input can panic
Triage Rules
Count these as likely security findings when evidence supports them:
- a direct control-plane HTTP listener bypasses the expected Caddy/TLS path
for authenticated API traffic
- a route that changes state or exposes sensitive operational data is reachable
without the expected auth, except for the explicit public/demo read-only mode
- an auth or request middleware can panic on unauthenticated remote input
- credentialed CORS, cookie flags, or missing CSRF/origin checks let another
browser origin use an operator session
- login or bearer-token checks lack reasonable throttling for a network-exposed
control-plane service
Do not count these as findings by themselves:
- Caddy or LibreQoS listening on a port when the route is protected as intended
- Caddy serving HTTPS for the WebUI/API path
- public API documentation that exposes only endpoint shape and no secret or
state-changing capability
allow_anonymous when the operator intentionally enabled the documented
public/demo read-only mode
- first-run setup exposure when the setup token and lifecycle are being audited
separately, unless the current section finds a concrete bypass
Audit Section Checklist
The audit-file section must include:
- Heading:
Network control-plane audit.
- Date, scope assumptions, and exact files or directories reviewed.
- Summary bullets separating findings from observations.
- One subsection per finding with the repo-relative path, short description,
exposure/threat, and recommended actions.
- A separate observations / not-findings subsection for open listeners or public
docs that are intentional and not vulnerable by themselves.
- No placeholders, no
..., and no unresolved <angle-bracket> tokens.
Step 3: Bridged Interface / eBPF Malformed-Traffic Audit
Use this step when the audit turns to bridged interfaces and the eBPF datapath.
This step is about malformed-packet handling, DoS, map exhaustion, ring-buffer
backpressure, packet-rate debug logging, and userspace handling of eBPF events.
It is not about the control interface.
Review these BPF-specific surfaces first:
src/rust/lqos_sys/src/bpf/lqos_kern.c
src/rust/lqos_sys/src/bpf/common/debug.h
src/rust/lqos_sys/src/bpf/common/dissector.h
src/rust/lqos_sys/src/bpf/common/dissector_tc.h
src/rust/lqos_sys/src/bpf/common/flows.h
src/rust/lqos_sys/src/bpf/common/heimdall.h
src/rust/lqos_sys/src/bpf/common/lpm.h
src/rust/lqos_sys/src/bpf/common/throughput.h
src/rust/lqos_sys/src/bpf/common/maximums.h
src/rust/lqos_sys/src/lqos_kernel.rs
src/rust/lqosd/src/throughput_tracker/
src/rust/lqos_heimdall/src/
Use this search as the starting point:
rg "bpf_debug\\(|frag_off|ihl|tot_len|doff|BPF_MAP_TYPE_HASH|BPF_MAP_TYPE_PERCPU_HASH|BPF_MAP_TYPE_LRU|MAX_FLOWS|MAX_TRACKED_IPS|bpf_ringbuf_output|bpf_probe_read_kernel|data_end|SKB_OVERFLOW|metadata|queue_mapping" src/rust/lqos_sys/src src/rust/lqosd/src/throughput_tracker src/rust/lqos_heimdall/src
For each packet path, identify the concrete behavior for:
- malformed Ethernet, VLAN, PPPoE, MPLS, IPv4, IPv6, TCP, UDP, and ICMP input
- IPv4
ihl, total length, and fragmentation checks before L4 parsing
- IPv6 extension headers and fragments
- bounded-loop limits for stacked headers and TCP options
- unshaped or spoofed traffic creating pinned-map entries
- map type, max entries, LRU behavior, and insert-failure behavior
bpf_trace_printk / bpf_debug calls reachable from bridged traffic
- ring-buffer size checks, backpressure, drop counters, and userspace panics
- metadata paths where malformed packets can become unexpected drops
Count a finding when malformed, spoofed, or high-cardinality traffic can cause
packet-rate expensive work, non-LRU map exhaustion, bogus flow/RTT/retransmit
state, unexpected packet drops, userspace panic, or unreported loss of capture
events. Do not count verifier-enforced memory safety, unknown non-IP traffic
that merely fails open, or untested live reachability claims as findings by
themselves.
Step 4: Panic, Error-Handling, and Type-Loss Audit
Use this step when the audit turns to code paths that can panic, hide errors, or
silently lose data. Include the sibling ../../lqos_api/src/ when it is present
because it is part of the deployed control-plane surface, but do not edit that
repo unless the user explicitly authorizes cross-repo changes.
Start with these searches, then inspect only runtime-reachable code. Exclude
tests, fixtures, generated output, vendored bindings, and historical copies such
as LibreQoS-old.py unless the user explicitly puts them in scope.
rg -n "\\bpanic!\\(|\\.unwrap\\(|\\.expect\\(|unreachable!\\(|todo!\\(|unimplemented!\\(|assert!\\(|from_raw_parts|transmute|unsafe \\{|as (u8|u16|u32|usize|i8|i16|i32|f32)|unwrap_or_default\\(|except Exception|except:|pass$" src/rust src --glob '*.py' ../../lqos_api/src
rg -n "as u32|as u16|as f32|partial_cmp\\(.*\\)\\.unwrap|to_str\\(\\)\\.unwrap|parse\\(\\)\\.unwrap|try_into\\(\\)\\.unwrap" src/rust ../../lqos_api/src
rg -n "except Exception|except:|pass$|sys.exit|int\\(|float\\(" src --glob '*.py' --glob '!LibreQoS-old.py' --glob '!LibreQoS-ancient.py' --glob '!LibreQoS.py.new'
For each candidate, identify:
- file name and exact line number
- whether the code is request-time, packet-time, config/import-time, startup-only,
test-only, or generated/vendor code
- whether an external user, bridged-interface packet, operator-managed file, or
internal telemetry value can trigger the path
- whether the impact is panic/DoS, poisoned shared state, incorrect rejection,
silent fallback, lossy conversion, wrapped counters, non-finite float handling,
or misleading operational data
Count a finding when evidence supports one of these:
- a request, websocket message, packet event, or operator-managed file can panic
a runtime task instead of returning an error
- malformed input can poison or permanently break shared runtime state
- error handling silently continues with a different shaping, auth, or telemetry
result that an operator would not see
- numeric conversion narrows kernel counters, flow counters, bandwidth values, or
timestamps in a way that can wrap, saturate unexpectedly, become non-finite, or
otherwise lose operational data
- an unsafe block reads caller-provided memory without a size check or serializes
uninitialized padding bytes
Do not count these as findings by themselves:
unwrap / expect in tests, examples, benchmarks, one-shot setup validation,
or process startup where failure stops boot cleanly
- unsafe FFI wrappers that validate sizes and keep pointer lifetimes local
- protocol fields that are intentionally narrower when the code checks range or
logs/clamps loss before export
- broad Python exception handling that only preserves backwards-compatible
tolerance and does not change shaping/auth/security behavior
The audit-file section must include:
- Heading:
Panic, error-handling, and type-loss audit.
- Date, scope, and exact searches or files reviewed.
- Summary bullets separating confirmed findings, reachability-unknown items, and
observations/not-findings.
- One subsection per finding with the repo-relative
path:line, short
description, exposure/threat, and recommended actions.
- No placeholders, no
..., and no unresolved <angle-bracket> tokens.
Step 5: Node Manager Privacy, Auth, and XSS Audit
Use this step when reviewing node_manager for missing anonymization of PII,
missing authentication or authorization on data access, browser-storage exposure,
and XSS. Review source files, not generated bundles, unless a generated artifact
is the only shipped source for that behavior.
Start with these surfaces:
src/rust/lqosd/src/node_manager/run.rs
src/rust/lqosd/src/node_manager/static_pages.rs
src/rust/lqosd/src/node_manager/auth.rs
src/rust/lqosd/src/node_manager/ws.rs
src/rust/lqosd/src/node_manager/ws/messages.rs
src/rust/lqosd/src/node_manager/local_api/
src/rust/lqosd/src/node_manager/js_build/src/
src/rust/lqosd/src/node_manager/static2/template.html
docs/v2.0/node-manager-ui.md
docs/v2.0/components.md
Use these searches as a starting point:
rg -n "localStorage|sessionStorage|document\\.cookie|innerHTML|outerHTML|insertAdjacentHTML|eval\\(|Function\\(|onclick=|onerror=|sanitize|DOMPurify|redact|redaction|redactable|allow_anonymous|auth_layer|route_layer|LoginResult|ReadOnly|Admin|Denied" src/rust/lqosd/src/node_manager docs/v2.0
rg -n "innerHTML\\s*=.*(\\+|`)|simpleRowHtml\\(|href=.*\\+|data-[^=]+=|textContent|innerText" src/rust/lqosd/src/node_manager/js_build/src --glob '*.js'
rg -n "ShapedDevice|network_json|CircuitById|AllShapedDevices|NetworkJson|Search|UnknownIps|CircuitDirectory|device_name|circuit_name|mac|ipv4|ipv6|comment" src/rust/lqosd/src/node_manager
For each candidate, identify:
- the exact
path:line
- whether access is unauthenticated, anonymous read-only, authenticated read-only,
or admin-only
- whether the exposed data includes subscriber/customer identifiers, circuit
names/IDs, device names/IDs, IPs, MACs, comments, topology names, tickets, or
integration secrets
- whether redaction happens server-side, in the transport payload, or only in the
browser display
- whether browser storage persists credentials, session tokens, topology drafts,
dashboard layouts, interface names, VLANs, or other operational data
- whether untrusted strings are inserted with
innerHTML, HTML tooltips,
attributes, inline handlers, or URLs without escaping and protocol validation
Count a finding when evidence supports one of these:
- anonymous/demo/read-only access can retrieve raw PII or sensitive operational
data with no server-side anonymization
- a route, websocket request, local API, static fallback, or file-serving path
exposes data without the expected auth boundary
- state-changing websocket/local API behavior is available to read-only or
anonymous users without a documented reason
- operator/customer/integration-controlled strings can reach
innerHTML or HTML
attributes without escaping
- an XSS would expose a session token, API key, config secret, localStorage value,
or pending control-plane operation
- localStorage retains sensitive topology/configuration data beyond the browser
session or logout without a clear need
Do not count these as findings by themselves:
- static JS/CSS/images served without auth when they contain no operator data,
credentials, or secrets
- client-side redaction that is documented as screenshot/demo display redaction,
unless the same mode is used as the privacy boundary for anonymous/public
access
- admin-only config views that already redact integration secrets before sending
them to the browser
innerHTML used only for fixed icons, fixed Bootstrap markup, or escaped values
The audit-file section must include:
- Heading:
Node Manager privacy, auth, and XSS audit.
- Date, scope, exact searches or files reviewed, and any excluded generated/vendor
output.
- Summary bullets separating confirmed findings, hardening observations, and
not-findings.
- One subsection per finding with repo-relative
path:line, short description,
exposure/threat, and recommended actions.
- A short localStorage/sessionStorage/cookie note, even when no sensitive
localStorage token is found.
- No placeholders, no
..., and no unresolved <angle-bracket> tokens.
Step 6: Audit Introduction, Executive Summary, Conclusion, and Grade
Use this step after the requested audit sections have enough findings to
summarize. Keep the write-up brief enough for release planning while preserving
the concrete evidence in the detailed sections below.
At the beginning of the audit file, add:
Introduction: the audit date, overall scope, what was static-only, and major
exclusions such as Linux/Ubuntu vulnerabilities, live firewall state, or
deployment-specific controls not visible in the checkout.
Executive summary: bullet points that signal the actionable issues found
later in the document. Include the highest-risk findings, non-findings that
matter to release confidence, and the recommended fix priority.
- An overall letter grade from
A to F. Use the grade as a concise release
posture signal, not as a substitute for findings.
At the end of the audit file, add:
Conclusion: restate the overall grade and explain the security posture in a
balanced way.
- Call out what LibreQoS is doing right, such as dependency health, auth layers,
TLS/Caddy direction, redaction of secrets, verifier-conscious eBPF bounds
checks, or any other strengths actually found in the detailed sections.
- Call out what needs work, prioritizing concrete risk over volume of findings.
- Keep recommendations aligned with the detailed findings. Do not introduce new
unsupported claims in the conclusion.
Suggested grading rubric:
A: No confirmed high-impact release issues, strong auth/session boundaries,
validated input handling, and only minor hardening items remain.
B: Good foundations with a few bounded, actionable hardening issues.
C: Acceptable foundations, but multiple actionable issues remain in
control-plane, WebUI, privacy, panic/DoS, or observability paths.
D: Security-critical issues are likely reachable or broad auth/privacy
boundaries are missing.
F: Known unauthenticated compromise, widespread secret exposure, or a
release-blocking vulnerability with no mitigation.
The introduction, executive summary, and conclusion must not contain
placeholders, speculative certainty, or findings that are absent from the body of
the audit.
Validation
- Re-read the audit section before returning; remove placeholders and vague conclusions.
- After changing any repo file, run the repo's anti-slop review path and fix non-zero slop before finishing.
1---2name: libreqos-security-audit3description: Repo-local LibreQoS workflow for release security audit passes. Use when auditing LibreQoS with cargo audit, cargo machete, cargo tree, CVE triage, network control-plane exposure review, bridged-interface/eBPF malformed-traffic review, panic/error-handling/type-loss review, node_manager privacy/auth/XSS review, executive-summary/conclusion grading, and audit-file findings updates.4---56# LibreQoS Security Audit78Use this skill for recurring LibreQoS security audit passes in this repo. It9covers the Rust dependency baseline, network control-plane exposure review,10bridged-interface/eBPF malformed-traffic review, and panic/error-handling/type11loss review, node_manager privacy/auth/XSS review, and final executive-summary12wrap-up. Use additional focused checks for Python, packaging, live-host13configuration, secrets, and14authentication flows outside these scopes.1516## Scope1718- Keep the audit repo-local. Do not install or update global skills.19- Start from the current repo checkout and respect dirty worktree boundaries.20- Write results into the requested audit file. If no file is named, ask before creating one.21- Distinguish security findings from maintenance warnings. "Not maintained" is not a security issue by itself.22- Distinguish reachable vulnerabilities from unused-feature or irrelevant-feature advisories. A vulnerability in a feature LibreQoS does not use is not a security finding by itself.23- Treat an open listener as evidence, not a vulnerability by itself. Record a24 security finding only when the listener exposes exploitability, credential or25 session leakage, unauthenticated access beyond the intended demo/public mode,26 authorization bypass, brute-force exposure, remotely triggerable panic/DoS, or27 a control action reachable without the expected protection.2829## Step 1: Rust Dependency Audit3031Run from `src/rust/` unless the user asks for a narrower crate:3233```text34cargo audit35cargo machete36cargo tree --locked --workspace --depth 137```3839Use the depth-1 tree only for a quick workspace dependency summary. For every40advisory or suspicious dependency, run an inverse tree query for the exact crate41and version when Cargo can resolve it, such as `cargo tree -i rand --locked` or42`cargo tree -i rand@0.8.5 --locked`. If Cargo cannot resolve the package ID,43use `cargo audit`'s dependency tree plus `rg` searches through manifests and44source files.4546If sandboxing blocks Cargo registry or advisory-db access, rerun the blocked command with approval instead of switching to stale data.4748### Reachability Rubric4950Treat an advisory as a security finding when at least one of these is true:5152- LibreQoS directly imports the vulnerable crate and calls the affected API.53- A current workspace crate exposes the vulnerable behavior through a LibreQoS54 runtime surface:55 `lqosd` node-manager HTTP/websocket handlers, `lqos_bus` message handling,56 `lqos_config` parsing for `network.json` / `ShapedDevices.csv` / config57 files, `lqos_network_devices` runtime access to shaped-device and topology58 state, `lqos_topology` / `lqos_topology_compile` topology projection,59 `lqos_probe` active probing, `lqos_overrides`, `uisp_integration`,60 `lqos_python`, `lqos_setup`, `lqos_netplan_helper`, `lqos_sys`61 TC/XDP/eBPF interaction, queue/bakery crates, or support-tool archive/input62 handling.63- The advisory applies to default features or enabled features in64 `Cargo.toml` / `Cargo.lock`, and no code-level usage check is needed for the65 vulnerable behavior to be present.6667Expected evidence searches include the affected crate name in `Cargo.toml`68files, affected function/type names in `src/rust/**`, relevant feature names in69manifests and `Cargo.lock`, and logger/auth/network/file-parsing entry points70when the advisory depends on runtime conditions.7172Treat an advisory as not currently security-relevant when all of these are true:7374- The affected crate is present only through an unused optional feature,75 build-only path, test-only path, or API surface LibreQoS does not call.76- Feature flags and source search support that conclusion.77- The audit note records what was checked.7879If the evidence is incomplete, record it as "reachability unknown" and list the80specific follow-up needed. Do not call it safe.8182### Triage Rules8384For every `cargo audit` result:8586- Record CVE/RUSTSEC/GHSA security advisories as findings only when they meet the reachability rubric above.87- For each security finding, include:88 - the advisory ID89 - the repo-relative path to the `Cargo.toml` that imports the vulnerable crate90 - repo-relative source paths that call the affected API or make the dependency reachable, when applicable91 - a short vulnerability description92 - recommended actions93- Do not list maintenance-only advisories as security findings. Put them in a separate non-security notes section.94- If an advisory depends on specific feature flags, runtime configuration, or API calls, verify those paths with `rg` before writing the conclusion.95- If reachability is unclear, use "reachability unknown" and say exactly what was checked and what remains unknown.9697For `cargo machete` results:9899- Record unused dependencies separately from security findings.100- Include the repo-relative manifest path and dependency name.101- Recommend removal plus the smallest focused validation command. Use a real102 crate name in the audit note, for example `cargo check -p103 lqos_network_devices`.104105For `cargo tree`:106107- Use depth-1 output only for a quick workspace summary.108- Use inverse tree queries to identify direct and transitive import paths for findings.109- Keep dependency-surface summaries short and tied to an audit decision.110111### Audit Section Checklist112113The audit-file section must include:114115- Heading: `Rust dependency audit`.116- Date, scope, and exact commands run.117- Summary bullets covering dependency count, security findings, unused118 dependencies, and non-security warnings.119- One subsection per security finding with advisory ID, affected crate, import120 paths, source-use paths where applicable, short description, reachability121 decision, and recommended actions.122- A separate unused-dependencies subsection for `cargo machete` output.123- A separate non-security warnings subsection for unmaintained or informational124 advisories.125- No placeholders, no `...`, and no unresolved `<angle-bracket>` tokens.126127## Step 2: Network Control-Plane Exposure Audit128129Use this step when the audit turns to external threats over the LibreQoS control130plane.131132### Scope Assumptions133134- Assume the operator installed the Caddy / SSL / TLS option.135- Treat Linux, Ubuntu, kernel, and distribution package vulnerabilities as out136 of scope because LibreQoS cannot fix them in-repo.137- Treat the control interface as in scope. Anything listening or reachable on138 the control interface should be reviewed.139- Treat the two bridge interfaces, whether XDP or Linux bridge backed by eBPF,140 as out of scope for this section.141- Include the sibling `../../lqos_api/` repo as read-only audit context when it142 is present, because the API is exposed behind Caddy.143- Do not edit `../../lqos_api/` unless the user explicitly authorizes144 cross-repo changes.145146### Evidence To Gather147148Review these surfaces first:149150```text151src/rust/lqos_setup/src/ssl.rs152src/rust/lqos_setup/src/web.rs153src/rust/lqosd/src/node_manager/154docs/v2.0/https-caddy.md155docs/v2.0/api.md156../../lqos_api/src/157../../lqos_api/README.md158```159160Use `rg` searches for listener addresses, routes, middleware, auth checks,161cookie settings, CORS, Caddy upstreams, and panic-prone request handling:162163```text164rg "bind\\(|TcpListener|listen|reverse_proxy|Caddy|CorsLayer|very_permissive|allow_anonymous|SameSite|Cookie|x-bearer|route_layer|unwrap\\(" src/rust docs ../../lqos_api165```166167For each reachable service or route, identify:168169- listener address and port170- whether Caddy proxies it and whether the direct port remains reachable171- authentication and authorization mechanism172- unauthenticated routes and whether they expose control, data, or only health/docs173- state-changing routes and their protection174- cookie flags, CORS policy, CSRF/origin checks, and session behavior175- rate limits or backoff for login/API authentication attempts176- request paths where malformed unauthenticated input can panic177178### Triage Rules179180Count these as likely security findings when evidence supports them:181182- a direct control-plane HTTP listener bypasses the expected Caddy/TLS path183 for authenticated API traffic184- a route that changes state or exposes sensitive operational data is reachable185 without the expected auth, except for the explicit public/demo read-only mode186- an auth or request middleware can panic on unauthenticated remote input187- credentialed CORS, cookie flags, or missing CSRF/origin checks let another188 browser origin use an operator session189- login or bearer-token checks lack reasonable throttling for a network-exposed190 control-plane service191192Do not count these as findings by themselves:193194- Caddy or LibreQoS listening on a port when the route is protected as intended195- Caddy serving HTTPS for the WebUI/API path196- public API documentation that exposes only endpoint shape and no secret or197 state-changing capability198- `allow_anonymous` when the operator intentionally enabled the documented199 public/demo read-only mode200- first-run setup exposure when the setup token and lifecycle are being audited201 separately, unless the current section finds a concrete bypass202203### Audit Section Checklist204205The audit-file section must include:206207- Heading: `Network control-plane audit`.208- Date, scope assumptions, and exact files or directories reviewed.209- Summary bullets separating findings from observations.210- One subsection per finding with the repo-relative path, short description,211 exposure/threat, and recommended actions.212- A separate observations / not-findings subsection for open listeners or public213 docs that are intentional and not vulnerable by themselves.214- No placeholders, no `...`, and no unresolved `<angle-bracket>` tokens.215216## Step 3: Bridged Interface / eBPF Malformed-Traffic Audit217218Use this step when the audit turns to bridged interfaces and the eBPF datapath.219This step is about malformed-packet handling, DoS, map exhaustion, ring-buffer220backpressure, packet-rate debug logging, and userspace handling of eBPF events.221It is not about the control interface.222223Review these BPF-specific surfaces first:224225```text226src/rust/lqos_sys/src/bpf/lqos_kern.c227src/rust/lqos_sys/src/bpf/common/debug.h228src/rust/lqos_sys/src/bpf/common/dissector.h229src/rust/lqos_sys/src/bpf/common/dissector_tc.h230src/rust/lqos_sys/src/bpf/common/flows.h231src/rust/lqos_sys/src/bpf/common/heimdall.h232src/rust/lqos_sys/src/bpf/common/lpm.h233src/rust/lqos_sys/src/bpf/common/throughput.h234src/rust/lqos_sys/src/bpf/common/maximums.h235src/rust/lqos_sys/src/lqos_kernel.rs236src/rust/lqosd/src/throughput_tracker/237src/rust/lqos_heimdall/src/238```239240Use this search as the starting point:241242```text243rg "bpf_debug\\(|frag_off|ihl|tot_len|doff|BPF_MAP_TYPE_HASH|BPF_MAP_TYPE_PERCPU_HASH|BPF_MAP_TYPE_LRU|MAX_FLOWS|MAX_TRACKED_IPS|bpf_ringbuf_output|bpf_probe_read_kernel|data_end|SKB_OVERFLOW|metadata|queue_mapping" src/rust/lqos_sys/src src/rust/lqosd/src/throughput_tracker src/rust/lqos_heimdall/src244```245246For each packet path, identify the concrete behavior for:247248- malformed Ethernet, VLAN, PPPoE, MPLS, IPv4, IPv6, TCP, UDP, and ICMP input249- IPv4 `ihl`, total length, and fragmentation checks before L4 parsing250- IPv6 extension headers and fragments251- bounded-loop limits for stacked headers and TCP options252- unshaped or spoofed traffic creating pinned-map entries253- map type, max entries, LRU behavior, and insert-failure behavior254- `bpf_trace_printk` / `bpf_debug` calls reachable from bridged traffic255- ring-buffer size checks, backpressure, drop counters, and userspace panics256- metadata paths where malformed packets can become unexpected drops257258Count a finding when malformed, spoofed, or high-cardinality traffic can cause259packet-rate expensive work, non-LRU map exhaustion, bogus flow/RTT/retransmit260state, unexpected packet drops, userspace panic, or unreported loss of capture261events. Do not count verifier-enforced memory safety, unknown non-IP traffic262that merely fails open, or untested live reachability claims as findings by263themselves.264265## Step 4: Panic, Error-Handling, and Type-Loss Audit266267Use this step when the audit turns to code paths that can panic, hide errors, or268silently lose data. Include the sibling `../../lqos_api/src/` when it is present269because it is part of the deployed control-plane surface, but do not edit that270repo unless the user explicitly authorizes cross-repo changes.271272Start with these searches, then inspect only runtime-reachable code. Exclude273tests, fixtures, generated output, vendored bindings, and historical copies such274as `LibreQoS-old.py` unless the user explicitly puts them in scope.275276```text277rg -n "\\bpanic!\\(|\\.unwrap\\(|\\.expect\\(|unreachable!\\(|todo!\\(|unimplemented!\\(|assert!\\(|from_raw_parts|transmute|unsafe \\{|as (u8|u16|u32|usize|i8|i16|i32|f32)|unwrap_or_default\\(|except Exception|except:|pass$" src/rust src --glob '*.py' ../../lqos_api/src278rg -n "as u32|as u16|as f32|partial_cmp\\(.*\\)\\.unwrap|to_str\\(\\)\\.unwrap|parse\\(\\)\\.unwrap|try_into\\(\\)\\.unwrap" src/rust ../../lqos_api/src279rg -n "except Exception|except:|pass$|sys.exit|int\\(|float\\(" src --glob '*.py' --glob '!LibreQoS-old.py' --glob '!LibreQoS-ancient.py' --glob '!LibreQoS.py.new'280```281282For each candidate, identify:283284- file name and exact line number285- whether the code is request-time, packet-time, config/import-time, startup-only,286 test-only, or generated/vendor code287- whether an external user, bridged-interface packet, operator-managed file, or288 internal telemetry value can trigger the path289- whether the impact is panic/DoS, poisoned shared state, incorrect rejection,290 silent fallback, lossy conversion, wrapped counters, non-finite float handling,291 or misleading operational data292293Count a finding when evidence supports one of these:294295- a request, websocket message, packet event, or operator-managed file can panic296 a runtime task instead of returning an error297- malformed input can poison or permanently break shared runtime state298- error handling silently continues with a different shaping, auth, or telemetry299 result that an operator would not see300- numeric conversion narrows kernel counters, flow counters, bandwidth values, or301 timestamps in a way that can wrap, saturate unexpectedly, become non-finite, or302 otherwise lose operational data303- an unsafe block reads caller-provided memory without a size check or serializes304 uninitialized padding bytes305306Do not count these as findings by themselves:307308- `unwrap` / `expect` in tests, examples, benchmarks, one-shot setup validation,309 or process startup where failure stops boot cleanly310- unsafe FFI wrappers that validate sizes and keep pointer lifetimes local311- protocol fields that are intentionally narrower when the code checks range or312 logs/clamps loss before export313- broad Python exception handling that only preserves backwards-compatible314 tolerance and does not change shaping/auth/security behavior315316The audit-file section must include:317318- Heading: `Panic, error-handling, and type-loss audit`.319- Date, scope, and exact searches or files reviewed.320- Summary bullets separating confirmed findings, reachability-unknown items, and321 observations/not-findings.322- One subsection per finding with the repo-relative `path:line`, short323 description, exposure/threat, and recommended actions.324- No placeholders, no `...`, and no unresolved `<angle-bracket>` tokens.325326## Step 5: Node Manager Privacy, Auth, and XSS Audit327328Use this step when reviewing node_manager for missing anonymization of PII,329missing authentication or authorization on data access, browser-storage exposure,330and XSS. Review source files, not generated bundles, unless a generated artifact331is the only shipped source for that behavior.332333Start with these surfaces:334335```text336src/rust/lqosd/src/node_manager/run.rs337src/rust/lqosd/src/node_manager/static_pages.rs338src/rust/lqosd/src/node_manager/auth.rs339src/rust/lqosd/src/node_manager/ws.rs340src/rust/lqosd/src/node_manager/ws/messages.rs341src/rust/lqosd/src/node_manager/local_api/342src/rust/lqosd/src/node_manager/js_build/src/343src/rust/lqosd/src/node_manager/static2/template.html344docs/v2.0/node-manager-ui.md345docs/v2.0/components.md346```347348Use these searches as a starting point:349350```text351rg -n "localStorage|sessionStorage|document\\.cookie|innerHTML|outerHTML|insertAdjacentHTML|eval\\(|Function\\(|onclick=|onerror=|sanitize|DOMPurify|redact|redaction|redactable|allow_anonymous|auth_layer|route_layer|LoginResult|ReadOnly|Admin|Denied" src/rust/lqosd/src/node_manager docs/v2.0352rg -n "innerHTML\\s*=.*(\\+|`)|simpleRowHtml\\(|href=.*\\+|data-[^=]+=|textContent|innerText" src/rust/lqosd/src/node_manager/js_build/src --glob '*.js'353rg -n "ShapedDevice|network_json|CircuitById|AllShapedDevices|NetworkJson|Search|UnknownIps|CircuitDirectory|device_name|circuit_name|mac|ipv4|ipv6|comment" src/rust/lqosd/src/node_manager354```355356For each candidate, identify:357358- the exact `path:line`359- whether access is unauthenticated, anonymous read-only, authenticated read-only,360 or admin-only361- whether the exposed data includes subscriber/customer identifiers, circuit362 names/IDs, device names/IDs, IPs, MACs, comments, topology names, tickets, or363 integration secrets364- whether redaction happens server-side, in the transport payload, or only in the365 browser display366- whether browser storage persists credentials, session tokens, topology drafts,367 dashboard layouts, interface names, VLANs, or other operational data368- whether untrusted strings are inserted with `innerHTML`, HTML tooltips,369 attributes, inline handlers, or URLs without escaping and protocol validation370371Count a finding when evidence supports one of these:372373- anonymous/demo/read-only access can retrieve raw PII or sensitive operational374 data with no server-side anonymization375- a route, websocket request, local API, static fallback, or file-serving path376 exposes data without the expected auth boundary377- state-changing websocket/local API behavior is available to read-only or378 anonymous users without a documented reason379- operator/customer/integration-controlled strings can reach `innerHTML` or HTML380 attributes without escaping381- an XSS would expose a session token, API key, config secret, localStorage value,382 or pending control-plane operation383- localStorage retains sensitive topology/configuration data beyond the browser384 session or logout without a clear need385386Do not count these as findings by themselves:387388- static JS/CSS/images served without auth when they contain no operator data,389 credentials, or secrets390- client-side redaction that is documented as screenshot/demo display redaction,391 unless the same mode is used as the privacy boundary for anonymous/public392 access393- admin-only config views that already redact integration secrets before sending394 them to the browser395- `innerHTML` used only for fixed icons, fixed Bootstrap markup, or escaped values396397The audit-file section must include:398399- Heading: `Node Manager privacy, auth, and XSS audit`.400- Date, scope, exact searches or files reviewed, and any excluded generated/vendor401 output.402- Summary bullets separating confirmed findings, hardening observations, and403 not-findings.404- One subsection per finding with repo-relative `path:line`, short description,405 exposure/threat, and recommended actions.406- A short localStorage/sessionStorage/cookie note, even when no sensitive407 localStorage token is found.408- No placeholders, no `...`, and no unresolved `<angle-bracket>` tokens.409410## Step 6: Audit Introduction, Executive Summary, Conclusion, and Grade411412Use this step after the requested audit sections have enough findings to413summarize. Keep the write-up brief enough for release planning while preserving414the concrete evidence in the detailed sections below.415416At the beginning of the audit file, add:417418- `Introduction`: the audit date, overall scope, what was static-only, and major419 exclusions such as Linux/Ubuntu vulnerabilities, live firewall state, or420 deployment-specific controls not visible in the checkout.421- `Executive summary`: bullet points that signal the actionable issues found422 later in the document. Include the highest-risk findings, non-findings that423 matter to release confidence, and the recommended fix priority.424- An overall letter grade from `A` to `F`. Use the grade as a concise release425 posture signal, not as a substitute for findings.426427At the end of the audit file, add:428429- `Conclusion`: restate the overall grade and explain the security posture in a430 balanced way.431- Call out what LibreQoS is doing right, such as dependency health, auth layers,432 TLS/Caddy direction, redaction of secrets, verifier-conscious eBPF bounds433 checks, or any other strengths actually found in the detailed sections.434- Call out what needs work, prioritizing concrete risk over volume of findings.435- Keep recommendations aligned with the detailed findings. Do not introduce new436 unsupported claims in the conclusion.437438Suggested grading rubric:439440- `A`: No confirmed high-impact release issues, strong auth/session boundaries,441 validated input handling, and only minor hardening items remain.442- `B`: Good foundations with a few bounded, actionable hardening issues.443- `C`: Acceptable foundations, but multiple actionable issues remain in444 control-plane, WebUI, privacy, panic/DoS, or observability paths.445- `D`: Security-critical issues are likely reachable or broad auth/privacy446 boundaries are missing.447- `F`: Known unauthenticated compromise, widespread secret exposure, or a448 release-blocking vulnerability with no mitigation.449450The introduction, executive summary, and conclusion must not contain451placeholders, speculative certainty, or findings that are absent from the body of452the audit.453454## Validation455456- Re-read the audit section before returning; remove placeholders and vague conclusions.457- After changing any repo file, run the repo's anti-slop review path and fix non-zero slop before finishing.