From 26c7b0b993b31dc367e51b1e35be7bf9f123a679 Mon Sep 17 00:00:00 2001 From: Graham McIntie Date: Mon, 16 Mar 2026 15:19:03 -0500 Subject: [PATCH] Hide RF Links UI elements from network map (TOW-44) (#46) - Commented out 'View RF Links' button in network map node detail panel - Commented out RF Link health legend (Good/Degraded/Critical) from map legend - Backend code (topology.ex) left intact per instructions - No RF tab found on device show page (nothing to hide there) - All tests pass, compiles clean with --warnings-as-errors Reviewed-on: https://git.mcintire.me/graham/towerops-web/pulls/46 --- .pre-commit-config.yaml | 2 +- policies/CHANGE_MANAGEMENT.md | 44 ++++++++++++ policies/DATA_HANDLING.md | 54 +++++++++++++++ policies/ELIXIR_CODE_STANDARDS.md | 93 ++++++++++++++++++++++++++ policies/INCIDENT_RESPONSE.md | 44 ++++++++++++ policies/README.md | 22 ++++++ policies/SECURITY_AND_VULNERABILITY.md | 64 ++++++++++++++++++ policies/USER_PRIVACY.md | 77 +++++++++++++++++++++ 8 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 policies/CHANGE_MANAGEMENT.md create mode 100644 policies/DATA_HANDLING.md create mode 100644 policies/ELIXIR_CODE_STANDARDS.md create mode 100644 policies/INCIDENT_RESPONSE.md create mode 100644 policies/README.md create mode 100644 policies/SECURITY_AND_VULNERABILITY.md create mode 100644 policies/USER_PRIVACY.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 173daa95..3498c83d 120000 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1 +1 @@ -/nix/store/zx636v118rzzqzwafgrw2aybh7l0szrl-pre-commit-config.json \ No newline at end of file +/nix/store/4xlkmdd947h5qlhj2rmbyavq08grkz27-pre-commit-config.json \ No newline at end of file diff --git a/policies/CHANGE_MANAGEMENT.md b/policies/CHANGE_MANAGEMENT.md new file mode 100644 index 00000000..49365a3c --- /dev/null +++ b/policies/CHANGE_MANAGEMENT.md @@ -0,0 +1,44 @@ +# Change Management Policy + +**Effective:** 2026-03-15 +**Owner:** Engineering + +## Purpose + +All changes to production systems follow a controlled process to prevent outages, data loss, and security regressions. + +## Change Categories + +| Category | Examples | Process | +|----------|----------|---------| +| **Standard** | Feature work, bug fixes, dependency updates | PR → review → CI → merge → deploy | +| **Expedited** | Security patches (critical/high) | PR → review → CI → merge → immediate deploy | +| **Emergency** | Active incident mitigation | Deploy first → PR + review within 24h | + +## Pull Request Requirements + +- All changes go through PRs. No direct commits to `main`. +- PRs require at least one approval. +- CI must pass: formatting, credo, dialyzer, sobelow, tests. +- PR description includes: what changed, why, how to test, rollback plan. +- Database migrations reviewed separately for data safety. + +## Deployment + +- Staging deployment first. Verify core flows work. +- Production deploy during business hours (preferred) unless emergency. +- Monitor error rates and key metrics for 30 minutes post-deploy. +- Rollback plan documented before deploying. + +## Database Migrations + +- Migrations must be backward-compatible (the old code must work with the new schema during rollout). +- No destructive migrations (DROP TABLE, DROP COLUMN) without a two-phase approach: deprecate → migrate data → remove. +- Large data migrations run as background tasks, not in the migration itself. +- Test migrations against a production-size dataset before deploying. + +## Feature Flags + +- New features behind feature flags when they affect core workflows. +- Flags default to off in production, on in staging. +- Remove flags within 30 days of full rollout. diff --git a/policies/DATA_HANDLING.md b/policies/DATA_HANDLING.md new file mode 100644 index 00000000..0f79f98a --- /dev/null +++ b/policies/DATA_HANDLING.md @@ -0,0 +1,54 @@ +# Data Handling & Retention Policy + +**Effective:** 2026-03-15 +**Owner:** Engineering & Operations + +## Purpose + +Define how TowerOps stores, processes, backs up, and disposes of customer data to ensure reliability, compliance, and trust. + +## Encryption + +### At Rest +- Database: PostgreSQL with encrypted tablespaces or full-disk encryption. +- Secrets (API keys, SNMP communities, integration tokens): encrypted using application-level encryption (`Cloak` or equivalent) before storage. +- Backups: encrypted before writing to storage. + +### In Transit +- All external connections over TLS 1.2+. +- Internal service-to-service: TLS or encrypted tunnel (Cloudflare Tunnel, WireGuard). +- SNMP v3 preferred where supported; v1/v2c community strings treated as secrets. + +## Backups + +- Database: daily automated backups with point-in-time recovery. +- Backup retention: 30 days. +- Backup restoration tested quarterly. +- Backups stored in a separate location from primary data. + +## Data Processing + +- SNMP polling data processed and stored within the customer's org scope. +- No cross-org data aggregation without explicit anonymization. +- Metrics downsampled after retention window (raw → hourly → daily). +- Background jobs operate within org context; job payloads contain `org_id`. + +## Data Disposal + +- Deleted records soft-deleted for 30-day grace period, then hard-purged. +- Purge jobs run nightly to remove expired soft-deleted records. +- Customer account deletion triggers full data purge after grace period. +- Credentials from revoked integrations deleted immediately (no grace period). + +## Access Controls + +- Role-based access: `owner`, `admin`, `member`, `viewer`. +- API tokens scoped to organization; cannot access other orgs. +- Token rotation supported; old tokens invalidated on rotation. +- Session timeout: 24 hours idle, 7 days absolute. + +## Audit Trail + +- All data mutations logged: who (user/agent), what (action), when (timestamp), context (org, resource). +- Audit logs immutable — append-only, no deletion. +- Retained for 1 year minimum. diff --git a/policies/ELIXIR_CODE_STANDARDS.md b/policies/ELIXIR_CODE_STANDARDS.md new file mode 100644 index 00000000..af9d270c --- /dev/null +++ b/policies/ELIXIR_CODE_STANDARDS.md @@ -0,0 +1,93 @@ +# Elixir Code Standards Policy + +**Effective:** 2026-03-15 +**Owner:** Engineering + +## Purpose + +All Elixir code in TowerOps must be idiomatic, readable, and maintainable. This policy ensures consistency across the codebase and reduces cognitive load for contributors. + +## Standards + +### Pattern Matching Over Conditionals + +- Use pattern matching in function heads instead of `if`/`case` when branching on structure. +- Prefer multi-clause functions over nested conditionals. + +```elixir +# Good +def process(%{status: :active} = device), do: ... +def process(%{status: :inactive} = device), do: ... + +# Avoid +def process(device) do + if device.status == :active do + ... + else + ... + end +end +``` + +### The Pipe Operator + +- Use `|>` for data transformation chains (3+ steps). +- Never start a pipe with a raw value — assign or wrap it. +- Each step in a pipe should take the piped value as its first argument. + +### With Clauses + +- Use `with` for sequences of dependent operations that may fail. +- Always include an `else` clause that handles expected error shapes. +- Keep `with` chains under 5 clauses; extract helpers if longer. + +### Naming + +- Modules: `PascalCase`, mirroring directory structure. +- Functions: `snake_case`, verb-first (`fetch_device/1`, `parse_response/1`). +- Private helpers: prefix with intent, not `do_` (e.g., `build_query` not `do_query`). +- Boolean functions: end with `?` (e.g., `active?/1`). + +### Contexts (Phoenix) + +- Business logic lives in context modules (`Towerops.Devices`, `Towerops.Alerts`). +- LiveViews and controllers call contexts — never Repo directly. +- Contexts return `{:ok, result}` or `{:error, reason}` tuples. +- Keep context functions focused; one public function per use case. + +### Error Handling + +- Use tagged tuples (`{:ok, _}` / `{:error, _}`) for expected failures. +- Let unexpected failures crash — the supervisor will restart. +- Never rescue broadly (`rescue e ->`) without re-raising or logging. +- Use `Ecto.Multi` for operations that must be atomic. + +### Documentation + +- All public functions must have `@doc` and `@spec`. +- Modules must have `@moduledoc` describing purpose and responsibilities. +- Internal/private functions: document with comments only when non-obvious. + +### Testing + +- Every public context function must have tests. +- Use `ExUnit.CaseTemplate` and factories for setup. +- Async tests (`async: true`) unless they touch shared state (database). +- Name tests descriptively: `"fetch_device/1 returns error when device not found"`. + +### Formatting + +- Run `mix format` before every commit. CI will reject unformatted code. +- Use `.formatter.exs` settings; do not override line length (98 chars). + +### Dependencies + +- Minimize external dependencies. Prefer stdlib and OTP. +- Every new dependency requires a brief justification in the PR description. +- Pin versions explicitly in `mix.exs`. + +## Enforcement + +- CI runs `mix format --check-formatted`, `mix credo --strict`, and `mix dialyzer`. +- Code review required for all merges to `main`. +- Violations block merge. diff --git a/policies/INCIDENT_RESPONSE.md b/policies/INCIDENT_RESPONSE.md new file mode 100644 index 00000000..20a1b7e6 --- /dev/null +++ b/policies/INCIDENT_RESPONSE.md @@ -0,0 +1,44 @@ +# Incident Response Policy + +**Effective:** 2026-03-15 +**Owner:** Engineering & Operations + +## Purpose + +Define how TowerOps detects, responds to, and recovers from production incidents to minimize customer impact and maintain trust. + +## Severity Levels + +| Level | Definition | Response Target | Examples | +|-------|-----------|----------------|----------| +| **SEV-1** | Service down, data loss risk, security breach | 15 minutes | Database corruption, auth bypass, full outage | +| **SEV-2** | Major feature broken, degraded for many customers | 1 hour | Polling failures, alert delivery broken, dashboard errors | +| **SEV-3** | Minor feature issue, workaround exists | 4 hours | UI glitch, non-critical integration failure | +| **SEV-4** | Cosmetic, no customer impact | Next business day | Typo, minor styling issue | + +## Response Procedure + +1. **Detect** — Monitoring alerts, customer report, or internal discovery. +2. **Acknowledge** — Responder claims the incident within response target. +3. **Assess** — Determine severity, blast radius, and root cause hypothesis. +4. **Mitigate** — Stop the bleeding. Rollback, feature flag, or hotfix. +5. **Resolve** — Fix the root cause. Deploy. Verify. +6. **Communicate** — Notify affected customers. Update status page. +7. **Post-mortem** — Blameless review within 5 business days for SEV-1/SEV-2. + +## Communication + +- SEV-1/SEV-2: Customer notification within 1 hour of detection. +- Status page updated for all SEV-1/SEV-2 incidents. +- Post-mortem shared with affected customers for SEV-1. + +## Post-Mortem Template + +Every SEV-1/SEV-2 post-mortem must include: + +- Timeline of events +- Root cause analysis +- Impact assessment (customers affected, duration) +- What went well +- What went poorly +- Action items with owners and due dates diff --git a/policies/README.md b/policies/README.md new file mode 100644 index 00000000..e24f03b8 --- /dev/null +++ b/policies/README.md @@ -0,0 +1,22 @@ +# TowerOps Policies + +Company policies governing code quality, security, privacy, and operations. + +## Policies + +| Policy | Description | +|--------|-------------| +| [Elixir Code Standards](ELIXIR_CODE_STANDARDS.md) | Idiomatic Elixir patterns, naming, testing, and formatting requirements | +| [Security & Vulnerability Prevention](SECURITY_AND_VULNERABILITY.md) | Pre-commit security checklist, dependency auditing, vulnerability response | +| [User Privacy](USER_PRIVACY.md) | Data classification, multi-tenant isolation, retention, export/deletion | +| [Incident Response](INCIDENT_RESPONSE.md) | Severity levels, response procedures, communication, post-mortems | +| [Change Management](CHANGE_MANAGEMENT.md) | PR process, deployment procedures, database migration safety | +| [Data Handling & Retention](DATA_HANDLING.md) | Encryption, backups, data processing, disposal, access controls | + +## Enforcement + +These policies are enforced through: + +1. **CI/CD** — Automated checks (formatting, credo, sobelow, dialyzer, tests) +2. **Code review** — Required for all PRs +3. **Quarterly reviews** — Policies reviewed and updated as the product evolves diff --git a/policies/SECURITY_AND_VULNERABILITY.md b/policies/SECURITY_AND_VULNERABILITY.md new file mode 100644 index 00000000..574ab619 --- /dev/null +++ b/policies/SECURITY_AND_VULNERABILITY.md @@ -0,0 +1,64 @@ +# Security & Vulnerability Prevention Policy + +**Effective:** 2026-03-15 +**Owner:** Engineering + +## Purpose + +Every commit must be evaluated for security implications before merge. TowerOps handles network infrastructure data — a breach could expose customer network topologies, credentials, and operational data. + +## Pre-Commit Security Checklist + +Before committing, verify: + +1. **No hardcoded secrets.** No API keys, tokens, passwords, or SNMP community strings in source. Use environment variables or runtime config. +2. **No SQL/HQL injection.** All database queries use parameterized Ecto queries — never string interpolation in queries. +3. **No LIKE injection.** Sanitize `%` and `_` in user-provided LIKE patterns using `Towerops.Helpers.sanitize_like/1`. +4. **Input validation.** All user input passes through Ecto changesets with explicit casting and validation before reaching business logic. +5. **Authorization checks.** Every controller/LiveView action verifies the current user has access to the requested resource via org membership. +6. **No mass assignment.** Changesets explicitly list permitted fields — never pass raw params to `cast/3` without filtering. +7. **Credential stripping.** API responses that include integration configs must strip `password`, `token`, `secret`, `api_key`, and `community` fields. + +## Dependency Security + +- Run `mix deps.audit` (via `mix_audit`) weekly and before releases. +- Run `mix sobelow` for static security analysis on every PR. +- Update dependencies monthly; patch critical CVEs within 48 hours. +- Erlang/OTP and Elixir versions must be on supported release tracks. + +## Authentication & Sessions + +- All sessions use secure, HTTP-only, SameSite=Lax cookies. +- Session tokens are rotated on privilege escalation (login, role change). +- API tokens are bearer tokens — never transmitted in query strings. +- Failed login attempts are rate-limited. +- Passwords hashed with `bcrypt` (Argon2 acceptable alternative). + +## Network Security + +- All external traffic over TLS. No exceptions. +- SNMP community strings treated as secrets — stored encrypted at rest, never logged. +- Agent-to-server communication uses mTLS or token-based auth with TOFU host key verification. +- Webhook endpoints validate signatures where supported. +- GraphQL introspection disabled in production. + +## Logging + +- Log security-relevant events: login, logout, failed auth, permission changes, data export. +- Never log credentials, tokens, full request bodies with sensitive fields, or PII. +- Retain security logs for 90 days minimum. + +## Vulnerability Response + +| Severity | Response Time | Action | +|----------|--------------|--------| +| Critical (RCE, auth bypass, data leak) | 4 hours | Patch, deploy, notify affected customers | +| High (privilege escalation, injection) | 24 hours | Patch, deploy | +| Medium (information disclosure, DoS) | 1 week | Patch in next release | +| Low (best practice deviation) | 1 month | Track in backlog | + +## Enforcement + +- CI runs `mix sobelow --exit` on every PR. +- `mix deps.audit` integrated into CI pipeline. +- Security-sensitive PRs (auth, crypto, data access) require explicit review from a senior engineer. diff --git a/policies/USER_PRIVACY.md b/policies/USER_PRIVACY.md new file mode 100644 index 00000000..cfc0cbc2 --- /dev/null +++ b/policies/USER_PRIVACY.md @@ -0,0 +1,77 @@ +# User Privacy Policy + +**Effective:** 2026-03-15 +**Owner:** Engineering & Operations + +## Purpose + +TowerOps monitors network infrastructure for ISPs and managed service providers. We handle sensitive operational data — device configurations, network topologies, traffic patterns, and customer metadata. This policy defines how we protect that data. + +## Core Principles + +1. **Collect only what's needed.** Never store data we don't have a clear product use for. +2. **Isolate by organization.** One customer must never see another customer's data. Ever. +3. **Encrypt in transit and at rest.** No exceptions. +4. **Default to private.** New features default to most-restrictive access. +5. **Delete when asked.** Customers can request full data deletion, and we comply promptly. + +## Data Classification + +| Classification | Examples | Handling | +|---------------|----------|----------| +| **Critical** | SNMP community strings, API keys, integration credentials | Encrypted at rest, never logged, stripped from API responses, access-audited | +| **Sensitive** | Device configs, network topologies, alert rules, user emails | Encrypted at rest, org-scoped access only, included in data export | +| **Operational** | Metrics, performance data, SNMP poll results | Org-scoped, retained per retention policy, aggregatable | +| **Public** | Product documentation, pricing | No restrictions | + +## Multi-Tenancy Isolation + +- All database queries MUST scope to the current user's organization via `org_id`. +- Row-Level Security (RLS) or application-level scoping enforced on every query. +- Background jobs (SNMP polling, alert evaluation) scope to their org context. +- Shared infrastructure (TimescaleDB, Redis) uses logical separation with org-prefixed keys. +- Cross-org data access is impossible by design — no admin backdoor, no superuser query mode. + +## Data Retention + +| Data Type | Default Retention | Customer Override | +|-----------|------------------|-------------------| +| SNMP metrics | 90 days raw, 2 years downsampled | Configurable | +| Alert history | 1 year | Configurable | +| Audit logs | 1 year | Minimum, not reducible | +| User sessions | 30 days | Not configurable | +| Deleted accounts | 30 days grace, then purged | Immediate on request | + +## Data Export & Deletion + +- Customers can export all their data via API (`GET /api/export`). +- Account deletion removes all org data within 30 days. +- Deletion requests logged and confirmed via email. +- Backups rotate out deleted data within the backup retention window (30 days). + +## Employee & Agent Access + +- Engineers access production data only through audited tooling — no direct database shells. +- AI agents (TowerOps agent, Paperclip agents) operate within org scope and cannot access other orgs. +- All production access logged with who, when, what, and why. +- Access reviewed quarterly; unused access revoked. + +## Third-Party Integrations + +- Integration credentials (PagerDuty, Gaiia, Sonar, etc.) stored encrypted. +- We send only the minimum data required for the integration to function. +- Integration data flows are documented per integration. +- Customers can revoke integrations at any time; credentials are deleted immediately. + +## Incident Response (Privacy) + +- Data breaches reported to affected customers within 72 hours. +- Breach scope assessed within 24 hours. +- Post-mortem published internally within 1 week. +- Regulatory notifications (if applicable) within required timelines. + +## Enforcement + +- Code review checks org-scoping on all data access. +- Automated tests verify tenant isolation. +- Quarterly privacy review of new features.