towerops/bugs.md
Graham McIntire 8338d0af1e fix: remaining bugs.md findings — Repo calls, process dict, CSS, rate limits, Oban, debug route
- Extract Repo calls from LiveViews into Agents.get_agent_token_for_org/2
  and Accounts.verify_new_totp_device/2 context functions
- Remove Process dictionary usage for cookie consent; use explicit assigns
- Reject url() references in status page custom CSS changeset
- Add per-organization rate limit check alongside per-IP
- Document Oban flush as emergency-only with audit logging
- Gate /admin/headers behind dev_routes (debug endpoint)
2026-05-11 19:34:18 -05:00

4.7 KiB

Bugs And Risk Findings

Review date: 2026-05-11

Scope: application-owned Elixir/Phoenix code, templates, config, deployment manifests, scripts, and dependency manifests. Vendored code and bundled third-party MIB data were not reviewed as first-party code.

Validation run:

  • mix credo --strict: passed, no issues.
  • mix deps.audit: passed, no vulnerabilities found.
  • mix hex.audit: passed, no retired packages found.
  • npm audit --prefix e2e --audit-level=low: passed, 0 vulnerabilities.

High

  • Category: OWASP A05 Security Misconfiguration / A08 Software and Data Integrity Failures / Availability
  • Evidence: lib/towerops_web/controllers/api/v1/mib_controller.ex:197, lib/towerops_web/controllers/api/v1/mib_controller.ex:244, lib/towerops_web/controllers/api/v1/mib_controller.ex:373
  • Problem: uploaded .tar.gz and .zip files are extracted by external commands into temp storage without limits on archive size, extracted file count, total uncompressed bytes, nesting depth, or per-file size. validate_extracted_paths/1 checks expanded paths after extraction, but does not prevent resource exhaustion during extraction and does not reject symlinks before File.cp_r!/2.
  • Fix: enforce upload body limits, archive entry count limits, max uncompressed bytes, max nesting depth, and reject symlinks/device files before copying. Prefer an archive reader that can inspect entries before writing, or run extraction in a constrained worker/container. Add tests for zip-slip, symlink, and oversized archive rejection.

4. MIB tokenizer creates atoms from untrusted file content

  • Category: OWASP A04 Insecure Design / Availability
  • Evidence: lib/snmpkit/snmp_lib/mib/snmp_tokenizer.ex:621, lib/snmpkit/snmp_lib/mib/snmp_tokenizer.ex:655, lib/snmpkit/snmp_lib/mib/parser.ex:535
  • Problem: uploaded/imported MIB content is converted with String.to_atom/1. Atoms are not garbage collected on the BEAM, so a malicious or simply very large set of unique identifiers can exhaust the VM atom table and crash the node.
  • Fix: keep tokenizer identifiers as strings or use a bounded atom allowlist with String.to_existing_atom/1 only for known reserved words. Add a regression test that parsing many unique identifiers does not grow the atom table.

Medium

6. CSP is duplicated and weakened by inline scripts

  • Category: OWASP A05 Security Misconfiguration / A03 Injection defense in depth
  • Evidence: lib/towerops_web/router.ex:21, lib/towerops_web/plugs/security_headers.ex:20, lib/towerops_web/components/layouts/root.html.heex:35, lib/towerops_web/components/layouts/root.html.heex:71, lib/towerops_web/components/layouts/root.html.heex:80
  • Problem: CSP is set in both the browser pipeline and endpoint-level security plug, with different directives. The effective behavior depends on header overwrite order. The policy also requires 'unsafe-inline' because templates include inline scripts, reducing CSP's value against XSS.
  • Fix: centralize CSP construction in one plug. Move inline scripts into assets/js/app.js or LiveView hooks and remove 'unsafe-inline' where possible. Consider nonces only for unavoidable inline bootstrapping.

11. Nested API helpers fetch child records without organization in the query

  • Category: OWASP A01 Broken Access Control / Performance / Idiomatic Ecto
  • Evidence: lib/towerops_web/controllers/api/v1/schedules_controller.ex:356, lib/towerops_web/controllers/api/v1/schedules_controller.ex:363, lib/towerops_web/controllers/api/v1/schedules_controller.ex:370, lib/towerops_web/controllers/api/v1/escalation_policies_controller.ex:279, lib/towerops_web/controllers/api/v1/escalation_policies_controller.ex:286
  • Problem: child resources are fetched by global ID and then checked against the parent ID in Elixir. The parent is scoped first, so this is not an immediate data leak, but the database does unnecessary global lookups and the pattern is easy to copy into a real authorization bug.
  • Fix: move nested-resource lookups into the OnCall context and query with all available scope predicates in SQL, including organization via joins when possible.

Low

16. MIB vendor listing counts files synchronously on request

  • Category: Performance
  • Evidence: lib/towerops_web/controllers/api/v1/mib_controller.ex:350, lib/towerops_web/controllers/api/v1/mib_controller.ex:373
  • Problem: each GET /admin/api/mibs request recursively walks the whole MIB directory with Path.wildcard("**/*"). This can be expensive with the large MIB tree already present in the repo and can block request processes under load.
  • Fix: cache counts, keep metadata in the database, or compute counts asynchronously.