116 lines
7.1 KiB
Markdown
116 lines
7.1 KiB
Markdown
# Sprint 10 — C2 TLS verify: redteam-friendly defaults + warning suppression
|
|
|
|
**Base**: `origin/main` (PR #11 merged — sprint 8 + 9 are in).
|
|
**Branch**: `sprint/10-c2-tls-default`.
|
|
**Scope**: tiny correctness sprint. No new feature. Flip a security-relevant default to match the actual operator usage of this tool.
|
|
|
|
---
|
|
|
|
## Symptom (user report)
|
|
|
|
> "Impossible de se connecter à mon C2 à cause de la vérification du certificat SSL (self signed)"
|
|
|
|
Operator opens C2 config card, fills URL + token, hits **Test connection** without noticing the checkbox → SSL VERIFY FAILED against self-signed Mythic.
|
|
|
|
## Root cause (workflow diagnosis confirmed)
|
|
|
|
The `verify_tls` chain is **fully intact** end-to-end :
|
|
React `verifyTls` state → `C2ConfigInput.verify_tls` → PUT body → `C2Config.verify_tls` column → `cfg.verify_tls` in API loader → `get_adapter(verify_tls=)` → `MythicAdapter._verify` → `requests.post(verify=self._verify)`.
|
|
|
|
The bug is the **default** at every layer is `True` :
|
|
- `backend/app/models/c2_config.py:22` — `default=True`
|
|
- `backend/migrations/versions/0006_c2_layer.py:29` — `server_default=sa.true()`
|
|
- `backend/app/api/c2.py:87` — `data.get("verify_tls", True)`
|
|
- `backend/app/services/c2/mythic.py:116` — `verify_tls: bool = True`
|
|
- `backend/app/services/c2/factory.py:9` — `verify_tls=True`
|
|
- `frontend/src/components/C2ConfigCard.tsx:27` — `useState(true)`
|
|
- `frontend/src/components/C2ConfigCard.tsx:74` — reset on delete `setVerifyTls(true)`
|
|
|
|
Mimic is a BAS / red-team lab tool ; the dominant case is a **self-signed Mythic instance**, not a publicly-trusted cert chain. Defaulting `verify=True` is hostile to the actual workflow.
|
|
|
|
Secondary defect : `urllib3.exceptions.InsecureRequestWarning` is never suppressed (zero hits for `disable_warnings` / `urllib3` in `backend/`). When operators correctly uncheck verify, stderr gets spammed once per HTTP call.
|
|
|
|
---
|
|
|
|
## Decisions (locked)
|
|
|
|
1. **Flip default to `False` at every layer** — model, API fallback, adapter, factory, React state, delete reset.
|
|
2. **New migration 0008** : flip `server_default` to `sa.false()`. Existing rows are NOT mutated (their stored boolean is preserved).
|
|
3. **Suppress `urllib3.InsecureRequestWarning` ONLY when `verify_tls=False`** — gated inside `MythicAdapter.__init__`. Keeps the warning live for any future code that legitimately verifies.
|
|
4. **Add helper text under checkbox** : "Leave unchecked for lab Mythic with self-signed certificates." Operators see why the box matters.
|
|
5. **No API contract change** — `C2ConfigInput.verify_tls: boolean` stays required. The fallback in `data.get("verify_tls", False)` only matters for hand-crafted requests.
|
|
|
|
## Out of scope
|
|
|
|
- Don't touch existing C2 endpoints behavior (route paths, payload shapes).
|
|
- Don't change the `verify_tls` field type or remove the column.
|
|
- Don't change the FakeAdapter (it makes no HTTP calls).
|
|
|
|
---
|
|
|
|
## Task A — Backend (backend-builder)
|
|
|
|
**Files** :
|
|
- `backend/app/models/c2_config.py` — line 22 : `default=True` → `default=False`
|
|
- `backend/app/api/c2.py` — line 87 : `data.get("verify_tls", True)` → `data.get("verify_tls", False)`
|
|
- `backend/app/services/c2/factory.py` — line 9 : `verify_tls: bool = True` → `verify_tls: bool = False`
|
|
- `backend/app/services/c2/mythic.py` — line 116 : `verify_tls: bool = True` → `verify_tls: bool = False`, AND add at top of file `import urllib3` + `from urllib3.exceptions import InsecureRequestWarning`, AND inside `__init__` after `self._verify = verify_tls`:
|
|
```python
|
|
if not verify_tls:
|
|
urllib3.disable_warnings(InsecureRequestWarning)
|
|
```
|
|
- **NEW migration** `backend/migrations/versions/0008_c2_verify_tls_default_false.py` — flip `server_default` to `sa.false()`. Use `op.batch_alter_table("c2_config")` for SQLite compatibility (Mimic uses SQLite per sprint 1 SPEC). Down-migration restores `sa.true()`.
|
|
- **Tests** : grep `backend/tests/` for `verify_tls` and flip every assertion that presupposed the old `True` default (likely in `test_c2_config*.py` PUT-without-verify-tls tests and GET-fresh-row tests). Don't add new tests — adapt existing ones.
|
|
|
|
**Constraints** :
|
|
- `pytest` baseline 468/468 must hold (or grow ; never shrink).
|
|
- `ruff` + `mypy --strict` clean.
|
|
- Migration 0008 must be reversible — round-trip `alembic upgrade head` then `alembic downgrade -1` then `alembic upgrade head` must work on a fresh SQLite DB.
|
|
- Don't restructure or refactor anything else. Minimum surface.
|
|
|
|
## Task B — Frontend (frontend-builder)
|
|
|
|
**Files** :
|
|
- `frontend/src/components/C2ConfigCard.tsx` :
|
|
- Line 27 : `useState(true)` → `useState(false)`
|
|
- Line 74 (delete handler) : `setVerifyTls(true)` → `setVerifyTls(false)`
|
|
- Under the checkbox JSX (around lines 169-182) : add a `<p>` with helper text :
|
|
```tsx
|
|
<p className="text-[12px] text-charcoal mt-xxs">
|
|
Leave unchecked for lab Mythic with self-signed certificates.
|
|
</p>
|
|
```
|
|
(Use the existing DESIGN.md tokens — `text-[12px] text-charcoal` matches the `hint` style on `FormField`. Confirm token name by reading neighboring components first.)
|
|
- **Vitest** : if `C2ConfigCard.test.tsx` exists, flip any "starts checked" assertion to "starts unchecked".
|
|
|
|
**Constraints** :
|
|
- `vitest` baseline 212/212 must hold.
|
|
- `tsc --noEmit` + `eslint --max-warnings=0` clean.
|
|
- No type change to `C2ConfigInput` (the field is already required `boolean`).
|
|
- Visual: same row, just one extra `<p>` hint below the checkbox-label row. Same brutalist treatment, no transition.
|
|
|
|
## Task C — Sequencing
|
|
|
|
Both tasks have **zero shared files**. Dispatch backend-builder + frontend-builder **in parallel**. No ordering constraint.
|
|
|
|
After both report green :
|
|
- **code-reviewer** : sprint diff scan (focus : migration reversibility, urllib3 gating, no leftover hardcoded `True`).
|
|
- **design-reviewer** : helper-text placement, token compliance, focus ring still works, no regression on the card.
|
|
- **spec-reviewer** : verify SPEC.md § Intégration C2 still matches the new defaults (may need a one-line note about lab-mode default ; check before editing).
|
|
|
|
## Definition of Done
|
|
|
|
- Test connection against self-signed Mythic from a freshly-created C2 config works **without unchecking anything**.
|
|
- Existing rows are untouched (operators who saved verify_tls=true keep it until they re-save).
|
|
- `pytest` 468/468 → 468+ (no shrink), `vitest` 212/212.
|
|
- `ruff` + `mypy --strict` + `tsc --noEmit` + `eslint` clean.
|
|
- Migration 0008 round-trip OK.
|
|
- No `urllib3.InsecureRequestWarning` on stderr when `verify_tls=False`.
|
|
- Code-reviewer + design-reviewer + spec-reviewer APPROVED.
|
|
- PR opened on Gitea ; tasks/pr-body-sprint-10.md drafted by team-lead.
|
|
|
|
## Operator notes (for PR body)
|
|
|
|
- This sprint flips a security-relevant default. Production operators who legitimately use a publicly-trusted cert chain will need to explicitly check the box — but the dominant case in this BAS tool is lab Mythic with self-signed, so the new default matches the actual workflow.
|
|
- Existing engagements with `verify_tls=true` already stored remain unchanged. They keep their current behaviour. If the operator reported the bug because their existing row has `verify_tls=true`, they will still need to uncheck + save once after this sprint lands.
|