Federal Modernization Workbook
Chapter 02 — The ServiceNow Client and Least Privilege
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.
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.
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_headersuses 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.dowithgrant_type=client_credentials, the client ID and the secret; otherwise basic authentication, for a developer instance only.1 - Paging.
sn_table_allreads/api/now/table/{table}withsysparm_query,sysparm_fields,sysparm_limit(100 a page here; the API’s default is 10,000) andsysparm_offset, andsysparm_exclude_reference_link.2 - Retries. One wrapper retries 429 and 503, waiting the
Retry-Afterseconds 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 NAMEprints 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.
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.
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"Prove every call is declared:
python -m pytest tests/python/test_kit_calls.py -qWhat the terminal shows, run on the kit’s fixtures (the same output as text) 9 passed.List what the re-read client needs on ServiceNow:
python clients/python/kit_calls.py --task reread --system servicenowTask 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.verifierWhat the terminal shows, run on the kit’s fixtures (the same output as text) Catch a grant that is too wide. An administrator gave the verifier
itilas well:python clients/python/kit_calls.py --task reread --system servicenow --granted "x_example_idkit.verifier,itil"What the terminal shows, run on the kit’s fixtures (the same output as text) The listing, then
missing: none,extra: itilandLeast privilege does not hold: fix the grant.The command exits 1.Catch the evidence export reusing the verifier:
python clients/python/kit_calls.py --task evidence --system servicenow --granted "x_example_idkit.verifier"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.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"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).
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_TOKENCreate three custom roles in the scoped application:
x_example_idkit.finding_writer,x_example_idkit.verifierandx_example_idkit.evidence_reader. Give the verifier read and write ACLs onu_identity_findingand create onsys_attachment; the evidence reader read onu_identity_findingonly; the finding writer only the Scripted REST resource’sREST_EndpointACL.1213Create 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
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 underextra:.Exercise each user. Request a token as the verifier, then list
u_identity_findingwith 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
PATCHa 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.
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
scoperestricts for the Table and Attachment APIs. ServiceNow’s example scopes areincident_readandincident_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
Footnotes
ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026:
POST /oauth_token.do,grant_type,client_id,client_secret, optionalscope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎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).↩︎ServiceNow, “Attachment API” (Zurich:
POST /now/attachment/file, its query parameters, the mandatoryContent-Typeheader and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026:
POST /oauth_token.do,grant_type,client_id,client_secret, optionalscope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎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).↩︎
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).↩︎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).↩︎ServiceNow, “Attachment API” (Zurich:
POST /now/attachment/file, its query parameters, the mandatoryContent-Typeheader and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎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).↩︎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).↩︎ServiceNow, “Attachment API” (Zurich:
POST /now/attachment/file, its query parameters, the mandatoryContent-Typeheader and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎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).↩︎
ServiceNow, “Attachment API” (Zurich:
POST /now/attachment/file, its query parameters, the mandatoryContent-Typeheader and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026:
POST /oauth_token.do,grant_type,client_id,client_secret, optionalscope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎ServiceNow, “Client credentials grant workflow” (Zurich, last updated October 8, 2026:
POST /oauth_token.do,grant_type,client_id,client_secret, optionalscope), https://www.servicenow.com/docs/r/oJu9n0q6rU~F9UHAnNCt5w/nEdqo82hKBPEeGDqhx~hqw (read November 1, 2026).↩︎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).↩︎ServiceNow, “Attachment API” (Zurich:
POST /now/attachment/file, its query parameters, the mandatoryContent-Typeheader and the roles), https://www.servicenow.com/docs/r/MVRCUFVyKWd7vUoUukbW6g/lfpHTMPdNaq_xdajhuTwEg (read November 1, 2026).↩︎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).↩︎
FedRAMP Marketplace data, https://www.fedramp.gov/marketplace/products.json (last change November 1, 2026): ServiceNow Government Community Cloud, F1305072116 (read November 1, 2026).↩︎
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).↩︎
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).↩︎
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).↩︎
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).↩︎
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).↩︎
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).↩︎
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).↩︎
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).↩︎




