S2 Sample · Companion extract

The Cloudwrit Signature Security Handbook

Lunch-length companion extract.

Fifteen pages, cross-sectioned from the full handbook shipped to Cloudwrit in March 2026. Executive summary, one findings chapter (session cookie hardening), one architectural chapter (IAM surface), and the first quarter of the remediation roadmap. Every page has been through the Cloudwrit editorial consent pass.

Client
Cloudwrit (anonymized, fictional)
Engagement
Signature Security Engagement - Growth tier
Period
2026-02-16 to 2026-03-27
Version
v1.3 (companion extract)
Testers
the operator, lead. Two contract specialists (anonymized).
Editorial
the operator, reviewed 2026-03-25. All pages.
AI drafting
Claude Opus (model version noted per chapter in full handbook)
Distribution
Cloudwrit engineering leadership plus oncall rota, access-controlled
I Executive summary

What happened, what is at stake, what to do next.

Cloudwrit is a Series A content-operations platform with 28 engineering staff, 180 customers, and approximately 3.2 million dollars of annual recurring revenue. This engagement ran for six weeks and produced the findings documented in Chapters III through VII.

The most important observation, stated plainly, is this: a determined attacker with access to a single reused password from one of Cloudwrit's customers could reach administrative billing functions within four steps, using no novel exploits, in under two hours. The attack path is documented in Chapter IV. The patches are in Chapter VIII. We believe all four steps are individually closeable in the next two sprints, and the path is closeable as a whole within the quarter.

At stake.

Cloudwrit's customer data includes draft manuscripts, internal corporate content, and a non-trivial volume of attorney-client privileged material. A breach of this data would be a material event. The regulatory exposure is moderate in Cloudwrit's current footprint; the reputational exposure is substantial, because Cloudwrit's customers specifically chose the platform on the basis of its trust posture.

A rough estimate of the direct financial impact of the attack path, were it fully exploited, is between $380,000 and $1.6 million. This range covers incident response, customer communication, regulatory notification, and the contractual remedies in Cloudwrit's enterprise MSA. It does not cover the revenue impact of customer churn.

What to do next.

The remediation roadmap in Chapter VIII sequences 41 findings into four waves. Wave one (two weeks) closes the four steps of the attack path and raises the baseline enough to take the most urgent risks off the table. Wave two (quarter 2) hardens the IAM surface described in Chapter V. Wave three (quarter 3) invests in detection. Wave four (quarter 4) is the continuous posture program that, going forward, replaces annual pentests with quarterly targeted engagements.

Total estimated effort across the four waves, at Cloudwrit's current team sizing, is approximately 38 engineering weeks distributed over twelve months, plus two consulting engagements (wave 2 IAM review, wave 3 detection build) that we have scoped and priced in Chapter IX.

What we did not test.

Client-side attacks on the web editor (JavaScript, browser extensions) were out of scope by agreement. The mobile application was out of scope. The third-party AI summarization service that Cloudwrit uses for their Summary feature was black-boxed; we tested the integration surface, not the vendor's internals. We recommend a follow-up engagement on the web-editor surface in Q3 2026 and an architectural review of the vendor integration when Cloudwrit's usage volume doubles, projected for Q4.

III.4 Findings · Session cookie hardening

Three findings that make up the cookie story.

Cloudwrit's session cookies are the single most-touched secret in the application. They are set at login, rotated on privilege change, and cleared on logout. Across those three lifecycle events we found three findings, two of which participate in the documented attack path.

F-07: Session cookie lacks SameSite attribute on /billing/* routes.

Severity
Medium. Exploitable via CSRF in a chained attack but requires user interaction.
Where
src/server/middleware/session.ts:42 and the router registration in src/server/routes/billing/index.ts:18.
Impact
A user with an active Cloudwrit session visiting a malicious page can have billing POSTs issued on their behalf. In the observed attack path, this is step 3 of 4.
Reproduce
curl -b "sid=VALID_SID" -X POST https://app.cloudwrit.io/billing/refund -d "amount=500&currency=USD"
Evidence
reports/F-07/request.har, reports/F-07/session-dump.txt, reports/F-07/attack-video.mp4 (redacted).
Fix
Add SameSite=Strict to the cookie options at session.ts:42. The two-line diff is in reports/F-07/patch.diff. This change does not affect the Cloudwrit iframe embed because the iframe already posts via the API token header, not the session cookie.
Verify
The test in reports/F-07/verify.test.ts asserts the Set-Cookie header includes SameSite=Strict for all login and refresh flows. Merge into the CI suite.

F-08: Session cookie retained beyond logout on /billing/* routes.

Severity
High. Exploitable without user interaction if the attacker obtains a post-logout cookie.
Where
src/server/auth/logout.ts:29. The Set-Cookie: sid=; Max-Age=0 header is missing on the billing subtree because billing was shipped under its own logout handler that predates the shared auth middleware.
Impact
A stolen cookie remains valid against /billing/* even after the user logs out. This is step 4 of the attack path.
Reproduce
Capture a cookie. Log out. Retry any /billing/* endpoint with the captured cookie. Observe that the request succeeds for up to the session TTL of 7 days.
Evidence
reports/F-08/capture-logout-retry.har, reports/F-08/timeline.md.
Fix
Consolidate billing logout under the shared authLogoutMiddleware. The refactor is approximately 40 lines across three files. Diff in reports/F-08/patch.diff. Staging validation passed on the Cloudwrit staging environment on 2026-03-21.
Verify
Integration test in reports/F-08/verify.test.ts exercises login-then-logout-then-billing-call and asserts 401 from all billing endpoints.

F-09: Session cookie rotation not triggered on privilege change to admin.

Severity
Low. Not part of the documented attack path. Hygiene-level issue.
Where
src/server/auth/privilege-change.ts:71.
Impact
An attacker who has captured a pre-admin cookie and later observes the victim's privilege rise to admin can retain the same cookie at admin level. Requires a specific prior-compromise condition, so graded Low.
Reproduce
Login as a non-admin. Capture cookie. Have the account elevated to admin. Retry administrative endpoints with the captured cookie. Succeeds.
Fix
Call the existing rotateSession helper inside the privilege-change flow. One-line addition. See reports/F-09/patch.diff.
Verify
Test in reports/F-09/verify.test.ts asserts a different cookie value post-elevation.

Decision log · why SameSite=Strict and not Lax.

Cloudwrit asked us on 2026-03-18 whether Lax would be sufficient. In theory Lax defends against CSRF for the observed attack class. In practice we chose Strict because the Cloudwrit iframe embed already has its own API-token authentication, which means the strict setting does not break any user-visible flow, and the additional protection from Strict over Lax is free. Decision rationale recorded at reports/decisions/D-11.md.

V.2 Architecture · The IAM surface

Where the permissions actually live.

Cloudwrit's IAM surface spans three planes: AWS IAM for infrastructure, a bespoke application RBAC layer for per-tenant access, and a small set of Stripe and Zendesk cross-account roles for third-party vendor access. This chapter describes the three planes, the trust relationships between them, and the three places where a reader should expect risk.

5.2.1 AWS IAM.

Cloudwrit runs in a single AWS account with twelve production IAM roles and approximately forty service accounts. The roles were created over the past eighteen months with varying levels of review. Our finding F-12 (see Chapter III.6) documents the three roles that carry permissions beyond what their workloads need. Of these, two are load-bearing and one is legacy. The legacy role (bamboo-deploy-2023) is unused since the migration to GitHub Actions and should be deleted; the deletion path is scripted in reports/F-12/cleanup.sh.

5.2.2 Application RBAC.

The application RBAC layer implements a role matrix with four roles (viewer, editor, admin, superadmin) across six resource types. The matrix is stored as a TypeScript constant in src/server/auth/rbac.ts and enforced at the route handler layer. Of the 74 routes in the application, 72 have correctly declared RBAC guards; the remaining two are the subject of findings F-14 and F-15.

The matrix itself is correct and follows a least-privilege model, with one intentional exception: the billing/refund endpoint is accessible to admin-level users (not only superadmin) because Cloudwrit's customer-support team, who operate at admin level, is authorized to issue partial refunds up to $500. This is documented in decision log D-04, with the compensating control being an audit log entry on every refund, retained for seven years.

5.2.3 Third-party cross-account roles.

Cloudwrit uses two third-party vendor roles: Stripe for payment orchestration and Zendesk for customer-support ticketing. Both roles are defined with external ID trust policies, consistent with AWS's published guidance. We verified the trust policies are correctly scoped on both (F-16 examines a slightly broader scope than strictly required on the Zendesk role; the finding is Low severity and is easy to tighten).

5.2.4 Where the risk concentrates.

Three places.

First, the AWS roles that connect deployment pipelines to production. A compromise of a deployment token gives broad access. Cloudwrit has short-lived OIDC-issued tokens, which is the right architecture; the remaining gap is that a small number of long-lived tokens still exist for emergency manual deploys. Finding F-13 proposes retiring them in favor of a break-glass procedure.

Second, the billing subtree. The RBAC matrix correctly scopes billing routes, but the session-cookie findings in Chapter III.4 mean that an attacker who obtains any authenticated session can reach billing under the conditions we documented. Closing F-07 and F-08 removes this concern.

Third, the cross-account vendor roles. The Zendesk role is over-scoped by a small margin. Stripe is fine. The cross-account path is not part of any observed attack path but it sits in the set of assets that would become interesting if an attacker reached AWS; we recommend tightening F-16 in wave 2.

Attack-path diagram

Reused password (customer leak) Dormant account (no 2FA, step 2) Session capture (F-07, step 3) Billing refund (F-08, step 4)
VIII.1 Roadmap · Wave 1 (first two weeks)

The two-week wave that closes the attack path.

Wave 1 is four findings, all closeable inside the current sprint plus the next. We have sequenced them to minimize merge conflicts and to let the verification step of each one inform the fix for the next.

  1. F-07 SameSite=Strict on /billing/* session cookie ~0.5 day

    Two-line middleware change plus one CI test. Close first because every subsequent fix assumes this is in place. Owner: Priya (engineering). Reviewer: Karim (security). Merge window: sprint current.

  2. F-08 Consolidate billing logout under shared auth middleware ~1.5 days

    Refactor of ~40 lines across three files. The staging validation passed during the engagement. Second in sequence because this fix removes the post-logout reuse window that F-07 assumes. Owner: Karim. Reviewer: Priya. Merge window: sprint current + 1.

  3. F-22 Enforce 2FA on all admin-eligible accounts ~1 day

    Adjusts the account-provisioning flow to require 2FA enrollment before the account's first admin elevation. Existing accounts are given a 14-day grace. Cuts step 2 of the attack path. Owner: Priya. Reviewer: Shivam (support lead, because support is the most-affected user class). Merge window: sprint current + 1.

  4. F-13 Retire long-lived AWS deploy tokens in favor of break-glass ~1.5 days

    Removes the small set of long-lived AWS tokens kept for emergency manual deploys. Replaces with a signed break-glass workflow documented in the runbook. Owner: Karim. Reviewer: Priya. Merge window: sprint current + 1. This is included in wave 1 because it removes a latent compromise path even though it is not in the observed attack path.

At the end of wave 1, the attack path documented in Chapter IV is fully closed. The waves that follow address the broader hardening program. See Chapter VIII.2 and VIII.3 for wave 2 (IAM hardening, Q2) and wave 3 (detection build, Q3).