# FieldOps Cloud Security Test Report

Date: 2026-06-16

## Scope

Safe local security checks were performed against the repository and XAMPP app. No destructive testing, external provider calls, credential attacks, or third-party service tests were performed.

## Checks Performed

| Check | Result | Evidence |
| --- | --- | --- |
| No obvious real secrets committed | Pass | Secret scan found only fake LLM test values |
| `.env` ignored | Pass | `.gitignore` includes `/.env` |
| `vendor/` ignored | Pass | `.gitignore` includes `/vendor/` |
| Dependency audit can run | Pass | `composer audit` reported no known vulnerability advisories |
| Password hashing service exists | Pass | `PasswordHasher`, `composer test` |
| RBAC service exists and tests pass | Pass | `RbacService`, `composer test` |
| CSRF enforced on non-API POST routes | Pass | Missing CSRF checks returned 403 |
| Security headers present | Pass after fix | XFO, XCTO, Referrer-Policy, Permissions-Policy, and CSP present |
| CSP present | Pass after fix | `SecurityHeaderService` and `tools/auth-production-smoke.cjs` |
| PHP version exposure disabled | Pass after fix | App bootstrap removes `X-Powered-By`; smoke verifies absence |
| Session cookies hardened | Pass after fix | Strict mode, HttpOnly, SameSite=Lax, Secure under HTTPS production; smoke verifies live HttpOnly/SameSite |
| Demo auto-login gated for production | Pass after fix | `AppEnvironment::shouldAutoLoginDemo()` and `tools/auth-production-smoke.cjs` verify demo-disabled production mode |
| CSV export formula protection | Pass after fix | Formula-leading value escaped |
| Upload safety | Pass for Gate 4 / partial large-data | CSV file/paste preview, validation, extension/MIME/size checks, binary/UTF-8/header/row/formula rejection, commit, duplicate-commit blocking, rollback, undo, audit hooks, and browser multipart upload smoke are covered. Large-data performance tests still required. |
| API auth/token route enforcement | Planned | Public REST token routes are now marked planned/not live in `docs/API_REFERENCE.md`; active API-style routes are portal/offline/assistant only |
| Live OAuth/webhook security | Not implemented | Integration foundations only |
| Billing webhook security | Pass for Gate 5 foundation / live provider pending | Stripe-style HMAC verification, replay tolerance, tenant metadata checks, provider-event idempotency, and payload-hash conflict rejection are covered; live payment capture remains disabled |
| Audit logging for sensitive actions | Partial | Schema exists; runtime writes not implemented for tested actions |

## Findings

### SEC-001: Production Demo Auto-Login

Severity: SEV-1

Status: Fixed.

`public/index.php` previously logged in `demo@example.test` automatically when no user session existed. This is now controlled by `AppEnvironment::shouldAutoLoginDemo()` and disabled when `APP_ENV=production` and `DEMO_ENABLED=0`.

Retest:

- `composer test`
- `composer check`
- `tools/auth-production-smoke.cjs`

### SEC-002: Session Cookie Hardening

Severity: SEV-2

Status: Fixed for application bootstrap policy.

The app now configures strict-mode sessions before `session_start()`, sets HttpOnly and SameSite=Lax, scopes the path to the app base path, and sets Secure cookies for HTTPS production requests.

Retest:

- `composer test`
- `tools/auth-production-smoke.cjs`

### SEC-003: Missing CSP and PHP Header Exposure

Severity: SEV-2

Status: Fixed for app-level headers.

The app now sends a CSP compatible with local assets and current Bootstrap/jsDelivr usage, sends Permissions-Policy, removes `X-Powered-By`, and sends HSTS when the request is HTTPS in production.

Retest:

- `composer test`
- `tools/auth-production-smoke.cjs`

### SEC-004: Public REST API Not Yet Implemented

Severity: SEV-2

`docs/API_REFERENCE.md` now separates active API-style routes from planned public REST routes. The planned REST routes are not registered yet and must not be marketed as live.

Required fix:

- Keep the API marked planned until routes, token auth, permission checks, tenant isolation, rate limiting, audit logs, and tests are implemented.

### SEC-005: Upload/Import Hardening Incomplete

Severity: SEV-2

Import type selection, validation preview, commit, undo, DB-backed smoke coverage, audit hooks, MIME/extension validation, browser upload coverage, malicious CSV fixtures, duplicate-commit blocking, and rollback coverage exist. Larger fixture and performance tests remain required.

Required fix:

- Add large-data import performance tests, durable import job persistence, and deeper row-error reporting for long-running imports.

### SEC-006: Billing Webhook Security

Severity: SEV-2

Status: Fixed for Gate 5 foundation.

`POST /api/billing/stripe/webhook` now rejects missing, malformed, stale, or invalid Stripe-style signatures, requires tenant metadata before recording an event, and enforces idempotency by provider event id plus payload hash.

Remaining live-release work:

- Keep payment capture disabled until Stripe live credentials, webhook secret, price IDs, operator approval, and subscription-state mutation tests are complete.

## Positive Security Results

- Existing service tests cover password hashing, RBAC, LLM secret masking, accounting provider validation, calendar validation, and import schema validation.
- Production auth smoke covers demo-disabled production mode, invalid login, signup, DB-backed role logins, and platform-admin isolation.
- Production auth smoke now also verifies CSP/security headers, `X-Powered-By` removal, and session cookie HttpOnly/SameSite policy.
- CSRF is enforced centrally for non-API POST routes.
- CSV export formula escaping was added and verified.
- Gate 3 MySQL tenant-isolation smoke verifies tenant-scoped reads, guarded cross-tenant writes, and quote/invoice child-row delete safety.
- Gate 5 billing webhook tests cover signature validation, replay tolerance, tenant metadata enforcement, duplicate replay handling, and payload-hash mismatch rejection.
- Composer audit runs against the generated lock file and reports no known vulnerability advisories.
- No real committed API keys or private keys were found.

## Security Release Position

Security status: Not production-ready.

The current security posture is acceptable for a local demo and controlled internal QA, but production/commercial deployment is blocked until API route truthfulness, live integration secret handling, live payment activation controls, and production infrastructure controls are completed and tested.
