# Sentry Security Review
# Classification: Confidential
# Date: 2026-06-08
# Reviewer: Claude Code (drafted); approved by Jason M. Schwefel (Founder / Global Administrator)
# Status: Approved

---

## 1. Vendor / Device Overview

| Field | Value |
|---|---|
| **Name** | Sentry (Functional Software, Inc., d/b/a Sentry) |
| **Website** | sentry.io |
| **Service / purpose** | Application crash and error reporting / aggregation |
| **CBB use case** | Receive anonymous crash diagnostics from the ScopeDOPE mobile app to diagnose and fix stability defects, with crash attribution into the shared `:ballistics` KMP module |
| **Data tier(s) accessed** | Tier 1 (anonymous technical diagnostics only) |
| **Access type** | Outbound SDK egress to Sentry ingest endpoint, authenticated by a public client DSN (not a secret credential in the traditional sense; see §5.1) |
| **Proposed onboarding date** | TBD — gated on this review's approval and cost approval |

---

## 2. Data Exposure Assessment

### 2.1 What CBB data will this vendor / device access?

Only **anonymous crash diagnostics**, and only from end-user devices where the user has explicitly opted in (default OFF — SPEC §16.1). A crash event payload is limited to:

- Stack trace (which code path failed)
- App version (`versionName`)
- OS version
- Device model (e.g., "Galaxy A16")
- Crash timestamp

**Explicitly excluded** (enforced in code via SDK configuration — see §5.1):

- No user identifiers (no accounts exist in ScopeDOPE — §4.6)
- No PII (name, email, contact, advertising ID, IP-derived identity)
- No location / GPS
- No ammunition profiles, DOPE data, ballistic settings, sessions, or file contents
- No firearm or shooting-activity information
- No Sentry "message", "extra", "tags" (beyond version/OS/model), or breadcrumb payloads carrying user content

This vendor accesses **no CBB corporate data** and **no ScopeDOPE Tier 4 SPEC material**. It only receives end-user device crash traces from opted-in installs.

### 2.2 Where will CBB data be stored by this vendor?

On Sentry's infrastructure. Sentry offers US and EU data-residency regions; CBB will select the **US region** at project creation. Default event retention on the relevant plan tiers is 90 days, after which events are purged. No CBB-controlled data store is involved.

### 2.3 Is any Highly Confidential data (Tier 4) involved?

- [ ] Yes
- [x] **No** — crash diagnostics are anonymous technical data. Engine internals are not transmitted; only stack-frame symbol names (e.g., function names) appear, which are not trade-secret algorithm content. No SPEC, DOPE-Blend methodology, or proprietary data is sent.

> **Residual consideration:** A stack trace can, in pathological cases, include a string value held in a local variable at crash time. Mitigated by SDK configuration that disables message/extra capture and applies a `beforeSend` scrubber (§5.1). Rated Low likelihood, Low impact (no PII exists in the app to leak — there are no accounts and ballistic data is non-personal).

### 2.4 Server-side data scrubbing (as-built backstop)

The Sentry org-level **Data Scrubber** is enabled with **default scrubbers** and **Scrub IP Addresses** ON. In addition, the **Additional Sensitive Fields** list below is configured as a deliberately broad prophylactic backstop: it redacts the *value* of any event field whose *key* matches one of these names. This is a second layer behind the app-side `sendDefaultPii = false` + `beforeSend` enforcement (§5.1) — it guards against a future app field being added and not individually accounted for.

> **Release gate:** Before every `v<version>` release, new persisted/serialized field names added since the last release are reviewed against this list and any missing identity/PII/location/file/ballistic names are added. See SDLC Pre-release checklist ("Sentry data-scrub currency check"). The list must not fall behind the app's field surface.

**Additional Sensitive Fields list (as configured in Sentry):**

```
latitude longitude lat lon lng gps gps_lat gps_lon geo geolocation location
coords coordinates coriolis_latitude_deg
name fname firstname first_name firstName first-name
lname lastname last_name lastName last-name
fullname full_name fullName displayname display_name displayName
username user_name userName user userid user_id userId
email emailaddress email_address emailAddress e_mail mail
phone phonenumber phone_number phoneNumber mobile tel telephone
address street streetaddress street_address city state
zip zipcode zip_code postal postalcode postal_code country
dob dateofbirth date_of_birth birthdate birthday age ssn social_security contact
session sessions dope dope_data ammo ammunition load rifle firearm gun
serial serialnumber serial_number
filepath file_path filePath filename file_name fileName path
deviceid device_id deviceId devicename device_name deviceName imei
mac macaddress mac_address ip ipaddress ip_address ipAddress
password passwd pwd pass secret token auth authorization
apikey api_key apiKey accesstoken access_token accessToken
refreshtoken refresh_token bearer credential credentials
privatekey private_key session_token cookie
```

> **Scope note:** Sentry scrubs on *key-name match*, not on free-text content within message strings. The primary defense against incidental content in trace strings remains the app-side `beforeSend` scrubber and the no-message/no-extra configuration (§5.1). This list is the backstop, not the primary control.

---

## 3. Vendor Security Posture (Software vendors only)

### 3.1 Compliance and certifications

| Standard | Certified? | Notes |
|---|---|---|
| SOC 2 Type II | Yes | Sentry publishes a SOC 2 Type II report (available under NDA via their Trust/Security portal) |
| ISO 27001 | Yes | Certified per Sentry's security documentation |
| GDPR compliant | Yes | DPA available; EU region offered |
| CCPA compliant | Yes | Addressed in Sentry's privacy/DPA terms |

> Reviewer to verify current certification status against Sentry's Trust portal at approval time; values above reflect Sentry's publicly stated posture and must be confirmed before sign-off.

### 3.2 Data processing agreement

- [x] A Data Processing Agreement (DPA) is available and must be executed during signup
- [ ] A DPA is not available

> **Condition:** Execute Sentry's DPA at account creation. ScopeDOPE's privacy policy (§6) names Sentry as a processor; the DPA must be in place before the SDK ships in any externally distributed build.

### 3.3 Breach history

No publicly disclosed material breach of customer event data known at drafting (past 3 years). Reviewer to re-check public disclosures and the vendor's status page history at approval time.

### 3.4 Security documentation

- [x] Vendor publishes a security whitepaper / trust page (sentry.io/security, sentry.io/trust)
- [x] Vendor publishes a current SOC 2 report (available under NDA)
- [x] Vendor has a responsible disclosure / bug bounty program

### 3.5 Authentication and access control

- [x] Vendor supports MFA (for the CBB Sentry org login)
- [x] Vendor supports SSO / SAML federation with Entra ID (paid tiers; optional, not required at beta)
- [x] Vendor supports API key / token scoping (auth tokens scoped per use; the client DSN is ingest-only and cannot read data)
- [x] Vendor supports OAuth with defined scopes (for dashboard/API access)

---

## 4. Hardware Security Assessment (Hardware / BLE devices only)

**N/A** — Sentry is a software/SaaS vendor. No hardware or BLE component.

---

## 5. Integration Security

### 5.1 Credential management

The SDK authenticates to the Sentry ingest endpoint using a **client DSN**. The DSN is an **ingest-only public identifier**: it permits sending events to one project and cannot read, query, or modify any data, and it is necessarily embedded in the distributed app binary at runtime. Storing it in Key Vault therefore protects it **at rest and in the repo/process** — not in the shipped binary (which is acceptable, since it is ingest-only).

**DSN storage — Azure Key Vault as single source of truth (no other copy):**

- [x] Each platform's DSN is stored only in **Azure Key Vault** (`kv-cbb-shared-prod`): `sentry-dsn-android` (Android) and `sentry-dsn-ios` (iOS, when that project is created). **Never committed to any repo; no `local.properties` copy.**
- [x] **Every build** (CI and local) fetches the DSN from Key Vault at build time via a Gradle task (`az keyvault secret show`) and injects it into `BuildConfig`. There is no checked-in fallback file.
- [x] **Graceful degradation (mandatory):** if the DSN cannot be fetched (no `az` auth, KV unreachable, offline), the build injects an **empty DSN** and **does not fail**. The `CrashReporter` treats an empty DSN as disabled / no-op. A developer without vault access can still build and run; crash reporting simply stays off (consistent with the opt-in default-OFF state).
- [x] Any **Sentry auth token** (used for CI symbol/source-map upload or dashboard automation) is a real secret, stored in `kv-cbb-shared-prod` (e.g. `sentry-auth-token`), scoped to the minimum required permission (release/upload only), unique to this integration.
- [x] Credentials are unique to this integration (not shared); CI build identity is granted **read** on the relevant KV secrets only.

> **Two-project / two-DSN model:** Android and iOS use **separate Sentry projects** and **separate DSNs** (`sentry-dsn-android`, `sentry-dsn-ios`), each in `kv-cbb-shared-prod`. The shared `:ballistics` KMP layer routes shared-code crashes through whichever platform SDK is active. The iOS project and its DSN are created when iOS development begins; this review's controls apply equally to both.

**Mandatory SDK hardening (enforced in code before any external ship):**

- `sendDefaultPii = false`
- No `User` object set; no `setTag`/`setExtra` carrying user content
- `beforeSend` hook scrubs or drops any event field outside the permitted payload (§2.1)
- Breadcrumbs limited to lifecycle/navigation with no user content; or disabled
- SDK initialized **only** when `crash_reporting_enabled == true` (opt-in gate, SPEC §16.1)

### 5.2 Network exposure

Outbound HTTPS only, from end-user devices to Sentry's ingest endpoint. **No inbound firewall rules, no webhook endpoints, no public IP exposure on CBB infrastructure.** CBB hosts nothing for this integration.

### 5.3 Offboarding plan

To revoke: disable/delete the Sentry project(s) (invalidates the DSN(s) — events stop being accepted), delete the `sentry-dsn-android` / `sentry-dsn-ios` / `sentry-auth-token` secrets from `kv-cbb-shared-prod`, and remove the SDK in a subsequent app release. To delete data: trigger project/event deletion in the Sentry dashboard and/or invoke Sentry's data-deletion process per the DPA. Event data also self-purges at the retention window (90 days).

### 5.4 Alerting and notification routing (as-built)

**Sentry org:** `cold-bore-ballistics` (US region) · **Project:** `scopedope-android` · **Team:** `#cold-bore-ballistics-llc`.

**Stability monitor (metric alert):**
- Metric: **Crash Free Session Rate** (dataset Releases, `crash_free_rate` / session, 1-hour interval)
- **High priority:** crash-free session rate **below 98%** (i.e. > 2% of sessions crashing) — the review's hard line / staged-rollout halt condition (SPEC §16.1)
- **Medium priority:** below **99%** (> 1% crashing) — early warning
- Resolve: default (auto-resolves when rate recovers above threshold)
- Assigned to team `#cold-bore-ballistics-llc`

**Notification routing:**
- Alert action: **Notify → Member → `crash@coldboreballisticsllc.com`** (dedicated shared mailbox, role-based — no single-person dependency)
- `crash@` is a Sentry org **Member** on team `#cold-bore-ballistics-llc` (least-privilege Member role; receives notifications only)
- **Verified:** test notification sent from the alert builder was received in the `crash@` mailbox (end-to-end chain confirmed: monitor → alert action → Sentry member → shared mailbox)
- Action throttle: notify on every trigger

> **iOS note:** the iOS project will replicate this monitor + the same `crash@` routing when created.

---

## 6. Risk Summary

| Risk | Likelihood (L/M/H) | Impact (L/M/H) | Mitigation |
|---|---|---|---|
| Incidental PII/user content in a stack trace string | L | L | `beforeSend` scrubbing; no message/extra capture; app holds no accounts/PII; ballistic data is non-personal |
| DSN abuse (third party sends junk events to our project) | L | L | DSN is ingest-only; rate-limiting and inbound filters in Sentry; spending cap ($50/mo) bounds cost |
| Vendor breach of stored event data | L | L | Anonymous diagnostics only; no PII at risk; SOC 2 / ISO 27001 vendor; 90-day retention |
| Cost overrun from event volume | L | L | Hard $50/mo spending cap; free tier through beta |
| Shipping SDK before privacy disclosure live | L | M | Hard gate: SDK must not ship externally until privacy policy §6 disclosure is live and DPA executed |

**Overall risk rating:** **Low**

**Recommendation:** **Approve with conditions** — **APPROVED by Founder 2026-06-08.** Conditions 1, 2, 4, and the cost-impact analysis are satisfied; condition 3 (SDK-hardening re-verification on the first signed release build) and condition 5's spending-cap confirmation at the trial→Developer transition remain as ongoing release-gate items, not blockers to approval.

**Conditions (if any):**

1. Execute Sentry's DPA at account creation (US data region). — **DONE** (DPA executed; US region confirmed, 2026-06-08).
2. Confirm current SOC 2 Type II / ISO 27001 status against Sentry's Trust portal at approval. — **DONE** (confirmed current, 2026-06-08).
3. SDK hardening per §5.1 implemented and verified before any externally distributed build. — **VERIFIED on-device** (2026-06-09): forced test crash transmitted to the `scopedope-android` project and audited in-dashboard — event carried stack trace, app version (0.9.6-beta4), OS (Android 16), device model only; **Users: 0** (no identity), no PII, no IP, no ballistic/session data, Session Replay absent. Confirms `sendDefaultPii=false`, `scope.user=null`, `beforeSend` scrub, and replay/NDK exclusions all effective. Re-verify on the first **signed release** build before public distribution.
4. Privacy policy §6 (Sentry disclosure) live before external distribution. — **DONE** (privacy policy live at public URL, 2026-06-08).
5. Spending cap set to $50/month; 2% crash-rate alert rule configured (SPEC §16.1 / SDLC §8.3). — **Alert CONFIGURED & verified** (crash-free session rate below 98% → `crash@` mailbox; test received). See §5.4. Spending cap still to confirm at trial→Developer transition.
6. Cost approval recorded (see Appendix — Cost-Impact Analysis).

---

## Appendix — Cost-Impact Analysis

Per CLAUDE.md, any new billable resource requires a written cost-impact analysis and explicit Founder approval before provisioning.

| Field | Value |
|---|---|
| **Added cost (now / beta)** | $0 — Sentry free Developer tier (≈5,000 errors/month) |
| **Added cost (at public 1.0.0 launch)** | $26/month — Team plan (≈50,000 errors/month) |
| **New monthly total** | Existing infrastructure + $0 during beta; + $26/month from public launch |
| **Hard spending cap** | $50/month (configured in Sentry billing) |
| **Alternatives considered** | Firebase Crashlytics (free, but no KMP crash attribution — rejected, SDLC §8.3); Bugsnag (KMP SDK was beta at selection — rejected); self-hosted Sentry (ops/maintenance burden inconsistent with a no-server app — rejected) |
| **Justification** | KMP crash attribution into the shared `:ballistics` module is essential for diagnosing engine defects across Android and (future) iOS from one source of truth |

**Founder cost approval:** ☑ Approved — date: 2026-06-08 — by: Jason M. Schwefel (Founder)

---

## Document Control

| Field | Value |
|---|---|
| **Document owner** | `jschwefel@coldboreballisticsllc.com` |
| **Classification** | Confidential (03_-_Confidential/) |
| **Location** | `OneDrive/CBB_Corporate/03_-_Confidential/Infrastructure/Sentry/Sentry_Security_Review.md` |
| **Cross-references** | "CBB Risk Register" SharePoint List (`coldboreballistics.sharepoint.com/sites/ColdBoreBallistics`); `SCOPEDOPE_PRIVACY_POLICY.md`; `ScopeDOPE_SDLC.md §8.3`; SPEC §16.1 |
| **Created** | 2026-06-08 |
| **Last updated** | 2026-06-09 |
| **Next full review due** | 2027-06-08 |

---

## Revision Control Block

| Rev | Date | Changed By | Reviewed By | Approved By | Summary |
|-----|------|------------|-------------|-------------|---------|
| DRAFT | 2026-06-08 | Claude Code | — | — | Initial draft of Sentry vendor security review for ScopeDOPE crash reporting; includes cost-impact analysis appendix; recommends Approve with conditions pending Founder sign-off |
| DRAFT | 2026-06-08 | Claude Code | — | — | Added §2.4 server-side data-scrub field list (as-built backstop) and release-gate note; documents the Additional Sensitive Fields configured in Sentry and the per-release currency check (SDLC + CLAUDE.md) |
| DRAFT | 2026-06-08 | Claude Code | — | — | §5.1/§5.3 revised: DSN moved to Azure Key Vault as single source of truth (no local copy), fetched at build time with graceful empty-DSN fallback; documented two-project/two-DSN model (`sentry-dsn-android`, `sentry-dsn-ios`) covering the planned iOS integration; offboarding updated to delete KV secrets |
| DRAFT | 2026-06-08 | Claude Code | — | — | As-built: Sentry org/project/DSN created (`sentry-dsn-android` stored in `kv-cbb-shared-prod`); added §5.4 alert & notification routing (crash-free session rate below 98% high / 99% medium → `crash@` shared mailbox as Sentry member; test notification verified end-to-end); annotated condition 5 as configured-and-verified |
| DRAFT | 2026-06-08 | Claude Code | — | — | Conditions 1, 2, 4 marked DONE per Founder confirmation: DPA executed (US region), SOC 2 Type II / ISO 27001 confirmed current, privacy policy §6 disclosure live. SDK may ship externally; condition 3 (SDK hardening verification on signed build) remains the only open item |
| DRAFT | 2026-06-09 | Claude Code | Jason M. Schwefel | Jason M. Schwefel | **APPROVED.** Status Draft→Approved; recommendation and cost-impact approval recorded (Founder approved 2026-06-08). Repointed risk-register cross-ref to the "CBB Risk Register" SharePoint List (the `Vendor_Risk_Register.md` file was retired — the register is a SharePoint List, not a file). Version number to be assigned by Founder. |
