Boundary
Account to draft
Observed effect
Unauthorized draft changes
Engineering focus
Object and action authorization

A denied read, a changed draft

The first response looked right. Two controlled accounts were involved: one owned the draft, and the other had no permission to read it. The second account's read was denied.

The problem showed up in the owner's view. After a separate write by the unrelated account, the owner opened the draft and saw the changed contents. This wasn't just a generous success message. The application had saved an edit from someone it wouldn't let read the same object.

Another operation changed the order of the draft's contents. That mattered because it extended the review beyond a single save action. Editing and rearranging reached the same resource through different paths, and neither enforced the expected permission in the recorded checks.

The draft identifier was already known. Nothing in the notes demonstrates finding unknown identifiers, or progressing the draft to a completed, sent, or signed result. The finding was narrower and concrete: an unrelated controlled account could change stored work that belonged to the other account.

The HTTP notebook

The denied read was the useful baseline. It showed that the unrelated account should not have access to the draft. The later state change described above is the finding; this notebook illustrates the read control, not the separate mutation.

Baseline control / HTTP/1.1SYNTHETIC + REDACTED

Read-only request

GET /redacted/draft HTTP/1.1
Host: research.example
Cookie: session=[REDACTED]
Accept: application/json

Illustrative denied read

HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: private, no-store
Content-Length: 25

{"error":"access_denied"}

Reconstructed for this article, not an original wire capture. The host uses the reserved .example namespace; paths, headers, and data are invented or redacted. Display only: no requests are sent.

A response code alone would not prove the write-side failure. The owner's independent view of changed state supplied that part of the evidence.

What the owner's check established

Read and write permissions answer different questions. A denied read tells you the account cannot retrieve the draft through that path. It says nothing about the checks made when an edit, reorder, or later workflow action arrives.

Authentication doesn't close that gap. It identifies the account making the request. An anti-CSRF check helps tie the request to its browser session. The server still needs to ask whether that account may perform this particular action on this particular draft.

The notes don't reveal a missing function, a middleware order, or any other internal cause. They show what the application allowed. The two write operations crossed a boundary that the read operation respected.

That is enough to report a loss of draft integrity. Whether an altered draft could later be sent or approved depends on rules that weren't established here. Those are follow-up questions, not additional impact already proved.

Closing the authorization gap

The denied read was already doing its job. That protection did not carry over to the tested writes. The proposed repair belongs wherever the application saves a draft change, including operations that rearrange its contents.

Before saving, the server should check whether the signed-in account may perform this action on this draft. Knowing the identifier is not permission to edit it. Every write path should use the same deny-by-default policy, with any referenced section or attachment checked against the authorized draft.

Sharing still needs to work. An invited editor may be allowed to save, while a viewer must remain read-only. Both cases matter: blocking everyone except the owner could stop valid collaboration without proving that every write path is covered.

Follow the change into background work

Sections and attachments deserve a check of their own. A valid draft and a valid attachment aren't necessarily a valid pair. Before saving, confirm that every referenced child belongs to the authorized draft and satisfies any extra permission rules.

Background work adds a timing problem. An editor might queue a change, then lose access before a worker applies it. The work item should retain the initiating account, draft, and permitted action; the worker should check the current policy before committing its result. The worker's own service account is not a substitute for the editor's permission.

Removed sharing, changed ownership, and a draft that has moved to a new state can all make queued work invalid. These are repair-review questions. The source notes do not establish that a queue or worker caused this finding.

Five checks before calling it fixed

These local tests use synthetic fixtures in a corrected application. They assert policy and stored state, so a changed response message alone cannot satisfy them.

Local regression checks for the proposed draft policy
FixtureLocal actionRequired assertions
Unrelated account and owned draftExercise the edit policy with the unrelated principal.Denied; contents, ordering, version, and queued work remain unchanged.
Draft with an explicitly shared editorSave a permitted change through the application service.Allowed; only the intended draft changes and the editor is attributed.
Authorized parent with a foreign child fixtureValidate a change that includes the mismatched child.The complete change is rejected; neither object is modified.
Queued work whose editor permission was removedRun the local worker after revoking the grant.Fresh policy denies application; the principal and resource binding is retained.
Shared viewer with read-only accessRead the fixture and evaluate each draft mutation action.Reading remains allowed; every mutation is denied without state changes.

What a retest still needs to show

A retest should cover save, reorder, child changes, and deferred work wherever those exist. It also needs an invited editor who can still edit and a viewer who cannot. Passing only the denied-owner case would leave both coverage and legitimate sharing unresolved. No completed retest is claimed here.

The useful question isn't whether the draft screen is protected. It's whether every place that saves a change checks who is allowed to make it.

Related case: When two browsers inherited one cart examines how navigation state became session authority. For planning and reporting advice, browse the Guides.

Who can change what in your application?

Shared editors, viewer roles, and background jobs are part of our SaaS penetration testing. Tell us how sharing works in your product and when you need it tested.

Request a pentest