OWASP Top 10:2025 compliance

OWASP Top 10:2025 controls and residual risks.

OWASP Top 10:2025 compliance

lindir treats the OWASP Top 10:2025 as a standing requirement. This document maps each risk to controls, residual risks, and ops guidance.

ID Risk Status Controls in lindir Residual risks
A01 Broken Access Control Partial Separate Bearer (admin) vs Host (enrolled machine) vs enroll (len_… one-shot, limited to POST /v1/hosts/enroll); HBAC on client login when rules exist; host routes reject missing/invalid enrollment secret; console WS uses short-lived single-use wsTicket (not the admin Bearer in the URL) Empty HBAC rule set allows all logins. API tokens are effectively all-admin. Fleet scripts/packages/console are intentional root RCE for any Bearer admin. Admin SPA stores token in sessionStorage (XSS → full control).
A02 Security Misconfiguration Partial Bind default 127.0.0.1; refuse weak bootstrapToken unless allowInsecureBootstrap; security response headers (nosniff, X-Frame-Options, Referrer-Policy); TLS via reverse proxy + requireForwardedHttps / trustedProxies No Content-Security-Policy. HTTP cleartext by design. LDAP allowAnonymous defaults true. Listening on 0.0.0.0 warns but does not refuse.
A03 Software Supply Chain Failures Partial Pin Dub deps via dub.selections.json; pin admin UI via package-lock.json; LDC for release builds; update-client uses distro packages Client updates trust the distro package repositories you configure.
A04 Cryptographic Failures Partial PBKDF2-HMAC-SHA256 (~210k iter) for passwords; hashed API/enroll tokens; enrollment secrets random; client config/cache mode 600 TLS not terminated in-process. Host bundle includes password hashes for offline PAM (host secret compromise → hash dump). Enrollment secrets compared as plaintext strings.
A05 Injection Strong Parameterized SQL (?); package name allowlist; typed JSON fields; maxBodyBytes; energy drop-ins from allowlisted enums/ints Fleet queries/scripts and MDM custom compliance scripts execute shell on hosts by design (admin-trusted).
A06 Insecure Design Partial Optional VPN (default null); least privilege host vs admin APIs; password hashes only on Host-auth channel; offline PAM refuses fallback on 401/403 HBAC empty-set allow is easy to leave open. No approval workflow for fleet RCE / energy / logind changes.
A07 Authentication Failures Strong Bootstrap vs API tokens + revoke; rate limiting; account policy (min length, expiry, lockout, must-change); WebAuthn for OIDC; generic client auth errors; enroll tokens (TTL / maxUses / hostname hint) Admin API is long-lived Bearer without MFA/step-up. Compromised admin token = full domain.
A08 Software or Data Integrity Failures Partial Versioned SQL migrations (001–012…); audit log of admin mutations; sudoers written with restrictive mode Client bundle / inventory / energy payloads are auth-protected but not signed. MITM without TLS can rewrite host policy.
A09 Security Logging and Alerting Failures Partial audit_events for auth ok/deny, tokens, fleet (incl. package results with hostname), enroll, account policy, VPN, MDM; searchable Admin Audit (newest-first sort, expandable package-job stdout/stderr) + CLI filters/CSV export; Dashboard denial strip Alerts (webhooks, SIEM, paging) for denial spikes, enroll abuse, or failed package jobs are left to your surrounding stack.
A10 Mishandling of Exceptional Conditions Partial Generic internal when exposeErrorDetails is false; rate limit → 429; offline PAM fail-closed on 401/403 Empty HBAC fails open. Some Exception catches ignore (e.g. early account_policy ensure). Energy/logind restart failure only warns.

Config knobs (server)

Key Default Purpose
bootstrapToken (required strong) Initial admin secret — refuse known-weak values unless allowInsecureBootstrap
allowInsecureBootstrap false Permit known-weak bootstrap tokens (labs only)
rateLimitPerMinute 120 Per-source IP request budget
maxBodyBytes 262144 Max HTTP body size (fleet job output / inventory)
exposeErrorDetails false Include exception text in 500 responses
requireForwardedHttps false Require X-Forwarded-Proto: https (use behind TLS reverse proxy)
trustedProxies [] Optional; when non-empty, only trust forwarded headers from these client IPs

Ops: TLS

The HTTP server speaks cleartext. For production, terminate TLS on Caddy/nginx/Traefik on the Tailscale or LAN interface, set requireForwardedHttps: true, and do not expose the backend port publicly.

Client host hardening

  • /etc/lindir/client.json, cache, and cache-auth.json: mode 600
  • Use online PAM auth when possible; offline fallback only on unreachable/transient errors (not on 401/403)
  • Offline cache TTL (offlineMaxAgeSeconds); password hashes kept out of the main policy cache
  • Run lindir-client sync as root via systemd timer; validate sudoers with visudo -cf
  • Energy / package / script jobs require root sync — treat Bearer admins as highly privileged

Verification

  • Unit tests: dub test
  • Manual: attempt API without token → 401; host API without secret → 401; oversized body → 413; weak bootstrap with defaults → refuse to start; enroll token cannot call non-enroll admin routes → 403