Skip to main content
This is not an official U.S. federal government website. The Federal Modernization Workbook is independent technical analysis. It does not represent the position of, and has not been reviewed or endorsed by, any federal agency.

Federal Modernization Workbook

Chapter 02 — The ServiceNow Client and Least Privilege

The ServiceNow half of the kit’s shared client: OAuth client-credentials tokens at oauth_token.do, Table API paging, the Attachment API and retries on 429 and 503. Declare every HTTP call the clients make, with the task that makes it and its least privilege, in one reviewed file; fail a test when a client makes a call that isn’t declared; and split the ServiceNow roles by task so the evidence reader can’t close a finding; with labs against fixtures and a ServiceNow developer instance.

An integration user with more roles than its calls need is a standing privilege nobody requested. Write the calls down, and make the tests hold the code to the list.

NoteIn this chapter

How the kit’s clients authenticate to ServiceNow and page through its tables, and the file that makes their privilege reviewable: config/calls.json, every HTTP call a Python client makes, the task that makes it, the least privilege it needs and the vendor page that says so. A test runs the clients against a fake HTTP session and fails on any call the file doesn’t declare for that task, and kit_calls.py prints what each task needs and compares it with a grant. This chapter covers the ServiceNow rows; Chapter 03 covers SailPoint’s and Microsoft Graph’s. Lab 1 runs on a laptop; Lab 2 creates the integration users and roles on a developer instance.

TipNarrated tour
Building Ticket-to-Machine, Chapter 02 — The ServiceNow Client and Least Privilege: narrated tour · 9 minutes · captions from the player's CC button · Download MP4 · All videos
Jump to a slide (16)

Deck · Narrated deck (about 9 minutes, captions) · Script · Captions. Recorded on November 1, 2026 from the approved chapter.

Goal and audience

Goal. By the end of this chapter you can say which ServiceNow role each kit component needs and why, show from a test that the code calls nothing else, and compare an integration user’s roles with that list before it goes live.

Audience. Developers who build or change the ServiceNow side, the ServiceNow administrators who create the integration users, and the ISSO who approves the access. You should read Python. You don’t need an instance until Lab 2.

The code: examples/servicenow-sailpoint

What exists

clients/python/kit_common.py holds what every client shares, as the implementation example’s shared client pieces table summarizes:

  • Configuration. Environment variables, or a JSON file for non-secret settings; a file that carries a secret is refused.
  • The token. sn_headers uses a token from a vault (SN_ACCESS_TOKEN) if one is set; otherwise it requests one with the OAuth client-credentials grant, a form post to /oauth_token.do with grant_type=client_credentials, the client ID and the secret; otherwise basic authentication, for a developer instance only.1
  • Paging. sn_table_all reads /api/now/table/{table} with sysparm_query, sysparm_fields, sysparm_limit (100 a page here; the API’s default is 10,000) and sysparm_offset, and sysparm_exclude_reference_link.2
  • Retries. One wrapper retries 429 and 503, waiting the Retry-After seconds or date, up to five times.

The clients make three kinds of ServiceNow call: the drift client posts findings to the Scripted REST resource; the re-read client lists waiting findings, patches each one and attaches the evidence through the Attachment API;3 and Chapter 07’s export reads closed findings. The kit’s README listed the roles in prose. Nothing tied that list to the code.

What this chapter adds

config/calls.json, one row per call: an ID, the system, the method, the path (with {placeholders}), the tasks that make it, the least privilege, and the vendor page. The ServiceNow rows:

Call Tasks Least privilege Source
POST /oauth_token.do drift-push, reread, evidence An inbound OAuth client (client credentials) with its own integration user 4
POST /api/x_example_idkit/identity_findings/finding drift-push x_example_idkit.finding_writer, through the REST_Endpoint ACL 5
GET /api/now/table/u_identity_finding reread x_example_idkit.verifier (read) 6
PATCH /api/now/table/u_identity_finding/{sys_id} reread x_example_idkit.verifier (write) 7
POST /api/now/attachment/file reread x_example_idkit.verifier (create on sys_attachment) 8
GET /api/now/table/u_identity_finding evidence x_example_idkit.evidence_reader (read only) 9

The roles are custom roles in the scoped application, design guidance: ServiceNow’s pages say the calling user needs roles that allow access to the table, and that no single role allows adding attachments by default, so a custom role has to grant it.1011 Splitting them by task is the point. The finding writer can create findings and nothing else; the verifier can close them, which is why the business rule (Chapter 05) accepts a close only from that account; and the evidence reader, new in this workbook, reads but can’t change a record it is about to hash.

clients/python/kit_calls.py reads the file:

  • --task NAME prints the task’s calls and the permissions it needs on each system;
  • --system S --granted "a,b" compares what an integration user or API client actually holds with what the task needs and exits 1 on anything missing or extra. Grants are typed or exported by the operator; the tool calls no network.

tests/python/test_kit_calls.py runs the drift client, the push, the re-read client with --apply and the evidence export’s live read against a fake HTTP session, then checks every recorded request against the file, by method and whole path, for that task. A new call in a client fails the test until someone adds its row, its privilege and its source, and a reviewer sees that row in the pull request. It also asserts that no task asks ISC or Graph for a write scope.

The tests

test_kit_calls.py, 9 tests. The ServiceNow token, paging and retry tests from the kit’s original suite, test_kit_common.py, still pass unchanged.

ImportantIllustrative code

The commands below run against a fake session and fictional fixtures. They show that the code calls only what the file declares, not that a ServiceNow instance grants exactly those roles. Run Lab 2 before trusting the role list on your release.

Lab 1: fixtures, no tenant

Work in examples/servicenow-sailpoint, with Python 3.10 or later, pytest and requests.

NoteEvery command in Lab 1

Every command in this lab, in order, from examples/servicenow-sailpoint. Each step below says what it does and what to look for.

# Step 1: Prove every call is declared
python -m pytest tests/python/test_kit_calls.py -q

# Step 2: List what the re-read client needs on ServiceNow
python clients/python/kit_calls.py --task reread --system servicenow

# Step 3: Catch a grant that is too wide
python clients/python/kit_calls.py --task reread --system servicenow --granted "x_example_idkit.verifier,itil"

# Step 4: Catch the evidence export reusing the verifier
python clients/python/kit_calls.py --task evidence --system servicenow --granted "x_example_idkit.verifier"

# Step 5: Run the original ServiceNow client tests
python -m pytest tests/python/test_kit_common.py -q -k "sn_ or retry or paging or base_urls"
  1. Prove every call is declared:

    python -m pytest tests/python/test_kit_calls.py -q

    Terminal window showing python -m pytest tests/python/test_kit_calls.py -q and what it printed

    What the terminal shows, run on the kit’s fixtures (the same output as text)

    9 passed.

  2. List what the re-read client needs on ServiceNow:

    python clients/python/kit_calls.py --task reread --system servicenow
    Task reread: reread_verification.py: list waiting findings, re-read each subject, close or reopen with evidence
    
    servicenow
      POST   /oauth_token.do                                            an inbound OAuth client (client credentials) with its own integration user
      GET    /api/now/table/u_identity_finding                          x_example_idkit.verifier (read on u_identity_finding)
      PATCH  /api/now/table/u_identity_finding/{sys_id}                 x_example_idkit.verifier (write on u_identity_finding)
      POST   /api/now/attachment/file                                   x_example_idkit.verifier (create on sys_attachment)
      needs: x_example_idkit.verifier

    Terminal window showing python clients/python/kit_calls.py --task reread --system servicenow and what it printed

    What the terminal shows, run on the kit’s fixtures (the same output as text)
  3. Catch a grant that is too wide. An administrator gave the verifier itil as well:

    python clients/python/kit_calls.py --task reread --system servicenow --granted "x_example_idkit.verifier,itil"

    Terminal window showing python clients/python/kit_calls.py --task reread --system servicenow --grante… and what it printed

    What the terminal shows, run on the kit’s fixtures (the same output as text)

    The listing, then missing: none, extra: itil and Least privilege does not hold: fix the grant. The command exits 1.

  4. Catch the evidence export reusing the verifier:

    python clients/python/kit_calls.py --task evidence --system servicenow --granted "x_example_idkit.verifier"

    Terminal window showing python clients/python/kit_calls.py --task evidence --system servicenow --gran… and what it printed

    What the terminal shows, run on the kit’s fixtures (the same output as text)

    missing: x_example_idkit.evidence_reader, extra: x_example_idkit.verifier, exit 1. The export would work with the verifier’s role; it would also be able to close findings.

  5. Run the original ServiceNow client tests:

    python -m pytest tests/python/test_kit_common.py -q -k "sn_ or retry or paging or base_urls"

    Terminal window showing python -m pytest tests/python/test_kit_common.py -q -k 'sn_ or retry or pagin… and what it printed

    What the terminal shows, run on the kit’s fixtures (the same output as text)

    7 passed, 12 deselected: the base URLs, the OAuth-then-basic order, Retry-After as seconds and as a date, a 429 retried after its Retry-After, and every paging style.

Lab 2: a ServiceNow developer instance

Not run by the authors. Use a Personal Developer Instance, never production. You need the instance from Chapter 01’s Lab 2. Work in examples/servicenow-sailpoint. You’ll replace: <instance> (your developer instance’s name), <writer-roles>, <verifier-roles> and <reader-roles> (the roles each integration user holds, comma-separated, as its user record lists them), <verifier-client-id> and <verifier-client-secret>, <reader-client-id> and <reader-client-secret> (from the verifier’s and the evidence reader’s OAuth integrations), and <sys_id> (a test record’s sys_id from step 4’s listing).

NoteEvery command in Lab 2

Every command in this lab, in order, from examples/servicenow-sailpoint. Each step below says what it does and what to look for.

# Step 3: Compare each user's roles
python clients/python/kit_calls.py --task drift-push --system servicenow --granted "<writer-roles>"
python clients/python/kit_calls.py --task reread --system servicenow --granted "<verifier-roles>"
python clients/python/kit_calls.py --task evidence --system servicenow --granted "<reader-roles>"

# Step 4: Exercise each user
export SN_ACCESS_TOKEN=$(curl -s -X POST "https://<instance>.service-now.com/oauth_token.do" -d grant_type=client_credentials -d client_id="<verifier-client-id>" -d client_secret="<verifier-client-secret>" | python -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
curl -s -H "Authorization: Bearer $SN_ACCESS_TOKEN" -H "Accept: application/json" "https://<instance>.service-now.com/api/now/table/u_identity_finding?sysparm_limit=5"

# Step 4: Exercise each user
export SN_ACCESS_TOKEN=$(curl -s -X POST "https://<instance>.service-now.com/oauth_token.do" -d grant_type=client_credentials -d client_id="<reader-client-id>" -d client_secret="<reader-client-secret>" | python -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
curl -s -w "\nHTTP %{http_code}\n" -X PATCH -H "Authorization: Bearer $SN_ACCESS_TOKEN" -H "Content-Type: application/json" -H "Accept: application/json" --data '{"work_notes": "lab: evidence reader write test"}' "https://<instance>.service-now.com/api/now/table/u_identity_finding/<sys_id>"

# Step 5: Back it out
unset SN_ACCESS_TOKEN
  1. Create three custom roles in the scoped application: x_example_idkit.finding_writer, x_example_idkit.verifier and x_example_idkit.evidence_reader. Give the verifier read and write ACLs on u_identity_finding and create on sys_attachment; the evidence reader read on u_identity_finding only; the finding writer only the Scripted REST resource’s REST_Endpoint ACL.1213

  2. Create one integration user per role, and one inbound OAuth client-credentials integration per user. On Zurich, ServiceNow documents this in the Machine Identity Console: Machine Identity Console > Inbound integrations > New integration > OAuth - Client credential grant. Older releases enable the grant with a system property (see what couldn’t be verified).14

  3. Compare each user’s roles with the call list, one task at a time:

    python clients/python/kit_calls.py --task drift-push --system servicenow --granted "<writer-roles>"
    python clients/python/kit_calls.py --task reread --system servicenow --granted "<verifier-roles>"
    python clients/python/kit_calls.py --task evidence --system servicenow --granted "<reader-roles>"

    Each should end Least privilege holds. Fix anything listed under extra:.

  4. Exercise each user. Request a token as the verifier, then list u_identity_finding with it:

    export SN_ACCESS_TOKEN=$(curl -s -X POST "https://<instance>.service-now.com/oauth_token.do" -d grant_type=client_credentials -d client_id="<verifier-client-id>" -d client_secret="<verifier-client-secret>" | python -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
    curl -s -H "Authorization: Bearer $SN_ACCESS_TOKEN" -H "Accept: application/json" "https://<instance>.service-now.com/api/now/table/u_identity_finding?sysparm_limit=5"

    Then request a token as the evidence reader and try to PATCH a record:

    export SN_ACCESS_TOKEN=$(curl -s -X POST "https://<instance>.service-now.com/oauth_token.do" -d grant_type=client_credentials -d client_id="<reader-client-id>" -d client_secret="<reader-client-secret>" | python -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
    curl -s -w "\nHTTP %{http_code}\n" -X PATCH -H "Authorization: Bearer $SN_ACCESS_TOKEN" -H "Content-Type: application/json" -H "Accept: application/json" --data '{"work_notes": "lab: evidence reader write test"}' "https://<instance>.service-now.com/api/now/table/u_identity_finding/<sys_id>"

    Expect a refusal.

  5. Back it out. Delete the OAuth integrations, the three users and the three roles, and revoke any token you saved. Clear the token from your shell:

    unset SN_ACCESS_TOKEN

The verified facts

Item Value Source
Client-credentials token POST https://<instance>/oauth_token.do; grant_type (client_credentials), client_id and client_secret required; scope optional (Zurich, updated October 8, 2026) 15
Table API, read GET /api/now/table/{tableName}; sysparm_query (encoded query with ^, ^OR, ORDERBY), sysparm_fields, sysparm_limit (default 10,000), sysparm_offset (default 0), sysparm_exclude_reference_link (default false); the caller needs roles that allow access to the table 16
Attachment API, upload POST /api/now/attachment/file with table_name, table_sys_id and file_name required; a Content-Type header is mandatory; create on sys_attachment plus read and write on the target table, and no single role allows adding attachments by default 17
Scripted REST API REST_Endpoint ACLs; web_service_admin to create, not to call 18
ServiceNow Government Community Cloud FedRAMP Certified, High, JAB 19

What couldn’t be verified

  • Which inbound client-credentials setup applies on a given release. The Zurich pages document the token request and the Machine Identity Console; an older page documents a system property, false by default. Kit README verify list, item 10.
  • What the token request’s optional scope restricts for the Table and Attachment APIs. ServiceNow’s example scopes are incident_read and incident_write; whether a scope can narrow a custom table wasn’t found (research gaps, row 11).
  • API access policies. The Scripted REST page says authentication is set through API access policies; whether an instance’s policies add roles the kit doesn’t list is on the verify list, item 13.

Federal requirements and evidence

What the output proves Requirement it supports KSI Who keeps it
Each integration user holds exactly the roles its calls need, and the code calls nothing else NIST SP 800-53 AC-6 (least privilege) and AC-2 (account management), in the Moderate baseline20 KSI-IAM-ELP; KSI-IAM-SNU config/calls.json and the test in the repository; the kit_calls.py --granted output kept with the access approval
The account that closes findings is separate from the ones that raise and read them AC-5 (separation of duties), in the Moderate baseline;21 Book 13’s rule that the deciding and acting identities are separate KSI-IAM-JIT The role definitions on the instance; the business rule’s verifier property
A change to what the integration may call is reviewed CM-3 (configuration change control) KSI-CMT-LMC; KSI-CMT-RVP The pull request that adds a row to calls.json
Lab 1, step 1: every HTTP call a client makes is declared, with its task and least privilege AC-6; CM-3(2) (testing changes) at Moderate22 KSI-IAM-ELP; KSI-CMT-VTD The pytest tests/python/test_kit_calls.py run, in the pull request’s checks
Lab 1, step 2: the roles the re-read client needs on ServiceNow, each with the vendor page that says so AC-6; AC-2 KSI-IAM-ELP; KSI-IAM-SNU The kit_calls.py --task reread --system servicenow output, attached to the access request
Lab 1, step 3: a grant wider than the calls need is named, and the command exits 1 AC-6 KSI-IAM-ELP The kit_calls.py --granted output that lists extra:, kept until the grant is fixed
Lab 1, step 4: the evidence export can’t reuse the role that closes findings AC-5; AC-6 KSI-IAM-JIT; KSI-IAM-ELP The kit_calls.py --task evidence --granted output
Lab 1, step 5: the ServiceNow client handles base URLs, retries and paging as tested SA-11 (developer testing) at Moderate23 KSI-SCR-MIT The pytest tests/python/test_kit_common.py run, in the pull request’s checks
Lab 2, steps 1 and 2: three custom roles, one integration user and one OAuth client per role AC-2; AC-5; IA-5 (authenticator management) for each client secret, at Moderate24 KSI-IAM-SNU; KSI-IAM-JIT The roles, users and inbound integrations on the instance, in the update set
Lab 2, step 3: each user’s real roles match the call list AC-6; AC-2 KSI-IAM-ELP; KSI-IAM-SNU The three kit_calls.py --granted outputs ending Least privilege holds., kept with the access approval
Lab 2, step 4: the verifier can read findings, and the evidence reader’s PATCH is refused AC-3 (access enforcement) and AC-6 at Moderate25 KSI-IAM-ELP The curl responses, including the refused PATCH, kept with the access test
Lab 2, step 5: no test integration, user, role or saved token is left AC-2; IA-5 KSI-IAM-SNU The instance’s deletion history; unset SN_ACCESS_TOKEN clears the shell
Production runs on ServiceNow Government Community Cloud, which the verified facts list as FedRAMP Certified, not on a developer instance FedRAMP authorization of the cloud services used; SA-9 (external system services) at Moderate26 KSI-SCR-MIT; KSI-SCR-MON The ISSO, with the authorization package and the Marketplace listing with its read date

The KSI column names the FedRAMP 20x Key Security Indicators whose related SP 800-53 controls include the row’s control.27 KSIs are indicators for cloud service providers: the agency authorizes its own system and lines its own evidence up with what its providers report against them; it doesn’t report KSIs itself.

Book 13 maps the loop’s controls in Appendix A.

Dangers

Danger What happens The guard
A client gains a call nobody listed The integration needs a role nobody approved, and someone grants it in production to make the error go away test_kit_calls.py fails on any undeclared call
One integration user for everything The account that reads evidence can also close findings, or the workflow’s account can close what it raised Roles split by task; --granted flags extras
itil or admin granted “to make it work” Broad access to every ITSM table --granted lists the extra role and exits 1
Basic authentication left on outside a developer instance A password in a pipeline, no token expiry The README limits basic to developer instances; prefer a vault-fed token
A secret in the config file It reaches the repository load_config refuses secret keys in a file
Paging stops early A waiting finding is never re-read sn_table_all pages until a short page; tested

Checklist

Back to top

Footnotes

  1. ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026: POST /oauth_token.do, grant_type, client_id, client_secret, optional scope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎

  2. ServiceNow, “Table API” (Zurich API reference: GET /now/table/{tableName} and its query parameters, defaults and roles), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html (read November 1, 2026).↩︎

  3. ServiceNow, “Attachment API” (Zurich: POST /now/attachment/file, its query parameters, the mandatory Content-Type header and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎

  4. ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026: POST /oauth_token.do, grant_type, client_id, client_secret, optional scope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎

  5. ServiceNow, “Scripted REST APIs” (Zurich API reference), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/custom-web-services/concept/c_CustomWebServices.html (read November 1, 2026).↩︎

  6. ServiceNow, “Table API” (Zurich API reference: GET /now/table/{tableName} and its query parameters, defaults and roles), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html (read November 1, 2026).↩︎

  7. ServiceNow, “Table API” (Zurich API reference: GET /now/table/{tableName} and its query parameters, defaults and roles), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html (read November 1, 2026).↩︎

  8. ServiceNow, “Attachment API” (Zurich: POST /now/attachment/file, its query parameters, the mandatory Content-Type header and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎

  9. ServiceNow, “Table API” (Zurich API reference: GET /now/table/{tableName} and its query parameters, defaults and roles), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html (read November 1, 2026).↩︎

  10. ServiceNow, “Table API” (Zurich API reference: GET /now/table/{tableName} and its query parameters, defaults and roles), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html (read November 1, 2026).↩︎

  11. ServiceNow, “Attachment API” (Zurich: POST /now/attachment/file, its query parameters, the mandatory Content-Type header and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎

  12. ServiceNow, “Scripted REST APIs” (Zurich API reference), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/custom-web-services/concept/c_CustomWebServices.html (read November 1, 2026).↩︎

  13. ServiceNow, “Attachment API” (Zurich: POST /now/attachment/file, its query parameters, the mandatory Content-Type header and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎

  14. ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026: POST /oauth_token.do, grant_type, client_id, client_secret, optional scope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎

  15. ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026: POST /oauth_token.do, grant_type, client_id, client_secret, optional scope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎

  16. ServiceNow, “Table API” (Zurich API reference: GET /now/table/{tableName} and its query parameters, defaults and roles), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/inbound-rest/concept/c_TableAPI.html (read November 1, 2026).↩︎

  17. ServiceNow, “Attachment API” (Zurich: POST /now/attachment/file, its query parameters, the mandatory Content-Type header and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎

  18. ServiceNow, “Scripted REST APIs” (Zurich API reference), https://www.servicenow.com/docs/bundle/zurich-api-reference/page/integrate/custom-web-services/concept/c_CustomWebServices.html (read November 1, 2026).↩︎

  19. FedRAMP Marketplace data, https://www.fedramp.gov/marketplace/products.json (last change November 1, 2026): ServiceNow Government Community Cloud, F1305072116 (read November 1, 2026).↩︎

  20. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  21. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  22. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  23. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  24. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  25. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  26. National Institute of Standards and Technology, SP 800-53B Moderate baseline, release 5.2.0 OSCAL profile (includes AC-2, AC-3, AC-5, AC-6, CM-3, CM-3(2), IA-5, SA-9 and SA-11), https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json (read November 1, 2026).↩︎

  27. FedRAMP, “Key Security Indicators,” FedRAMP Consolidated Rules for 2026, indicator titles and “Related SP 800-53 Controls,” https://www.fedramp.gov/2026/reference/key-security-indicators/ (read November 2, 2026).↩︎