@tideorg/mcp 1.4.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/GAP_REGISTER.md +19 -9
- package/PRIVACY.md +41 -0
- package/README.md +204 -120
- package/adapters/AGENTS.md +3 -2
- package/adapters/CLAUDE.md +1 -1
- package/adapters/replit.md +0 -0
- package/canon/anti-patterns.md +55 -62
- package/canon/concepts.md +46 -34
- package/canon/custom-contracts.md +12 -8
- package/canon/feature-mapping.md +27 -75
- package/canon/framework-matrix.md +69 -22
- package/canon/hosting-options.md +111 -0
- package/canon/iga-change-requests-api.md +211 -0
- package/canon/invariants.md +33 -35
- package/canon/redirect-handler.md +0 -0
- package/canon/security-gap-mapping.md +506 -0
- package/canon/security-runtime-probes.md +177 -0
- package/canon/tidecloak-bootstrap.md +84 -15
- package/canon/tidecloak-endpoints.md +60 -98
- package/canon/troubleshooting.md +201 -53
- package/canon/version-policy.md +8 -12
- package/mcp-server/dist/http.d.ts +2 -0
- package/mcp-server/dist/http.js +46 -0
- package/mcp-server/dist/index.d.ts +0 -0
- package/mcp-server/dist/index.js +2 -601
- package/mcp-server/dist/server.d.ts +2 -0
- package/mcp-server/dist/server.js +617 -0
- package/package.json +48 -45
- package/playbooks/add-auth-nextjs-existing.md +33 -17
- package/playbooks/add-auth-nextjs-fresh.md +1 -1
- package/playbooks/add-rbac-nextjs.md +7 -2
- package/playbooks/bootstrap-realm-from-template.md +33 -18
- package/playbooks/configure-e2ee-roles-and-policies.md +0 -0
- package/playbooks/deploy-tidecloak-docker.md +31 -28
- package/playbooks/diagnose-broken-login.md +0 -0
- package/playbooks/diagnose-missing-roles-or-claims.md +2 -2
- package/playbooks/initialize-admin-and-link-account.md +22 -19
- package/playbooks/migrate-from-existing-auth.md +0 -0
- package/playbooks/protect-api-nextjs.md +40 -1
- package/playbooks/protect-aspnet-core-asgard.md +313 -0
- package/playbooks/protect-routes-nextjs.md +24 -10
- package/playbooks/provision-tidecloak-skycloak.md +213 -0
- package/playbooks/setup-forseti-e2ee.md +32 -50
- package/playbooks/setup-iga-admin-panel.md +113 -171
- package/playbooks/start-tidecloak-dev.md +0 -0
- package/playbooks/verify-jwt-server-side.md +20 -1
- package/prompts/add-admin-approval-flow.md +0 -0
- package/prompts/build-private-customer-portal.md +1 -1
- package/prompts/migrate-generic-auth-to-tide.md +0 -0
- package/prompts/secure-existing-app.md +0 -0
- package/prompts/security-gap-analysis.md +55 -0
- package/reference-apps/INDEX.md +0 -0
- package/reference-apps/encrypted-communication/anti-patterns.md +3 -3
- package/reference-apps/encrypted-communication/bootstrap-sequence.md +2 -2
- package/reference-apps/encrypted-communication/manifest.yaml +0 -0
- package/reference-apps/encrypted-communication/role-policy-matrix.md +0 -0
- package/reference-apps/encrypted-communication/scenario.md +2 -2
- package/reference-apps/git-pr-signing-service/anti-patterns.md +0 -0
- package/reference-apps/git-pr-signing-service/bootstrap-sequence.md +8 -8
- package/reference-apps/git-pr-signing-service/manifest.yaml +0 -0
- package/reference-apps/git-pr-signing-service/role-policy-matrix.md +0 -0
- package/reference-apps/git-pr-signing-service/scenario.md +0 -0
- package/reference-apps/iga-admin-governance/anti-patterns.md +18 -16
- package/reference-apps/iga-admin-governance/bootstrap-sequence.md +10 -5
- package/reference-apps/iga-admin-governance/manifest.yaml +0 -0
- package/reference-apps/iga-admin-governance/role-policy-matrix.md +12 -13
- package/reference-apps/iga-admin-governance/scenario.md +15 -13
- package/reference-apps/organisation-password-manager/anti-patterns.md +0 -0
- package/reference-apps/organisation-password-manager/bootstrap-sequence.md +5 -6
- package/reference-apps/organisation-password-manager/manifest.yaml +0 -0
- package/reference-apps/organisation-password-manager/role-policy-matrix.md +0 -0
- package/reference-apps/organisation-password-manager/scenario.md +0 -0
- package/reference-apps/policy-governed-signing/anti-patterns.md +0 -0
- package/reference-apps/policy-governed-signing/bootstrap-sequence.md +9 -9
- package/reference-apps/policy-governed-signing/manifest.yaml +0 -0
- package/reference-apps/policy-governed-signing/role-policy-matrix.md +0 -0
- package/reference-apps/policy-governed-signing/scenario.md +0 -0
- package/skills/tide-diagnostics/SKILL.md +1 -1
- package/skills/tide-integration/SKILL.md +9 -18
- package/skills/tide-learning-capture/SKILL.md +0 -0
- package/skills/tide-mcp-qa/SKILL.md +139 -0
- package/skills/tide-rbac-and-e2ee/SKILL.md +5 -5
- package/skills/tide-reviewer/SKILL.md +1 -1
- package/skills/tide-route-and-api-protection/SKILL.md +0 -0
- package/skills/tide-scenario-resolver/SKILL.md +0 -0
- package/skills/tide-security-analyst/SKILL.md +182 -0
- package/skills/tide-setup/SKILL.md +1 -1
- package/skills/tide-solutions-architect/SKILL.md +0 -0
- package/canon/delegation.md +0 -195
- package/playbooks/setup-server-delegation.md +0 -142
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Role: MCP QA Engineer (Pre-Release Gate)
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Decide whether the Tide MCP pack is safe to release. This role runs the deterministic test gate, then does the semantic review the gate cannot, and issues a single verdict: **SHIP**, **SHIP_WITH_WARNINGS**, or **BLOCK**. It is the pre-release quality gate for the pack (canon, playbooks, skills, prompts, reference-apps, and the MCP server that exposes them).
|
|
8
|
+
|
|
9
|
+
This role is **read-only and advisory about the release**. It runs tests and reads content; it does not fix defects it finds. When it finds a blocker, it names the file and the fix owner and blocks the release.
|
|
10
|
+
|
|
11
|
+
## Two layers, one verdict
|
|
12
|
+
|
|
13
|
+
1. **Deterministic gate (objective, must be green).** `cd mcp-server && npm test` — protocol smoke tests (all tools/prompts present, each returns correct-looking content, graceful errors) plus content-consistency checks (no stray legacy endpoints, no merge markers, referenced playbooks exist, SG-01..SG-18 present, GAP counts sum, honesty guards present). **Any red here is an automatic BLOCK.**
|
|
14
|
+
2. **Semantic review (judgment, gate can't see it).** Spot-read tool outputs and doctrine for correctness, coherence, and honesty; drive a sample of eval cases; confirm new/changed capabilities actually lead an agent to the right behavior.
|
|
15
|
+
|
|
16
|
+
A release is **SHIP** only when the gate is green *and* semantic review finds no correctness/honesty blocker.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Boundary
|
|
21
|
+
|
|
22
|
+
| This role owns | Does NOT own |
|
|
23
|
+
|----------------|--------------|
|
|
24
|
+
| Running `npm test` and interpreting it | Fixing failing tests or defects |
|
|
25
|
+
| Semantic/coherence review of tool outputs & doctrine | Writing new pack content |
|
|
26
|
+
| Driving sample eval cases | Building features |
|
|
27
|
+
| Honesty/overclaim audit | Changing doctrine to make a test pass |
|
|
28
|
+
| The release verdict (SHIP / WARN / BLOCK) | Publishing/releasing (that's the human's call after the verdict) |
|
|
29
|
+
|
|
30
|
+
**Hard rule:** never weaken a check or edit doctrine to turn a gate green. If a test is wrong, report it as a test defect; do not silence it.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## When to Trigger
|
|
35
|
+
|
|
36
|
+
- Before publishing/releasing the MCP pack or the npm package.
|
|
37
|
+
- After a substantial doctrine change (a reconciliation like the IGA API migration, a new capability, a merge of feature branches).
|
|
38
|
+
- On demand: "QA the MCP", "is the pack ready to release", "run the release gate".
|
|
39
|
+
|
|
40
|
+
## When NOT to Trigger
|
|
41
|
+
|
|
42
|
+
- Building or editing pack content (that's the authoring roles).
|
|
43
|
+
- Diagnosing a live TideCloak app (that's `tide-diagnostics`).
|
|
44
|
+
- Security-analysing a customer system (that's `tide-security-analyst`).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Execution
|
|
49
|
+
|
|
50
|
+
### Step 1 — Run the deterministic gate
|
|
51
|
+
```bash
|
|
52
|
+
cd mcp-server && npm test
|
|
53
|
+
```
|
|
54
|
+
Record the summary line and every failing check verbatim. If the harness itself crashes, that is a BLOCK (the gate is not trustworthy). If any check is red, the verdict is **BLOCK** — capture the failures and skip to the report; do not rationalize a red gate into a ship.
|
|
55
|
+
|
|
56
|
+
### Step 2 — Semantic review (only meaningful once the gate is green)
|
|
57
|
+
Call the tools through the MCP server (or read the assets directly) and judge what the grep-level gate cannot:
|
|
58
|
+
|
|
59
|
+
- **Correctness of guidance.** Read `tide_security_analysis`, `tide_hosting`, and the IGA reference (`tide_canon iga-change-requests-api`). Do the endpoints, payloads, and status codes read as internally consistent? Would an agent following them do the right thing?
|
|
60
|
+
- **Coherence after change.** For the area that changed since last release, read the primary canon + one consumer (a playbook or reference-app) and confirm they agree. (E.g. after the IGA migration: `canon/iga-change-requests-api.md` vs a bootstrap script vs `setup-iga-admin-panel` — same surface, same payloads.)
|
|
61
|
+
- **Routing.** `tide_choose_playbook` / `tide_choose_scenario` on a few real phrasings land on the right path (hosted→skycloak, "security analysis"→analyst, "org password manager"→scenario, ambiguous→disambiguation, not silent default).
|
|
62
|
+
- **Honesty audit (load-bearing).** Confirm the pack does NOT overclaim:
|
|
63
|
+
- Security analysis keeps the out-of-scope section and never claims Tide fixes injection/XSS/deps (AP-SEC-1).
|
|
64
|
+
- Hosting is not sold as fully turnkey before GAP-066 (AP-HOST-2); the Tideless-IGA caveat is present.
|
|
65
|
+
- IGA "no single bypass / tamper-evident" claims are scoped to Tide mode, not Tideless.
|
|
66
|
+
- Anything marked REQUIRES_RUNTIME_VALIDATION (e.g. the IGA bootstrap loop) is flagged as such, not asserted as verified.
|
|
67
|
+
|
|
68
|
+
### Step 3 — Drive sample eval cases
|
|
69
|
+
Pick a representative spread from `evals/cases.yaml` (a fresh-setup case, a protection case, a security-analysis case, a hosting case, an IGA governance case). For each, reason: given the pack's current content, would an agent hit the `expected_behavior` and avoid the `failure_conditions`? Note any case the pack would now fail. (This is judgment, not execution — say so.)
|
|
70
|
+
|
|
71
|
+
### Step 4 — Verdict + report
|
|
72
|
+
Emit the Release Readiness Report. One verdict:
|
|
73
|
+
- **SHIP** — gate green, no semantic blocker, no unmanaged overclaim.
|
|
74
|
+
- **SHIP_WITH_WARNINGS** — gate green; only non-blocking issues (cosmetic, low-risk doc gaps, known REQUIRES_RUNTIME_VALIDATION items that are correctly labelled). List them.
|
|
75
|
+
- **BLOCK** — any red gate check, any harness crash, any correctness or honesty defect, or a sample eval the pack would now fail. Name each blocker + the file + the fix owner (which authoring role).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Release Readiness Report (required format)
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
# Tide MCP — Release Readiness Report
|
|
83
|
+
Verdict: SHIP | SHIP_WITH_WARNINGS | BLOCK
|
|
84
|
+
Scope: <what changed since last release / full pack>
|
|
85
|
+
|
|
86
|
+
## Deterministic gate
|
|
87
|
+
Command: cd mcp-server && npm test
|
|
88
|
+
Result: <N/M passed> (PASS/FAIL)
|
|
89
|
+
Failures: <each red check verbatim, or "none">
|
|
90
|
+
|
|
91
|
+
## Semantic review
|
|
92
|
+
- Correctness: <observations, or "no issues">
|
|
93
|
+
- Coherence (changed area): <what you cross-checked → verdict>
|
|
94
|
+
- Routing: <phrasings tried → where they landed>
|
|
95
|
+
- Honesty audit: <overclaim checks → pass/fail with evidence>
|
|
96
|
+
|
|
97
|
+
## Sample eval cases
|
|
98
|
+
<case id → would-pass / would-fail (why)>
|
|
99
|
+
|
|
100
|
+
## Blockers (if any)
|
|
101
|
+
1. <defect> — <file:line> — fix owner: <authoring role/skill>
|
|
102
|
+
|
|
103
|
+
## Warnings (non-blocking)
|
|
104
|
+
- <item, why it's acceptable to ship>
|
|
105
|
+
|
|
106
|
+
## Recommendation
|
|
107
|
+
<one line: release, or fix blockers then re-run the gate>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Verification (of the QA pass itself)
|
|
113
|
+
|
|
114
|
+
- [ ] `npm test` was actually run this pass; its summary is quoted.
|
|
115
|
+
- [ ] A red gate or harness crash was reported as BLOCK (never rationalized).
|
|
116
|
+
- [ ] The honesty audit ran and its results are in the report.
|
|
117
|
+
- [ ] Every blocker names a file and a fix owner.
|
|
118
|
+
- [ ] No check was weakened and no doctrine was edited to pass the gate.
|
|
119
|
+
|
|
120
|
+
## Anti-Patterns
|
|
121
|
+
|
|
122
|
+
- **AP-QA-1** — Shipping on a red or crashed gate. The deterministic gate is a hard floor.
|
|
123
|
+
- **AP-QA-2** — Editing a test or doctrine to turn a check green instead of reporting the defect. QA reports; it does not launder failures.
|
|
124
|
+
- **AP-QA-3** — Skipping the honesty audit. Overclaiming (turnkey hosting, crypto-tamper-evidence on Tideless, "fixes XSS") is a release blocker, not a warning.
|
|
125
|
+
- **AP-QA-4** — Reporting a green gate as SHIP without semantic review. Grep-green is necessary, not sufficient.
|
|
126
|
+
- **AP-QA-5** — Treating a correctly-labelled REQUIRES_RUNTIME_VALIDATION item as a blocker. It ships as a warning; an *unlabelled* one is the blocker.
|
|
127
|
+
|
|
128
|
+
## Handoff Trace
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
[TRACE]
|
|
132
|
+
Scenario: pre-release QA of the Tide MCP pack
|
|
133
|
+
Role: MCP QA Engineer
|
|
134
|
+
Reason: <release / post-change gate>
|
|
135
|
+
Preconditions: mcp-server builds; npm test runnable
|
|
136
|
+
Verdict: <SHIP | SHIP_WITH_WARNINGS | BLOCK>
|
|
137
|
+
Next: <release, or route blockers to named authoring roles>
|
|
138
|
+
[/TRACE]
|
|
139
|
+
```
|
|
@@ -43,7 +43,7 @@ Self-encryption (this skill) is **permanently user-bound**. It cannot be upgrade
|
|
|
43
43
|
- App is not Tide-enabled. Route to `tide-setup` skill.
|
|
44
44
|
- API routes do not verify JWT yet. Route to `tide-route-and-api-protection` skill first. RBAC on unverified tokens is meaningless.
|
|
45
45
|
- User needs shared/group encryption (policy-governed VVK encryption with Forseti contracts). That is covered by playbook `setup-forseti-e2ee`, not this skill. This skill covers self-encryption only.
|
|
46
|
-
- User asks about Keycloak-to-TideCloak migration.
|
|
46
|
+
- User asks about a **direct Keycloak-to-TideCloak** migration. That specific path is not yet documented (GAP-023). (Migrating an app off generic-OIDC / NextAuth / Clerk is a separate task covered by `playbooks/migrate-from-existing-auth.md`; the open gap is the KC→TideCloak realm/data migration itself.)
|
|
47
47
|
|
|
48
48
|
**Early sharing detection**: If the user request mentions any of: "sharing", "share encrypted", "chosen recipients", "other users can decrypt", "cross-user decryption", "encrypt for others", "selective sharing" — route directly to `setup-forseti-e2ee`. Do NOT start with self-encryption and attempt to add sharing later. Self-encryption cannot be upgraded to shared encryption by renaming roles or changing parameters. The SDK call path must be different from the start. VERIFIED (session-001, F-07).
|
|
49
49
|
|
|
@@ -139,8 +139,8 @@ Self-encryption is user-bound. Only the encrypting user can decrypt their own da
|
|
|
139
139
|
- [ ] Bypassing UI (direct curl) still enforces role server-side
|
|
140
140
|
|
|
141
141
|
### Self-Encryption (if applicable)
|
|
142
|
-
- [ ] `doEncrypt('tag'
|
|
143
|
-
- [ ] `doDecrypt('tag'
|
|
142
|
+
- [ ] `doEncrypt([{ data, tags: ['tag'] }])` succeeds for user with `_tide_{tag}.selfencrypt` role
|
|
143
|
+
- [ ] `doDecrypt([{ data: ciphertext, tags: ['tag'] }])` returns original data for same user
|
|
144
144
|
- [ ] `doEncrypt` fails for user without the selfencrypt role
|
|
145
145
|
- [ ] `doDecrypt` by a different user fails (expected behavior — self-encryption is user-bound; A-27 INFERRED, not runtime-confirmed)
|
|
146
146
|
- [ ] Ciphertext stored in DB is not plaintext
|
|
@@ -181,8 +181,8 @@ Self-encryption is user-bound. Only the encrypting user can decrypt their own da
|
|
|
181
181
|
|
|
182
182
|
This is a policy-flow failure, not a role issue. See T-14.
|
|
183
183
|
|
|
184
|
-
1. Check admin policy was fetched from `GET /realms/{realm}/tide-policy-resources/admin-policy`
|
|
185
|
-
2. Check admin policy bytes were base64-decoded before use
|
|
184
|
+
1. Check admin policy was fetched (admin bearer) from `GET /admin/realms/{realm}/iga/role-policies`, reading the signed bytes from the `policy` field (the public `tide-policy-resources/admin-policy` endpoint does not exist on main)
|
|
185
|
+
2. Check admin policy bytes were base64-decoded from the `policy` field before use
|
|
186
186
|
3. Check `addPolicy(adminPolicyBytes)` was called after approval, not during request construction
|
|
187
187
|
4. Check URL construction has no double slashes (strip trailing slash from `auth-server-url`)
|
|
188
188
|
|
|
@@ -65,7 +65,7 @@ The Reviewer is optional per-step but **mandatory before handing the app to the
|
|
|
65
65
|
- [ ] Does `next.config.ts` have `strictExportPresence = false`? (not `reexportExportsPresence`)
|
|
66
66
|
- [ ] Does `next.config.ts` have the `@tidecloak/react` ESM alias? (AP-42)
|
|
67
67
|
- [ ] Does `public/tide_dpop_auth.html` exist? (I-12, L-07)
|
|
68
|
-
- [ ]
|
|
68
|
+
- [ ] Is the DPoP auth page served by a `app/tide_dpop/[...path]/route.ts` catch-all route handler (NOT `next.config.ts` rewrites) with a sha256 hash-pinned CSP (NOT `script-src 'unsafe-inline'`) and an `Allow-CSP-From: *` header? (I-12)
|
|
69
69
|
- [ ] Does `public/silent-check-sso.html` exist? (I-07)
|
|
70
70
|
- [ ] Does the redirect handler exist at the configured `redirectUri`? (I-16)
|
|
71
71
|
- [ ] Is CSP set to `frame-src '*'`? (I-06)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Role: Security Analyst (Gap Analysis)
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Analyze an **existing** system — one that may have no Tide involvement at all — and produce an honest security gap analysis that maps concrete weaknesses to the Tide capabilities that remove them. This is the pack's entry point for "here is my current app; where am I exposed, and what would Tide change?"
|
|
8
|
+
|
|
9
|
+
This role is **read-only and advisory**. It inspects and reports. It does not install Tide, write app code, or run bootstrap. When the operator accepts a finding, it hands off to the execution roles (`tide-setup`, `tide-integration`, `tide-route-and-api-protection`, `tide-rbac-and-e2ee`) via the normal team model (I-18).
|
|
10
|
+
|
|
11
|
+
It is the inverse of the rest of the pack: the pack builds Tide apps; this role audits non-Tide apps and shows where Tide fits.
|
|
12
|
+
|
|
13
|
+
## Two tiers: static, then runtime
|
|
14
|
+
|
|
15
|
+
The analysis runs in two tiers. Do the first always; do the second only with explicit authorization.
|
|
16
|
+
|
|
17
|
+
1. **Static (always)** — inspect code, config, and schema. Produces *candidate* findings, mostly tagged INFERRED. This tier never touches the running system.
|
|
18
|
+
2. **Runtime confirmation (opt-in, authorized)** — send a small number of non-destructive probes to the live target to confirm which candidates are real, upgrading INFERRED → VERIFIED (or dropping false positives). Governed entirely by `canon/security-runtime-probes.md`, including a hard authorization gate and non-exploitation limits. Do NOT run this tier unless the operator owns or is authorized to test the target and consents. When in doubt, stay static-only.
|
|
19
|
+
|
|
20
|
+
A static hypothesis and a runtime-confirmed finding are different strengths of evidence — never present the former as the latter.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Boundary
|
|
25
|
+
|
|
26
|
+
| This role owns | Does NOT own |
|
|
27
|
+
|----------------|--------------|
|
|
28
|
+
| Inspecting an existing codebase / deployment | Writing or modifying app code |
|
|
29
|
+
| Identifying trust concentrations | Running TideCloak bootstrap |
|
|
30
|
+
| Mapping gaps to Tide capabilities (`canon/security-gap-mapping.md`) | Installing the Tide SDK |
|
|
31
|
+
| Severity triage and evidence collection | Fixing the gaps it finds |
|
|
32
|
+
| Producing the gap-analysis report | Compliance review of Tide-built code (→ `tide-reviewer`) |
|
|
33
|
+
| Naming the remediation playbook sequence | Executing the remediation |
|
|
34
|
+
| Honestly scoping what Tide does NOT fix | Overstating Tide's coverage to close a sale |
|
|
35
|
+
|
|
36
|
+
**Hard rule**: This role never claims a security property Tide provides without an evidence tag and a source. It never presents Tide as fixing a gap listed in the "Out of scope" table of `canon/security-gap-mapping.md`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## When to Trigger
|
|
41
|
+
|
|
42
|
+
- "Do a security analysis of my app / system."
|
|
43
|
+
- "Where is my current auth weak?"
|
|
44
|
+
- "What would Tide change about my security posture?"
|
|
45
|
+
- "Audit this codebase for identity/key/authorization gaps."
|
|
46
|
+
- "We're evaluating Tide — show me the gaps it would close in what we have."
|
|
47
|
+
- Before a migration, to justify and scope the work (`migrate-from-existing-auth`).
|
|
48
|
+
|
|
49
|
+
## When NOT to Trigger
|
|
50
|
+
|
|
51
|
+
- Building a new Tide app → `tide-setup` + team model (I-18).
|
|
52
|
+
- Reviewing already-written Tide code for compliance → `tide-reviewer`.
|
|
53
|
+
- Diagnosing a broken Tide login → `tide-diagnostics`.
|
|
54
|
+
- Full cryptographic red-team of a Tide construction → the internal `tide-crypto-redteam` skill (different surface: it attacks Tide's own crypto, this maps a customer's gaps).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Core Doctrine
|
|
59
|
+
|
|
60
|
+
1. **A gap is a named trust concentration, not a technology.** "Uses passwords" is not a finding. "Password verification collapses to trust in one database and one server process; a dump enables offline cracking of every account" is. Every finding must name the single party or artifact that, once compromised, defeats the control.
|
|
61
|
+
|
|
62
|
+
2. **Evidence before claim.** Every finding cites a concrete artifact — file path + line, config value, schema column, or a runtime observation (an HTTP response). Tag each: **VERIFIED** (observed directly), **INFERRED** (strongly implied by observed code), **ASSUMED** (operator statement, unconfirmed). No evidence → not a finding, at most a question.
|
|
63
|
+
|
|
64
|
+
3. **Honesty is the product.** The out-of-scope section is mandatory. Presenting Tide as fixing injection, XSS, or dependency CVEs destroys the credibility of the real findings. A visibly honest analysis is more persuasive than an inflated one. (AP-SEC-1)
|
|
65
|
+
|
|
66
|
+
4. **Map to capability, not to slogans.** Each gap maps to a specific Tide mechanism with sourcing from `canon/feature-mapping.md` / `canon/invariants.md`, and to a real playbook in `playbooks/`. If no playbook exists, say so and point at `GAP_REGISTER.md` — do not improvise.
|
|
67
|
+
|
|
68
|
+
5. **Severity is about blast radius under single compromise**, per the rubric in `canon/security-gap-mapping.md`. Ask: "If exactly one party here is compromised, what does the attacker get?"
|
|
69
|
+
|
|
70
|
+
6. **Confirm the mode before claiming a cryptographic property.** IGA/QEA has two modes: Tide (`iga.attestor=tide`, licensed — cryptographic) and Tideless (`iga.attestor=simple`/unset, default — username attestation, server-enforced, no crypto). Findings SG-07/SG-14/SG-16 lean on IGA; their strong "no single bypass / tamper-evident" claims hold **only in Tide mode**. Never assert them for a Tideless realm. Same discipline for any capability whose guarantee is mode- or config-dependent — verify the target's actual configuration, don't assume the strongest variant. See the IGA-model note in `canon/security-gap-mapping.md`.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Execution
|
|
75
|
+
|
|
76
|
+
### Step 1 — Scope and consent
|
|
77
|
+
Confirm what you may inspect (repo, running instance, config) and that you are authorized to analyze it. State the boundary: read-only, no changes. If a live target is in scope, confirm it is the operator's own system or an authorized engagement.
|
|
78
|
+
|
|
79
|
+
### Step 2 — Inventory the trust architecture
|
|
80
|
+
Before grepping for patterns, answer four questions from the code/config:
|
|
81
|
+
- **Identity**: How are users authenticated? Where do credentials live?
|
|
82
|
+
- **Authority**: How are tokens/sessions signed? Who holds the signing key?
|
|
83
|
+
- **Authorization**: Where is the access decision made — client, server, or both?
|
|
84
|
+
- **Data**: What sensitive data exists, and who can read it in the clear?
|
|
85
|
+
|
|
86
|
+
### Step 3 — Run the static gap sweep
|
|
87
|
+
Load `canon/security-gap-mapping.md`. Work through SG-01 … SG-18. For each, run the detection commands against the target and record hits with evidence. Do NOT stop at the first finding — enumerate exhaustively (especially SG-05, where a sampled API audit gives false assurance).
|
|
88
|
+
|
|
89
|
+
Also collect anything matching the "Out of scope" table — you will report these separately.
|
|
90
|
+
|
|
91
|
+
### Step 4 — Triage
|
|
92
|
+
For each candidate gap:
|
|
93
|
+
- Name the trust concentration.
|
|
94
|
+
- Assign severity (CRITICAL / HIGH / MEDIUM / INFO).
|
|
95
|
+
- Attach evidence with a confidence tag (mostly INFERRED at this point).
|
|
96
|
+
- Note gap interactions (e.g. SG-02 + SG-07: one admin who also controls the signing key is worse than either alone; SG-13 nullifies SG-02's protection entirely; SG-07 + SG-14: an admin who acts *and* erases the trail).
|
|
97
|
+
|
|
98
|
+
### Step 4b — Runtime confirmation (opt-in, only if authorized)
|
|
99
|
+
If — and only if — the operator owns or is authorized to test a live target and consents, run the runtime tier per `canon/security-runtime-probes.md`. Confirm the authorization gate first. Send the small set of non-destructive probes for the candidate findings, upgrade confirmed ones to VERIFIED with a Runtime confirmation line, and downgrade/drop those a probe refutes. Never mutate data, never reach beyond the operator's own test account, never exploit an out-of-scope class to "confirm" it. If not authorized, skip this step and keep findings at their static confidence.
|
|
100
|
+
|
|
101
|
+
### Step 5 — Map to Tide
|
|
102
|
+
For each in-scope gap, pull the Tide replacement and remediation path from `canon/security-gap-mapping.md`. Verify each named playbook exists (`tide_list playbooks`). Carry the **honesty note** for each — the limits of the Tide replacement are part of the finding, not an appendix.
|
|
103
|
+
|
|
104
|
+
### Step 6 — Report
|
|
105
|
+
Emit the report in the format below. Lead with the trust-architecture summary and the highest-severity findings. Put the honest out-of-scope section in the body, not buried.
|
|
106
|
+
|
|
107
|
+
### Step 7 — Hand off
|
|
108
|
+
Offer the remediation sequence. If the operator accepts, hand to the execution roles via I-18 (Scenario Resolver → Setup → Application → Security → IAM/Policy → Reviewer). Do not begin remediation from this role.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Report Format
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
# Tide Security Gap Analysis — <system name>
|
|
116
|
+
Scope: <what was inspected> | Authorization: <confirmed by whom>
|
|
117
|
+
Method: static (repo) | both (static + runtime) — list which SG findings were probed
|
|
118
|
+
|
|
119
|
+
## Trust Architecture
|
|
120
|
+
- Identity: <where credentials live>
|
|
121
|
+
- Authority: <who holds signing keys>
|
|
122
|
+
- Authorization: <where the decision is made>
|
|
123
|
+
- Sensitive data: <what, readable by whom>
|
|
124
|
+
|
|
125
|
+
## Findings (most severe first)
|
|
126
|
+
### [SEVERITY] SG-XX — <title>
|
|
127
|
+
- Trust concentration: <the single party/artifact>
|
|
128
|
+
- Evidence: <file:line / config / HTTP obs> [VERIFIED|INFERRED|ASSUMED]
|
|
129
|
+
- Runtime confirmation: <exact request → observed response> [VERIFIED] (omit if static-only or not probed)
|
|
130
|
+
- Failure scenario: <what one compromise yields>
|
|
131
|
+
- Tide replacement: <mechanism> (source: canon/...) [sourcing tag]
|
|
132
|
+
- Remediation: <playbook sequence>
|
|
133
|
+
- Limits: <honesty note — what this does NOT cover>
|
|
134
|
+
|
|
135
|
+
## Gap Interactions
|
|
136
|
+
<pairs that compound, e.g. SG-02 + SG-07>
|
|
137
|
+
|
|
138
|
+
## Not Addressed by Tide (found in scope)
|
|
139
|
+
<injection / XSS / deps / rate-limiting / etc., with standard remediation pointers>
|
|
140
|
+
<or: "None observed in the inspected scope.">
|
|
141
|
+
|
|
142
|
+
## Recommended Remediation Sequence
|
|
143
|
+
1. <playbook> — closes <SG-XX>
|
|
144
|
+
2. ...
|
|
145
|
+
Handoff: <execution role to engage first, per I-18>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Verification
|
|
151
|
+
|
|
152
|
+
This analysis is complete only when:
|
|
153
|
+
- [ ] Every SG in `canon/security-gap-mapping.md` (SG-01 … SG-18) was checked (not sampled), or its non-applicability is stated.
|
|
154
|
+
- [ ] Every finding names a trust concentration and carries evidence with a confidence tag.
|
|
155
|
+
- [ ] Every Tide-replacement claim cites `canon/feature-mapping.md` or `canon/invariants.md`.
|
|
156
|
+
- [ ] Every remediation names a playbook that actually exists.
|
|
157
|
+
- [ ] The out-of-scope section is present and non-empty (or explicitly states none found).
|
|
158
|
+
- [ ] No finding claims Tide fixes an out-of-scope gap class.
|
|
159
|
+
- [ ] If the runtime tier ran: authorization was confirmed, every probe was non-destructive and confined to the operator's own account, and probed findings carry a Runtime confirmation line. Unprobed findings remain at their static confidence.
|
|
160
|
+
|
|
161
|
+
## Anti-Patterns
|
|
162
|
+
|
|
163
|
+
- **AP-SEC-1** — Presenting Tide as fixing injection/XSS/CSRF/deps/rate-limiting. Kills credibility of the real findings.
|
|
164
|
+
- **AP-SEC-2** — A trust-concentration claim with no evidence artifact. Every finding needs a file, config value, or runtime observation.
|
|
165
|
+
- **AP-SEC-3** — Sampling APIs instead of enumerating them (SG-05). A "clean" sampled audit is worse than no audit — it manufactures false assurance.
|
|
166
|
+
- **AP-SEC-4** — Relocating a single point instead of removing it and calling it a fix. Moving a JWT secret from env to KMS is not what SG-02 asks for; threshold signing removes the whole-key.
|
|
167
|
+
- **AP-SEC-5** — Overstating severity to motivate migration. Report SG-10 (remote JWKS over TLS) as a hardening delta of the Tide model, not as a live vulnerability, unless TLS is also broken.
|
|
168
|
+
- **AP-SEC-6** — Beginning remediation from this role. Analyst reports; execution roles build. Do not blur the boundary.
|
|
169
|
+
- Runtime-tier anti-patterns (AP-SEC-7 … AP-SEC-10) live in `canon/security-runtime-probes.md`: no unauthorized probing, no mutating "checks", no escalation into exploitation, no runtime-level confidence on an unprobed hypothesis.
|
|
170
|
+
|
|
171
|
+
## Handoff Trace
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
[TRACE]
|
|
175
|
+
Scenario: security gap analysis of existing (non-Tide) system
|
|
176
|
+
Role: Security Analyst
|
|
177
|
+
Reason: <operator requested audit / migration scoping>
|
|
178
|
+
Preconditions: read-only scope confirmed, authorization confirmed
|
|
179
|
+
Findings: <N gaps, severities>
|
|
180
|
+
Next: <Setup / Scenario Resolver for remediation, or "report only — awaiting operator decision">
|
|
181
|
+
[/TRACE]
|
|
182
|
+
```
|
|
@@ -33,7 +33,7 @@ If multiple paths are plausible and the repo does not resolve it, ask the user b
|
|
|
33
33
|
## When NOT to Trigger
|
|
34
34
|
|
|
35
35
|
- App already has working Tide auth (provider present, adapter JSON loaded, login functional). Proceed to route-and-api-protection or rbac-and-e2ee skill instead.
|
|
36
|
-
- User is asking about
|
|
36
|
+
- User is asking about a **direct Keycloak-to-TideCloak** migration. That specific path is not yet documented (GAP-023) — say so and stop. (Retrofitting an app off generic-OIDC / NextAuth / Clerk is a different task, covered by `playbooks/migrate-from-existing-auth.md`; the open gap is the KC→TideCloak realm/data migration itself.)
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
File without changes
|
package/canon/delegation.md
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
# Server-Side Delegation - Canon
|
|
2
|
-
|
|
3
|
-
Canonical reference for server-side admin API calls via delegation tokens.
|
|
4
|
-
|
|
5
|
-
When your app server needs to call TideCloak admin APIs (manage users, roles, policies), it cannot use the browser's DPoP-bound access token. The delegation flow issues a new token bound to the server's ephemeral key.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Packages
|
|
10
|
-
|
|
11
|
-
| Package | Side | Purpose |
|
|
12
|
-
|---------|------|---------|
|
|
13
|
-
| `@tidecloak/server` | Server | `TideDelegation` class, middleware, DPoP proof generation |
|
|
14
|
-
| `@tidecloak/js` | Browser | `createTideFetch` wrapper, `signDelegationRequest`, `signDpopApproval` |
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Flow: Forgetful Interrupt Pattern
|
|
19
|
-
|
|
20
|
-
1. Browser calls server endpoint (e.g., `GET /api/admin/users`)
|
|
21
|
-
2. Server `requireDelegation()` middleware checks cache. No cached token.
|
|
22
|
-
3. Server generates ephemeral Ed25519 key, responds **419** with challenge
|
|
23
|
-
4. `createTideFetch` intercepts 419, calls `IAMService.signDelegationRequest(payload)` and `IAMService.signDpopApproval(serverJkt)`
|
|
24
|
-
5. Browser POSTs signed artifacts to `/api/delegation`
|
|
25
|
-
6. Server `handleDelegation()` exchanges with TideCloak token endpoint
|
|
26
|
-
7. TideCloak validates chain of trust, signs delegation token via ORK network
|
|
27
|
-
8. Server caches delegation token, responds `{ delegated: true }`
|
|
28
|
-
9. `createTideFetch` retries original request. Server finds cached token, proceeds.
|
|
29
|
-
|
|
30
|
-
Subsequent requests use the cached token until it expires.
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Setup
|
|
35
|
-
|
|
36
|
-
### Server
|
|
37
|
-
|
|
38
|
-
```js
|
|
39
|
-
import { TideDelegation } from '@tidecloak/server';
|
|
40
|
-
|
|
41
|
-
const delegation = new TideDelegation({
|
|
42
|
-
tidecloakUrl: process.env.TIDECLOAK_URL,
|
|
43
|
-
realm: process.env.TIDECLOAK_REALM,
|
|
44
|
-
clientId: process.env.TIDECLOAK_CLIENT_ID,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Exchange endpoint
|
|
48
|
-
app.post('/api/delegation', authenticate, delegation.handleDelegation());
|
|
49
|
-
|
|
50
|
-
// Protected route with delegation
|
|
51
|
-
app.get('/api/admin/users',
|
|
52
|
-
authenticate,
|
|
53
|
-
delegation.requireDelegation(),
|
|
54
|
-
async (req, res) => {
|
|
55
|
-
const users = await req.delegation.fetch(adminUrl + '/users');
|
|
56
|
-
res.json(users);
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Browser
|
|
62
|
-
|
|
63
|
-
```js
|
|
64
|
-
import { createTideFetch } from '@tidecloak/js';
|
|
65
|
-
|
|
66
|
-
const appFetch = createTideFetch(window.fetch);
|
|
67
|
-
|
|
68
|
-
// Delegation is transparent
|
|
69
|
-
const users = await appFetch('/api/admin/users');
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Scoped Delegation
|
|
75
|
-
|
|
76
|
-
Limit roles in the delegation token. If `roles` is specified, only listed roles/clients appear. Everything else is stripped.
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
// Only read role for my-app client
|
|
80
|
-
delegation.requireDelegation({
|
|
81
|
-
roles: {
|
|
82
|
-
clients: { 'my-app': ['read'] }
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
// Realm + multiple clients
|
|
87
|
-
delegation.requireDelegation({
|
|
88
|
-
roles: {
|
|
89
|
-
realm: ['admin'],
|
|
90
|
-
clients: {
|
|
91
|
-
'my-app': ['read', 'write'],
|
|
92
|
-
'other-app': ['view']
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// No filtering (default)
|
|
98
|
-
delegation.requireDelegation()
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Rules:
|
|
102
|
-
- Omitting `roles` entirely keeps all user roles
|
|
103
|
-
- Once `roles` is specified, unlisted realms/clients are stripped
|
|
104
|
-
- Cannot escalate. Requesting a role the user does not have silently drops it.
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Token Exchange Endpoint
|
|
109
|
-
|
|
110
|
-
The server exchanges artifacts at TideCloak's standard OIDC token endpoint:
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
POST /realms/{realm}/protocol/openid-connect/token
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Parameters (form-encoded):
|
|
117
|
-
- `grant_type`: `urn:ietf:params:oauth:grant-type:token-exchange`
|
|
118
|
-
- `client_id`: app client ID
|
|
119
|
-
- `subject_token`: user's access token
|
|
120
|
-
- `subject_token_type`: `urn:ietf:params:oauth:token-type:access_token`
|
|
121
|
-
- `actor_token`: browser-signed delegation request JWT
|
|
122
|
-
- `actor_token_type`: `urn:ietf:params:oauth:token-type:jwt`
|
|
123
|
-
- `dpop_approval`: session key approval of server's DPoP key
|
|
124
|
-
|
|
125
|
-
Headers:
|
|
126
|
-
- `DPoP`: server's DPoP proof JWT (Ed25519, contains server's public key)
|
|
127
|
-
|
|
128
|
-
Response: `{ access_token, token_type: "DPoP", expires_in, issued_token_type }`
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Chain of Trust
|
|
133
|
-
|
|
134
|
-
Two-hop validation ensures the delegation token is legitimately authorized:
|
|
135
|
-
|
|
136
|
-
**HOP 1**: `subject_token.cnf.jkt == thumbprint(BROWSER_KEY from actor_token header)`
|
|
137
|
-
The browser that owns the access token is the same one that signed the delegation request.
|
|
138
|
-
|
|
139
|
-
**HOP 2**: `actor_token.cnf.jkt == thumbprint(SERVER_KEY from DPoP header)`
|
|
140
|
-
The delegation request authorizes the server's specific ephemeral key.
|
|
141
|
-
|
|
142
|
-
Validated at two layers:
|
|
143
|
-
1. TideCloak authenticator (Java) - before token is built
|
|
144
|
-
2. ORK signing model (C#) - before token is threshold-signed
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## Using req.delegation.fetch()
|
|
149
|
-
|
|
150
|
-
The `fetch` helper on `req.delegation` handles DPoP proof generation and Authorization headers:
|
|
151
|
-
|
|
152
|
-
```js
|
|
153
|
-
// GET
|
|
154
|
-
const users = await req.delegation.fetch(url);
|
|
155
|
-
|
|
156
|
-
// POST with JSON
|
|
157
|
-
await req.delegation.fetch(url, { method: 'POST', body: { name: 'role' } });
|
|
158
|
-
|
|
159
|
-
// PUT
|
|
160
|
-
await req.delegation.fetch(url, { method: 'PUT', body: { enabled: true } });
|
|
161
|
-
|
|
162
|
-
// DELETE
|
|
163
|
-
await req.delegation.fetch(url, { method: 'DELETE' });
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## Anti-Patterns
|
|
169
|
-
|
|
170
|
-
- **Do not use master admin credentials** for server-side admin calls. Use delegation.
|
|
171
|
-
- **Do not forward the user's browser token** to admin API. It is DPoP-bound to the browser's key.
|
|
172
|
-
- **Do not skip DPoP on delegation requests**. The delegation token is DPoP-bound to the server's ephemeral key. Every admin API call needs a fresh DPoP proof with `ath` claim.
|
|
173
|
-
- **Do not share delegation tokens across users**. They are cached per session ID.
|
|
174
|
-
- **Do not hardcode server keys**. They are ephemeral Ed25519 keys, generated per session.
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Prerequisites
|
|
179
|
-
|
|
180
|
-
- TideCloak with `tide-chain-of-trust` client authenticator enabled (added by default in recent TideCloak builds)
|
|
181
|
-
- `DelegationToken:1` in the VRK model whitelist (requires VRK rotation after adding)
|
|
182
|
-
- DPoP enabled on the client
|
|
183
|
-
- `@tidecloak/server` installed on the backend
|
|
184
|
-
- `createTideFetch` wrapping fetch on the frontend
|
|
185
|
-
- An `authenticate` middleware that sets `req.accessToken` from the Authorization header
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Invariants
|
|
190
|
-
|
|
191
|
-
- Delegation tokens are short-lived (max 600s / 10 minutes)
|
|
192
|
-
- Server key is ephemeral per session, cleared when delegation cache expires
|
|
193
|
-
- The browser must approve each server key via ORK enclave (DPoP approval)
|
|
194
|
-
- Scoped delegation can only reduce roles, never add them
|
|
195
|
-
- Delegation tokens have `act: { sub: clientId }` per RFC 8693
|