All Posts

Test app permissions before you launch

· Zero to Hundred

Test app permissions by checking who can read each private record and who can change it. Use two controlled accounts with different access, write down the expected decisions, and check the server's response. After a denied change, confirm the stored record stayed unchanged.

For a Buyer running software they didn't write, this is a useful check between getting a copy running and putting real customer data into it. It adds evidence about specific permissions. Passing a small checklist doesn't establish that the whole app is secure.

What should each account be allowed to do?

Write the permission rules before testing them. For every important record or action, name the account's role, its workspace, and whether access should be allowed. A successful login establishes identity; it doesn't establish permission to use every feature. OWASP's authorization guidance makes that distinction and recommends checking permissions on every request.

Consider a hypothetical app for private quote requests. Alice belongs to Workspace A. Ben belongs to Workspace B. Cara can view Workspace A's requests but can't edit them. The app's intended policy could look like this:

Account and actionExpected result under this example policy
Alice reads a request in Workspace AAllowed
Ben reads that same private requestDenied
Alice edits a request in Workspace AAllowed
Ben edits that requestDenied; stored values unchanged
Cara reads a request in Workspace AAllowed
Cara edits itDenied; stored values unchanged
A signed-out visitor opens the private requestDenied
A visitor opens an intentionally public pageAllowed

This policy is a made-up example. Write the rules for your own app. A shared team record may legitimately be visible to several accounts. A private personal record may need a narrower rule.

If you're still deciding what the software needs to support, the CRM planning guide explains why record permissions belong in the scope from the start.

How do you prepare accounts and records for the check?

Use an isolated copy you're authorized to test, accounts you control, and synthetic records. Keep live payments, real email delivery, and customer data out of this exercise. Record the source version and configuration so you know which copy the results describe.

Create a clearly labelled request under Alice's account. Give Ben a different request so you can verify both accounts work normally. Use separate browser profiles or containers for their sessions. Switching tabs in the same signed-in browser may leave both tabs using the same account.

Check an allowed action first. Alice should be able to open her example request. Ben should be able to open his. If neither works, fix the setup before interpreting a denial as successful access control.

Two accounts cover one relationship. Add accounts for the other roles your app supports, such as Cara's viewer role and an administrator. Record missing roles as untested rather than extending a result from one pair to everyone.

Can another account open the same record directly?

Check a private record's direct page and the request that supplies its data, using an account that the policy excludes. Hiding the record from the navigation doesn't establish that the server protects it.

In the example, save Alice's synthetic request link. Open that same link in Ben's separate session. Check the response body as well as the screen: it shouldn't contain Alice's private request details. If the interface loads data through an API, have the person responsible for the software check that data request under Ben's session too.

OWASP's object-level authorization guidance explains that an endpoint acting on a record identifier must check the caller's permission for that record and action. A long random identifier doesn't replace that check.

Repeat the relevant checks for search results, exports, and attached files if the app has them. A protected detail page can coexist with an export that includes the wrong workspace's records. Use only the synthetic data and routes within your authorized copy.

How do you check that a denied write changed nothing?

Record the original value, attempt the disallowed change, then read the record again through an authorized account. An error banner alone doesn't tell you whether the server changed data before reporting failure.

For the hypothetical request, Alice records its title. Ben attempts a title change that the policy forbids. Alice then reloads the request and confirms that its title is unchanged. A developer should also check for related side effects, such as an incorrectly queued notification or changed workspace assignment.

Separate permission to edit a record from permission to edit every field in it. An ordinary member might be allowed to change a request's description while being forbidden from changing its workspace or approval status. OWASP's property-level authorization guidance recommends limiting returned fields and accepting changes only to fields the caller may update.

For each important boundary, keep an allowed case beside the denied case. Otherwise, an app that rejects every write could appear to pass the checklist while being unusable.

What about administrator actions and removed access?

Test sensitive actions using a role that lacks permission, even when the interface hides the button. OWASP's function-level authorization guidance covers this boundary between ordinary users and privileged functions. Check the app's actual roles and actions; a route's name doesn't establish who may use it.

Include changes to access in the written policy. For example, what should happen after a member is removed from a workspace? Check that behavior with an already-open session and with a fresh sign-in. Record when the system is designed to apply revocation, then compare the result with that rule. Continued access to private data or privileged writes needs an explanation and review before real use.

Don't assume every denial must return the same HTTP status. The intended response may differ for a signed-out user, a forbidden action, or a hidden record. What matters is that the response follows the app's policy, discloses no protected data, and causes no unauthorized change.

What evidence should you keep before launch?

Keep a short result log tied to the version you checked. Record the account's role and workspace, the synthetic record, the action, the expected result, and what actually happened. For writes, include the saved value before and after. Leave results blank until you've performed the check.

CaseEvidence to record
Allowed read or writeThe intended account can complete the action
Denied readResponse checked; protected data absent
Denied writeResponse checked; original stored values preserved
Role or membership changeExisting and fresh sessions checked against the revocation policy
Untested pathWhat's missing, who will check it, and when

Redact session tokens, credentials, and personal data before sharing a report. Give the person fixing a failure enough detail to repeat it with synthetic accounts. After a fix, repeat both the denied case and the corresponding allowed case on the corrected version.

Stop short of real customer data when a private record crosses a forbidden boundary, an unauthorized change persists, or an important permission rule remains unexplained. Complex roles, sensitive data, and multiple access paths may need a qualified security review beyond this exercise.

For a purchase, keep this log with your broader app-evaluation checklist. The checklist covers what can be established before payment, what needs source access, and what remains before launch. When you browse software, include the work needed to check these permissions in your plan.

Zero to Hundred

The marketplace where shipped becomes sold.

Get product news from Zero to Hundred

© 2026 Zero to Hundred. All rights reserved.