A release can be secure when every component is old and secure when every component is new, yet fail during the hours between those states. A rolling deployment may route one request to the previous service version and the next to the new one. A browser can keep an old bundle while the API has changed. A feature flag can be on, off, targeted, or returning its fallback. A worker can execute a message created before a role was revoked. A database can contain the expanded schema while old and new writers coexist.
This is the buyer question behind pre-release application security testing of transition states: does an important security invariant still hold while versions, credentials, flags, messages, and data are moving? A clean all-new staging run does not answer it.
Consider a hypothetical multi-tenant reporting product. A user in tenant A can request an export of tenant A data. During a release, authorization moves from the export service into a shared policy component. The UI is new, half the API pods are old, an export job created before a role change remains in the queue, and a cached decision survives for several minutes. Both endpoint versions pass ordinary happy-path tests. Neither path may ever cross into tenant B. After the user's tenant A export privilege is revoked, the queued job must also follow the product's agreed post-revocation contract for tenant A. This scenario is illustrative, not a customer case.
The object of the test is an invariant, not a build
Start with one sentence that must remain true throughout the transition. For example: “After role R is revoked, that subject cannot read or generate tenant A data through any synchronous or deferred path.” Another useful invariant is: “A request authenticated for tenant A can never select an object owned by tenant B, regardless of service version or flag state.”
The sentence needs operational detail. Define the subject, resource, action, boundary, revocation event, and permitted latency. “Revocation is fast” is not testable. “All online reads deny the old session within 30 seconds, and no privileged deferred job may begin after that time” is. If the product intentionally lets a job that was validly accepted finish after revocation, record that as the business rule and test the narrower promise: immutable origin, tenant, scope, and executing identity must travel with the job and cannot be replaced by caller-controlled data.
This focus complements a broader pre-launch penetration-testing guide. That guide helps define assets, roles, depth, and release criteria. Here the unit of coverage is different: the same invariant is exercised across states that can exist during deployment.
Build the transition-state matrix before the window opens
The release plan, not a generic vulnerability checklist, supplies the axes. Record which server and worker versions can coexist, which client versions remain active, which flags influence the path, how long credentials and authorization decisions can be cached, which messages can outlive the request, and which schema steps change what old code can read or write.
Do not enumerate every theoretical combination. Select current, upcoming, fallback, and high-risk states that cross a trust boundary or could preserve stale authority.
Scroll sideways to compare
| State to exercise | Controlled setup | Denial assertion | Positive control | Evidence to retain |
|---|---|---|---|---|
| Initial/current | Old client, old service, current schema | Cross-tenant subject is denied | Same-tenant authorized subject succeeds | Build marker, subject, tenant, decision, correlation ID |
| Mixed version | Old and new services or workers both eligible | Revoked or foreign-tenant request is denied by each version | Authorized request reaches each version and succeeds | Routing proof and separate result per version |
| Flag on/off | Force each security-relevant branch | Neither branch broadens authority | Eligible subject succeeds in both intended paths | Flag context and evaluated variation |
| Flag fallback | Make evaluation fail in the agreed test harness | Fallback cannot bypass the server policy | Prove the branch ran; use a permitted action or independent readiness control | Evaluation error, fallback value, server decision |
| Revoked old credential | Establish a working session, then revoke role or grant | The same credential is denied within the agreed latency | Fresh authorized credential still works | Issue, revocation, request, and decision timestamps |
| Delayed event | Hold a benign privileged job until after revocation or tenant change | Execution follows the agreed post-change rule | An equivalent currently authorized job succeeds | Message identity, immutable security context, worker version |
| Rollback or roll-forward | Exercise the approved old-code rollback or forward-fix build against current schema and policy | Neither path restores stale authority or crosses a tenant boundary | A compatible authorized operation succeeds | Schema version, code version, path taken, result |
The positive control is essential. If both the forbidden and permitted requests fail, the test has usually shown an outage or a broken harness, not enforcement. An intentional fail-closed fallback is the exception: document that the protected feature is deliberately unavailable, prove the fallback branch was selected, and use an independent readiness or allowed control that must remain permitted. Record that result as fail-closed unavailability, not evidence of full feature availability. Evidence should also prove which variant processed the action without leaking tokens or sensitive records.
Verify old and new application paths separately
Rolling deployment systems deliberately scale down old replicas while scaling up new ones. Coexistence is expected behavior, not an edge case. The test environment therefore needs a reliable way to target or identify each version: a test-only routing rule, a version header returned to an authorized harness, a worker pool label, or trace metadata. Randomly repeating a request until logs “look mixed” produces weak evidence.
Test both directions of compatibility. Send an old client to the new server and, where the real release topology permits it, a new client to the old server. Repeat the invariant through every public API, export, search, support, and background path that can perform the protected action. A UI hiding a button is not a denial; the server must reject the direct request.
Old code deserves special attention after a policy or data change. A rollback can restore availability while restoring an authorization bug. “The old binary starts” and “the old binary enforces the current tenant and revocation rules” are different acceptance criteria.
Feature flags create policy-adjacent states
A feature flag is not automatically an authorization control. A client-visible flag may hide interface behavior while the server endpoint remains callable. A server-side flag can legitimately select a policy, but then its evaluated value, fallback, targeting context, and failure mode are part of the security design and must be tested as such.
Use deterministic synthetic contexts to force on, off, targeted, and fallback states. Verify the invariant at the server. The fallback must produce an explicit, reviewed result; “whatever the SDK returns” is not an acceptance rule.
Also test disagreement between client and server. An old browser may render the former workflow while the server evaluates the new flag rules. The safe outcome may be a clear denial or a compatible response, but it must not be a request that silently enters an unreviewed legacy path.
Revocation must outrun sessions, tokens, and decision caches
The useful test starts with a credential that demonstrably works. Record a successful, same-tenant action, revoke the role or grant, and then replay the same benign action against every eligible old and new service path. An HTTP 200 from an OAuth revocation endpoint is not sufficient evidence by itself: the standard also permits that response when the submitted token is already invalid, and it recognizes that revocation knowledge may propagate between servers with delay.
Set an agreed revocation latency based on the system's risk model. Probe each path at a recorded cadence and retain both the last accepted action and the first point after which checks remain consistently denied through the agreed latency plus an observation window. Report a bound—such as “accepted at 20 seconds; every probe denied from 25 seconds through the end of the 90-second window”—rather than claiming an exact propagation instant. A later reacceptance is a failure, even if an earlier probe was denied. Include server sessions, self-contained access tokens, refresh tokens, policy-decision caches, gateway caches, long-lived connections, and impersonation or support sessions where they exist.
Session guidance recommends renewing the session identifier after privilege changes and destroying the previous identifier. That is a useful control, but the test still checks behavior. A stale session, a new session, and a fresh authorized control should all be exercised because a failure in one cache layer can otherwise look like successful revocation elsewhere.
Delayed messages need an explicit authorization contract
Queues separate acceptance time from execution time. Standard queue semantics can deliver a message more than once, which is why idempotent processing matters. Security adds another question: whose authority is evaluated when the worker finally runs?
There is no universal answer that every pre-revocation job must be cancelled. A payment already committed may need to complete; a scheduled export may need current authorization at execution. Define the rule per operation. For a privileged deferred action, useful message context often includes an immutable tenant, subject or service identity, action, resource scope, authorization version, acceptance time, and correlation ID. Treat all mutable payload fields as untrusted at execution.
A reproducible synthetic test can pause a harmless export or notification, revoke the initiating privilege, deploy the mixed worker pool, and then release the message. Run the equivalent authorized job as the positive control. Repeat delivery once if the platform supports an authorized redrive mechanism. The assertion covers the final side effect, not only a “denied” log line: no foreign-tenant file, notification, index entry, or callback should appear.
Migration safety determines whether rollback is real
Schema migration plans often use an expand, migrate, contract sequence so old and new application versions can tolerate the same data while they overlap. Vendor documentation for fleet rollouts explicitly describes mixed application versions and recommends postponing removal of the old structure until every instance uses the new path. This is deployment guidance, not proof that a particular migration is secure.
Map each schema step to the invariant. During expand, can an old reader interpret a missing tenant or policy-version field as global access? During dual write, can one representation receive a stricter label while the other remains broad? During backfill, which identity owns the update and can it cross tenant boundaries? During contract, has a column, key, or history needed by the old authorization path been destroyed?
The release gate must distinguish reversible from irreversible states. Before a destructive contract step, rollback may mean redeploying old code. After the step, that same action may be invalid because old code cannot understand the new data or enforce the current policy. The approved response may then be roll-forward, restore to an isolated environment, or a separately tested compensating release. Calling every deployment “rollbackable” conceals this decision.
Run a five-step authorized test without exploit payloads
Use synthetic tenants A and B, test roles, random object identifiers, and harmless records. Agree written authorization, traffic limits, stop conditions, test windows, retention of evidence, and whether workers or queues may be paused. No uncontrolled scan or destructive request is needed to answer the invariant question.
- BaselineProve the old credential and the current authorized control can reach the intended resource before revocation.
- TransitionPlace the system in one named mixed-version, flag, queue, or migration state and prove which variant handles the action.
- Security eventRevoke the role, tenant membership, session, or grant and record the authoritative timestamp.
- Paired checksRun the forbidden action and its authorized positive control through every eligible synchronous and deferred path.
- DecisionCompare observed latency and side effects with the invariant; retest a fixed build or stop at an irreversible gate.
The record should be reproducible but minimal: scenario ID, code and schema versions, flag state, synthetic identities, relevant timestamps, decision source, request or message correlation, observed result, and sanitized evidence. Store token hashes or opaque labels rather than live secrets. A pass says the invariant held in the named states and window; it does not prove every combination or the absence of other vulnerabilities.
If the candidate build, flag targeting rules, authorization-cache lifetime, worker behavior, or migration order changes after the test, reopen the affected matrix rows. Retest the changed path and its adjacent transition states before reusing the evidence. A previous pass belongs to the recorded configuration and cannot automatically approve a different release.
Turn the matrix into a release decision and a useful pentest brief
This work is narrower than a whole-product assessment and deeper than a happy-path regression. It fits when a release changes authorization, identity, tenant routing, long-lived sessions, feature-controlled server behavior, queues, workers, or data representation. The change-triggered pentesting guide helps decide when a delta merits manual testing; the transition matrix then defines what “tested” means during that delta.
Before an independent assessment, agree the invariant, transition graph, reachable old and new versions, flag controls, revocation latency, deferred-action semantics, schema gates, synthetic accounts, environment differences, and stop conditions. Useful deliverables are the completed state matrix, exact versions and states exercised, paired positive and negative results, observed propagation windows, side effects, findings with reproducible steps, and a bounded retest record. They should also name states that could not be created safely.
Pentest.red can scope this as part of an authorized web application pentest when independent evidence would support a release decision. The scope planner is a practical place to list tenants, roles, release stages, workers, flags, and rollback constraints before discussing timing and deliverables. The goal is a defensible answer to one precise question: when the application is between versions, does the security rule still hold?
PENTEST.RED
Discuss pentest scope and pricing
We start by agreeing on systems, access, constraints and the expected evidence.
Based on in-depth research, edited for clarity and practical use.