---
id: ATS-PFM-SEC-011
scope: CAP-PFM-SEC-011
product_area: platform
module: security
revision: 3
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-PFM-SEC-011-01
    title: The recorded security-event fields
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-01
    preconditions: Prepare an audited administration mutation.
    steps: Perform the mutation and read its event.
    expected: >-
      The event presents actor, customer/workspace context, affected keys, before/after information
      and request identity.
    negative: false
    data_needs: Use the records described in the setup.
    bound_to:
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
        the actor in their wall, the session, the keys, before and after, and the request
      - TST-PFM-SEC-141
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#takes
        the actor, the customer and the request from the execution context when the capability names
        none
      - >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#answers the
        customer’s history, newest first, with the row the grant it made wrote
      - TST-PFM-SEC-051
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
      the actor in their wall, the session, the keys, before and after, and the request
    category: rule
    test_references:
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
        the actor in their wall, the session, the keys, before and after, and the request
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#takes
        the actor, the customer and the request from the execution context when the capability names
        none
      - >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#answers the
        customer’s history, newest first, with the row the grant it made wrote
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
        the actor in their wall, the session, the keys, before and after, and the request
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
        the actor in their wall, the session, the keys, before and after, and the request
    binding_scope: >-
      The binding applies to the cited executable observations and their dependency boundary. Extra
      setup, operations or combinations in this scenario still require evidence before the whole
      scenario is qualified.
  - id: ATS-PFM-SEC-011-02
    title: Refusal events survive rollback
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-02
      - INV-DOM-PFM-SEC-003-20
    preconditions: Prepare lockout and delegation refusals.
    steps: Attempt both mutations and read the trail afterward.
    expected: >-
      Refused events carry machine reasons while the rejected mutations leave their target records
      unchanged.
    negative: true
    data_needs: Use the records described in the setup.
    bound_to:
      - >-
        services-platform:modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js#records
        the refused attempt with the guard’s reason, outside the transaction that was rolled back
      - TST-PFM-SEC-121
      - >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#records the refused
        attempt too, so an access review sees what was tried
      - TST-PFM-SEC-051
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
        a refusal with its reason, and the operator behind an impersonated session
      - TST-PFM-SEC-141
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js#records
      the refused attempt with the guard’s reason, outside the transaction that was rolled back
    category: failure
    test_references:
      - >-
        services-platform:modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js#records
        the refused attempt with the guard’s reason, outside the transaction that was rolled back
      - >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#records the refused
        attempt too, so an access review sees what was tried
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.integration.spec.js#records
        a refusal with its reason, and the operator behind an impersonated session
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js#records
        the refused attempt with the guard’s reason, outside the transaction that was rolled back
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js#records
        the refused attempt with the guard’s reason, outside the transaction that was rolled back
    binding_scope: >-
      The binding applies to the cited executable observations and their dependency boundary. Extra
      setup, operations or combinations in this scenario still require evidence before the whole
      scenario is qualified.
  - id: ATS-PFM-SEC-011-03
    title: Invitation secrets are absent from audit
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-03
      - INV-DOM-PFM-SEC-003-20
    preconditions: Complete an invitation lifecycle using a password.
    steps: Search the captured security trail.
    expected: 'The raw token, its hash and the password do not appear.'
    negative: true
    data_needs: Use the records described in the setup.
    bound_to:
      - >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        neither the raw token nor its hash anywhere in what it recorded
      - TST-PFM-SEC-083
      - >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        no password either, having been given one
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
      neither the raw token nor its hash anywhere in what it recorded
    category: invariant
    test_references:
      - >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        neither the raw token nor its hash anywhere in what it recorded
      - >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        no password either, having been given one
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        neither the raw token nor its hash anywhere in what it recorded
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        neither the raw token nor its hash anywhere in what it recorded
    binding_scope: >-
      The binding applies to the cited executable observations and their dependency boundary. Extra
      setup, operations or combinations in this scenario still require evidence before the whole
      scenario is qualified.
  - id: ATS-PFM-SEC-011-04
    title: 'History scope, filters and cursor continuation'
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-04
    preconditions: Prepare events under two customers.
    steps: Read pages and filter by entity kind and identity.
    expected: >-
      Results are newest first, satisfy the filters, do not repeat page rows and omit the other
      customer’s history.
    negative: false
    data_needs: Use the records described in the setup.
    bound_to:
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#answers
        the customer's events newest first, the newest of them whole
      - TST-PFM-SEC-142
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#narrows
        the trail to one entity type
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#walks
        the trail with the cursor it answered, without repeating a row
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#leaves
        out another customer's events, whoever wrote them
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#answers
      the customer's events newest first, the newest of them whole
    category: boundary
    test_references:
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#answers
        the customer's events newest first, the newest of them whole
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#narrows
        the trail to one entity type
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#walks
        the trail with the cursor it answered, without repeating a row
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#leaves
        out another customer's events, whoever wrote them
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#answers
        the customer's events newest first, the newest of them whole
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#answers
        the customer's events newest first, the newest of them whole
    binding_scope: >-
      The binding applies to the cited executable observations and their dependency boundary. Extra
      setup, operations or combinations in this scenario still require evidence before the whole
      scenario is qualified.
  - id: ATS-PFM-SEC-011-05
    title: History requires explanation permission
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-05
    preconditions: Prepare a caller with read authority but no access-explanation permission.
    steps: Request customer history.
    expected: The request returns 403.
    negative: true
    data_needs: Use the records described in the setup.
    bound_to:
      - >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#refuses the reader,
        who may not read the history either
      - TST-PFM-SEC-051
    provenance: test
    source: >-
      services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#refuses the reader, who
      may not read the history either
    category: permission
    test_references:
      - >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#refuses the reader,
        who may not read the history either
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#refuses the reader,
        who may not read the history either
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/e2e/console-users.e2e.spec.js#refuses the reader,
        who may not read the history either
    binding_scope: >-
      The binding applies to the cited executable observations and their dependency boundary. Extra
      setup, operations or combinations in this scenario still require evidence before the whole
      scenario is qualified.
  - id: ATS-PFM-SEC-011-06
    title: Deletion success and refusal events
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-07
    preconditions: Prepare one deletable and one protected identity.
    steps: Attempt both deletions and inspect the trail.
    expected: 'Both tested outcomes appear, including the machine reason for refusal.'
    negative: true
    data_needs: Use the records described in the setup.
    bound_to:
      - >-
        services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
        security trail, naming who deleted whom
      - TST-PFM-SEC-069
      - >-
        services-platform:modules/security/tests/integration/services/invitations/delete-user.integration.spec.js#records
        the refused attempt under the code the database’s refusal carried
      - TST-PFM-SEC-117
    provenance: test
    source: >-
      services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
      security trail, naming who deleted whom
    category: transition
    test_references:
      - >-
        services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
        security trail, naming who deleted whom
      - >-
        services-platform:modules/security/tests/integration/services/invitations/delete-user.integration.spec.js#records
        the refused attempt under the code the database’s refusal carried
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
        security trail, naming who deleted whom
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
        security trail, naming who deleted whom
    binding_scope: >-
      The binding applies to the cited executable observations and their dependency boundary. Extra
      setup, operations or combinations in this scenario still require evidence before the whole
      scenario is qualified.
  - id: ATS-PFM-SEC-011-07
    title: Review security changes and refused attempts with offer and manage an invitation
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-SEC-008-04
      - BR-CAP-PFM-SEC-011-03
    exercises:
      - CAP-PFM-SEC-011
      - CAP-PFM-SEC-008
    preconditions: An administrator sends an invitation with a usable token.
    steps: 'Read the invitation history and security events after delivery, resend and withdrawal.'
    expected: Events identify the administrative actions without exposing invitation tokens.
    negative: false
    data_needs: >-
      An isolated customer with two workspaces, ordinary and staff identities, and records owned by
      this run.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-platform:modules/security/tests/unit/services/invitations/invite-user.spec.js#keeps
        the user and the invitation when the e-mail could not be sent, and says why so the
        administrator can resend
      - >-
        services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
        neither the raw token nor its hash anywhere in what it recorded
    action_evidence:
      provenance: proposed
      source:
        - >-
          services-platform:modules/security/tests/unit/services/invitations/invite-user.spec.js#keeps
          the user and the invitation when the e-mail could not be sent, and says why so the
          administrator can resend
        - >-
          services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
          neither the raw token nor its hash anywhere in what it recorded
    assertion_evidence:
      provenance: proposed
      source:
        - >-
          services-platform:modules/security/tests/unit/services/invitations/invite-user.spec.js#keeps
          the user and the invitation when the e-mail could not be sent, and says why so the
          administrator can resend
        - >-
          services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
          neither the raw token nor its hash anywhere in what it recorded
    binding_scope: >-
      No executable assertion is bound. The scenario specifies proposed proof and does not establish
      implemented or passing behavior.
  - id: ATS-PFM-SEC-011-08
    title: Review security changes and refused attempts with maintain console memberships
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-SEC-009-08
      - BR-CAP-PFM-SEC-011-07
    exercises:
      - CAP-PFM-SEC-011
      - CAP-PFM-SEC-009
    preconditions: A reachable member has a retained history and can be deleted.
    steps: Delete the member and read security history with explanation permission.
    expected: The successful deletion event remains attributable through the former-user identity.
    negative: false
    data_needs: >-
      An isolated customer with two workspaces, ordinary and staff identities, and records owned by
      this run.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-platform:modules/security/tests/unit/services/invitations/user-deletion.spec.js#refuses
        a delete that would leave the customer with nobody who can administer its security
      - >-
        services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
        security trail, naming who deleted whom
    action_evidence:
      provenance: proposed
      source:
        - >-
          services-platform:modules/security/tests/unit/services/invitations/user-deletion.spec.js#refuses
          a delete that would leave the customer with nobody who can administer its security
        - >-
          services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
          security trail, naming who deleted whom
    assertion_evidence:
      provenance: proposed
      source:
        - >-
          services-platform:modules/security/tests/unit/services/invitations/user-deletion.spec.js#refuses
          a delete that would leave the customer with nobody who can administer its security
        - >-
          services-platform:modules/security/tests/e2e/users.e2e.spec.js#writes the deletion to the
          security trail, naming who deleted whom
    binding_scope: >-
      No executable assertion is bound. The scenario specifies proposed proof and does not establish
      implemented or passing behavior.
  - id: ATS-PFM-SEC-011-09
    title: Review security changes and refused attempts with assign roles within a controlled scope
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-SEC-010-13
      - BR-CAP-PFM-SEC-011-04
    exercises:
      - CAP-PFM-SEC-011
      - CAP-PFM-SEC-010
    preconditions: A member has no custom role assignment and history is readable.
    steps: 'Grant, move and withdraw a role, then page through that member’s history.'
    expected: Each event names the corresponding assignment and the relevant widening classification.
    negative: false
    data_needs: >-
      An isolated customer with two workspaces, ordinary and staff identities, and records owned by
      this run.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-platform:modules/security/tests/integration/services/membership/grant-history.integration.spec.js#lists
        the grant, the move and the withdrawal, each naming the grant it was about
      - >-
        services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#leaves
        out another customer's events, whoever wrote them
    action_evidence:
      provenance: proposed
      source:
        - >-
          services-platform:modules/security/tests/integration/services/membership/grant-history.integration.spec.js#lists
          the grant, the move and the withdrawal, each naming the grant it was about
        - >-
          services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#leaves
          out another customer's events, whoever wrote them
    assertion_evidence:
      provenance: proposed
      source:
        - >-
          services-platform:modules/security/tests/integration/services/membership/grant-history.integration.spec.js#lists
          the grant, the move and the withdrawal, each naming the grant it was about
        - >-
          services-platform:modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js#leaves
          out another customer's events, whoever wrote them
    binding_scope: >-
      No executable assertion is bound. The scenario specifies proposed proof and does not establish
      implemented or passing behavior.
  - id: ATS-PFM-SEC-011-10
    title: >-
      Review security changes and refused attempts with model customer responsibilities with roles
      and policies
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-SEC-011-02
      - BR-CAP-PFM-SEC-012-10
    exercises:
      - CAP-PFM-SEC-011
      - CAP-PFM-SEC-012
    preconditions: A role editor lacks authority for a requested widening change.
    steps: Attempt the edit and inspect the definition and refusal history.
    expected: The definition is unchanged and the classified refusal survives rollback.
    negative: true
    data_needs: >-
      An isolated customer with two workspaces, ordinary and staff identities, and records owned by
      this run.
    bound_to: []
    provenance: proposed
    source:
      - 'services-platform:modules/security/services/audit/refused-attempts.ts:auditedAttempt'
      - >-
        services-platform:modules/security/tests/integration/services/definitions/manage-roles.integration.spec.js#refuses
        an edit that would widen a role the editor holds themselves, whoever else holds it
    action_evidence:
      provenance: proposed
      source:
        - 'services-platform:modules/security/services/audit/refused-attempts.ts:auditedAttempt'
        - >-
          services-platform:modules/security/tests/integration/services/definitions/manage-roles.integration.spec.js#refuses
          an edit that would widen a role the editor holds themselves, whoever else holds it
    assertion_evidence:
      provenance: proposed
      source:
        - 'services-platform:modules/security/services/audit/refused-attempts.ts:auditedAttempt'
        - >-
          services-platform:modules/security/tests/integration/services/definitions/manage-roles.integration.spec.js#refuses
          an edit that would widen a role the editor holds themselves, whoever else holds it
    binding_scope: >-
      No executable assertion is bound. The scenario specifies proposed proof and does not establish
      implemented or passing behavior.
evidence:
  - >-
    Pinned services-platform references for the structured rows:
    modules/security/tests/e2e/console-users.e2e.spec.js;
    modules/security/tests/e2e/users.e2e.spec.js;
    modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js;
    modules/security/tests/integration/services/invitations/delete-user.integration.spec.js;
    modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js;
    modules/security/tests/integration/services/persistence/audit-events.integration.spec.js;
    modules/security/tests/integration/services/persistence/audit-events.queries.integration.spec.js.
  - >-
    Testing back-fill reviews the pinned suite assertions, related capabilities, domain invariants,
    state transitions and workflow failure boundaries. New unbound rows remain proposed.
coverage:
  rule: covered
  transition: covered
  validation: 'gap: FND-PFM-SEC-068'
  permission: covered
  edge: 'gap: FND-PFM-SEC-068'
  failure: covered
  boundary: covered
  concurrency: 'gap: FND-PFM-SEC-068'
  invariant: covered
  interaction: covered
coverage_notes:
  validation: No scenario isolates this category. The referenced finding retains the missing design proof.
  edge: No scenario isolates this category. The referenced finding retains the missing design proof.
  concurrency: No scenario isolates this category. The referenced finding retains the missing design proof.
invariant_proofs:
  - scenario: ATS-PFM-SEC-011-02
    proves:
      - INV-DOM-PFM-SEC-003-20
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/services/invitations/revoke-lockout.integration.spec.js#records
      the refused attempt with the guard’s reason, outside the transaction that was rolled back
    proof_scope: >-
      The cited check exercises one part of an invariant whose full guarantee remains unknown. It
      supplies no proof of concurrent exclusion or unrestricted history protection.
  - scenario: ATS-PFM-SEC-011-03
    proves:
      - INV-DOM-PFM-SEC-003-20
    provenance: test
    source: >-
      services-platform:modules/security/tests/integration/lifecycle/invitation/invitation-arc.integration.spec.js#carries
      neither the raw token nor its hash anywhere in what it recorded
    proof_scope: >-
      The cited check exercises one part of an invariant whose full guarantee remains unknown. It
      supplies no proof of concurrent exclusion or unrestricted history protection.
permission_proof_review:
  - key: platform.security-access.explain
    scenario_candidates:
      - ATS-PFM-SEC-011-05
    qualification: >-
      These are candidate scenarios for the authority boundary, not an exhaustive allowed/refused
      matrix for this key. The finding retains combinations not established by the executable
      assertions.
    gap: FND-PFM-SEC-068
    provenance: inferred
    source: >-
      specs/platform/security/planning/capabilities/CAP-PFM-SEC-011-review-who-changed-access-and-what-was-refused.md
examiner: codex
examined_at: '2026-10-23'
final_text: OpenAI
---
# Review security changes and refused attempts: Capability Test Specification

## Coverage

| Required rule | Scenario or finding |
|---|---|
| `BR-CAP-PFM-SEC-011-01` | `ATS-PFM-SEC-011-01` |
| `BR-CAP-PFM-SEC-011-02` | `ATS-PFM-SEC-011-02`, `ATS-PFM-SEC-011-10` |
| `BR-CAP-PFM-SEC-011-03` | `ATS-PFM-SEC-011-03`, `ATS-PFM-SEC-011-07` |
| `BR-CAP-PFM-SEC-011-04` | `ATS-PFM-SEC-011-04`, `ATS-PFM-SEC-011-09` |
| `BR-CAP-PFM-SEC-011-05` | `ATS-PFM-SEC-011-05` |
| `BR-CAP-PFM-SEC-011-07` | `ATS-PFM-SEC-011-06`, `ATS-PFM-SEC-011-08` |

The category map records specified proof. A covered category can contain proposed scenarios; it does not mean the checks have run. The bindings identify individual executable contributions, while findings retain missing rules, permissions, transitions and assertions.

## Interactions With Related Capabilities

The interaction scenarios connect this capability to the related actions and workflow handoffs named in their proof references. Each case observes the outcome after the connected action changes records or available authority. A collection of separately bound checks does not establish an ordered feature journey.

Source revisions and assertion references are recorded in frontmatter. Steps and expected observations carry separate evidence annotations. Proposed checks remain distinct from executable test statements, and static review supplies no run result.
