| name | assess |
| description | Run a 5-domain CAF security posture assessment for an AWS service. Produces (1) assessment-summary.md — CAF-structured findings with GAPS tables, (2) sar-facts.json — extracted SAR data, (3) checkpoint-results.json — factual checkpoint verification. These feed into the research, validate, and generate phases. |
| disable-model-invocation | false |
| argument-hint | [./input-folder] [--service=<name>] [--questions=<file>] |
Service Approval — Assessor
Perform a comprehensive security posture assessment for an AWS service across the 5 AWS Cloud
Adoption Framework (CAF) Security Perspective domains. Produces structured output that feeds
into the Researcher, Validator, and Generator agents.
This skill bridges the gap between "What does this service support?" (assessment) and
"What controls should we apply?" (controls generation). GAPS identified here become
controls requirements. SAR facts become condition key verification data. Checkpoint
results become SCP/RCP feasibility inputs.
Outputs
| File | Purpose | Consumed By |
|---|
.service-approval/<slug>/01-assess/assessment-summary.md | CAF-structured findings with GAPS tables per domain | Researcher (gap-driven controls), Summarizer |
.service-approval/<slug>/01-assess/sar-facts.json | Extracted SAR data (actions, condition keys, resource types, permission-only actions) | Validator (condition key verification), Researcher (IAM controls) |
.service-approval/<slug>/01-assess/checkpoint-results.json | Factual checkpoint verification results (RCP, declarative policies, FIPS, RAM, etc.) | Validator (SCP/RCP expressibility), Generator (skip inapplicable controls) |
.service-approval/<slug>/01-assess/assessment-questionnaire.md | SITCO Bar questionnaire answers (if --questions provided) | Summarizer (evidence) |
.service-approval/<slug>/01-assess/domains/{DOMAIN}-research.md | Intermediate domain research files | Researcher (reuse findings) |
Inputs
Parse arguments:
./input-folder (optional) — read .md, .json, .xlsx, .pdf, .docx files for context
--service=<name> — AWS service name (e.g., "Amazon Bedrock AgentCore", "AWS Lambda", "Amazon S3")
--questions=<file> — Path to SITCO Bar Questions file (.docx or .txt). If provided, also produce questionnaire answers.
If no --service and no input folder, ask the user for a service name.
Prerequisites
Per-service slug and phase directories are created after slug derivation in Step 0b.
No hardcoded prerequisites — paths are derived dynamically.
MCP call logging
Required: emit canonical mcp:<server>:call events to pipeline.log after EVERY MCP tool invocation. The Stop hook's P1 check (tools/validate/validate_pipeline_integrity.py — see _MCP_CALL_TOKEN_RE) anchors on the [mcp:<server>:call] token. The _hook_log auto-mirror copies canonical events into mcp-calls.log for free.
After each MCP call, run:
python3 -m tools.validate.log \
--slug <slug> \
--phase 01-assess \
--source mcp:<server> \
--verdict call \
--message "<tool-name>: <one-line description>"
Substitute <server> with the MCP server name (awsknowledge, aws-documentation, kb-search, etc.).
Step 0: SAR Pre-Read
The Service Authorization Reference page is the definitive source for IAM actions, resource types, and condition keys.
- Look up the SAR slug from
input/sar-slugs.json (regenerate with python3 scripts/regenerate-sar-slugs.py if the service is missing).
- Read the SAR page:
https://docs.aws.amazon.com/service-authorization/latest/reference/list_{slug}.html
- SAR pages are large — if truncated (contains
start_index=N), keep reading with increasing start_index. Safety cap: 50 reads.
- Extract these 4 categories:
-
permission_only_actions — Actions marked "[permission only]" with no API call
-
condition_keys — Service-specific keys (format: prefix:keyname), excluding aws:* globals.
Emit as an array of OBJECTS {"key": "prefix:keyname", "type": "String", "applies_to_actions": ["CreateX", "UpdateX"]},
NOT as bare strings. Populate applies_to_actions from the SAR page's "Condition keys" column
joined to the Actions table — this is what downstream phases use for semantic SAR matching
(compute_gateable_by.py) and for capabilities.iam.condition_keys[] in research-capabilities.
Without applies_to_actions, validator CHECK-S7 fails in Phase 1 and every cold-start run has
to back-fill the field.
Type field (REQUIRED). Every condition_keys entry MUST include a type field
extracted verbatim from the SAR page's Condition Keys table "Type" column. Valid values
include: String, Bool, Date, Numeric, ARN, IPAddress, ArrayOfString,
ArrayOfARN (and a few others — copy verbatim from the SAR row, don't normalize or
abbreviate). The type enables downstream generate-preventive to detect multi-value
keys (ArrayOf*) that require ForAnyValue:/ForAllValues: qualifiers in IAM
conditions — without qualifier, IAM AccessAnalyzer rejects the policy at deploy time
with "The request context key X has multiple values." The schema at
schemas/sar-facts.schema.json already permits type (optional field on conditionKey
$defs) — this extraction is the upstream half of that contract.
Placeholder form. The SAR Actions table renders object-tag placeholders as <key>
literals (e.g., s3:ExistingObjectTag/<key>, s3:RequestObjectTag/<key>). Normalize
to ${key} form during extraction because the sar-facts.schema.json condition-key
pattern rejects </> characters but accepts ${}, and downstream Phase 1 consumers
expect the ${} placeholder form. Deterministic substitution: replace /<([^>]+)>
with /${\1} in key names before emission.
Resource-tag empty-binding fallback. Some SAR pages define tag-based condition keys
(e.g., <service>:BucketTag/${TagKey}, <service>:ResourceTag/${TagKey}) in the
Condition Keys section but do NOT bind them in the Actions table's Condition-keys
column. This would leave applies_to_actions: [], which the schema rejects. When the
key name matches ^<service>:.*Tag/\$\{?TagKey\}?$ (resource-tag key) AND the Actions
table binding is absent, derive applies_to_actions by listing every action whose name
contains the resource-type fragment from the key (e.g., BucketTag → actions with
"Bucket" in the name). Record source: "sar_tag_keys_fallback" on the derived entry so
downstream validators can distinguish derived bindings from SAR-verbatim ones. Do NOT
use this fallback for non-tag keys — it only holds for resource-tag conditions where
the SAR's omission is a documentation limitation, not an actual constraint. This mirrors
the rule in research-capabilities/SKILL.md for consistency across Phase 0 / Phase 1.
-
resource_types — Resource type names and ARN patterns
-
wildcard_only_actions — Actions that can ONLY use "Resource": "*" in IAM policies.
Extraction rule: An action is wildcard-only if and only if its "Resource types" column
in the SAR Actions table is EMPTY — no resource types listed at all. Check EACH action
row individually. Common categories of wildcard-only actions:
- Create* actions for new top-level resources (resource doesn't exist yet, so no ARN)
- List* actions with an empty Resource types column (list all resources of a type)
- Permission-only actions with an empty Resource types column
CRITICAL: An action is NOT wildcard-only if it has ANY resource types listed in the SAR
table, even if those types are marked optional or not-required. Many List* actions that
list sub-resources of a parent (e.g., listing targets of a specific parent resource) DO
have resource types and support resource-level scoping — do NOT mark these as wildcard.
Similarly, ListTagsForResource typically has resource types listed and is NOT wildcard-only.
False positives here cause overly permissive
Resource: "*" in generated IAM policies.
Missing entries cause the IaC generator to attempt invalid resource ARN scoping.
- Also extract: total action count, SLR details, dependent actions.
DO NOT write sar-facts.json yet — Step 0b (below) derives the canonical slug first.
{
"service": "{service-name}",
"sar_url": "https://docs.aws.amazon.com/...",
"total_actions": 42,
"permission_only_actions": ["action1", "action2"],
"condition_keys": [
{"key": "prefix:key1", "applies_to_actions": ["CreateX", "UpdateX"]},
{"key": "prefix:key2", "applies_to_actions": ["DeleteX"]}
],
"resource_types": [
{"name": "resource", "arn_pattern": "arn:aws:service:*:*:resource/*"}
],
"wildcard_only_actions": ["action3"],
"service_linked_roles": [
{"role_name": "AWSServiceRoleFor*", "service_principal": "*.amazonaws.com", "permissions": ["action1"]}
],
"cfn_prefix": "{CfnPrefix}",
"service_prefix": "{iam-prefix}"
}
Step 0b: Derive Canonical Slug and Promote Staging
NEW: After SAR facts are extracted (Step 0), derive the canonical service slug and atomically promote the intake staging directory to the per-service tree.
-
Read intake manifest from staging:
import json
from pathlib import Path
staging_base = Path(".service-approval/_staging")
if staging_base.exists():
staging_dirs = sorted(staging_base.iterdir(), key=lambda p: p.name, reverse=True)
if staging_dirs:
latest_staging = staging_dirs[0]
manifest_path = latest_staging / "00-intake" / "intake-manifest.json"
with open(manifest_path) as f:
intake_manifest = json.load(f)
-
Derive canonical slug using tools/slug.py:
from tools.slug import derive_canonical_slug
service_name = sar_facts["service"]
service_prefix = sar_facts["service_prefix"]
user_candidate = intake_manifest["service"].get("candidate_slug", service_prefix)
slug = derive_canonical_slug(service_name, service_prefix, user_candidate)
-
Create service root and promote staging:
from tools.paths import service_root, phase_dir
service_dir = service_root(slug)
assess_dir = phase_dir(slug, "assess")
assess_dir.mkdir(parents=True, exist_ok=True)
(assess_dir / "domains").mkdir(exist_ok=True)
import shutil
staging_intake = latest_staging / "00-intake"
target_intake = service_dir / "00-intake"
if target_intake.exists():
shutil.rmtree(target_intake)
shutil.move(str(staging_intake), str(target_intake))
if not any(latest_staging.iterdir()):
latest_staging.rmdir()
-
Write sar-facts.json to the canonical path:
Write to .service-approval/<slug>/01-assess/sar-facts.json using the JSON structure from Step 0.
-
Record slug in intake manifest (update in place):
intake_manifest["service"]["canonical_slug"] = slug
intake_path = service_dir / "00-intake" / "intake-manifest.json"
with open(intake_path, "w") as f:
json.dump(intake_manifest, f, indent=2)
All downstream steps (checkpoints, domain research, assessment summary) write to .service-approval/<slug>/01-assess/ per the per-slug layout.
Step 1: Parallel Domain Research
For EACH of the 5 CAF Security Perspective domains, use subagents for parallel research.
Each domain has mandatory reads, mandatory searches, and KB queries defined in CLAUDE.md.
Substitute {service} with the actual service name, {service_slug} with the GR page slug,
and {cfn_prefix} with the CloudFormation prefix from SAR facts.
Spawn 5 agents in parallel:
Agent("IAM research for {service}")
Agent("LOGGING research for {service}")
Agent("INFRA research for {service}")
Agent("DATA research for {service}")
Agent("INCIDENT research for {service}")
Each research agent MUST:
- Execute ALL mandatory reads for its domain (from CLAUDE.md)
- Execute ALL mandatory searches (substituting
{service})
- Query relevant Knowledge Bases using
mcp__kb-search__search_* tools
- Include the SAR facts for IAM/LOGGING/DATA domains
- Write findings to
.service-approval/<slug>/01-assess/domains/{DOMAIN}-research.md
- Include source URLs for every finding
CRITICAL RESEARCH RULES:
- Do NOT search the same URL or query twice
- Limit to ~40 MCP tool calls per domain
- TOOL FAILURE GUARD: If a tool call returns an error, treat the data as ABSENT
Assessment Depth Requirements
Each domain research agent MUST produce these depth markers to achieve parity with
the hosted solution (~140K char output vs the current ~29K):
IAM domain depth:
- Enumerate ALL permission-only actions (from SAR page
[permission only] annotations)
- Build condition key → applicable actions reverse index (which actions each CK applies to)
- List ABAC support per resource type (not just "partial" — name each type + support level)
- List ALL AWS managed policies with their purpose
LOGGING domain depth:
- Enumerate ALL CloudWatch metric names per namespace (not just "30+ metrics")
- Document CloudTrail data event pagination (keep reading if truncated)
- List ALL AWS Config resource types with their relationship details
INFRA domain depth:
- Enumerate ALL CloudFormation resource types from the CFN template reference
- List ALL service quotas with numerical values + default vs adjustable
- Document Console Private Access support
DATA domain depth:
- Per-resource encryption breakdown (not "supports KMS" — specify which resources + which key types)
- FIPS endpoint URLs (actual URLs, not just "supported")
- Compliance program list with in-scope/not-in-scope distinction
Research Quality Enforcement
These rules ensure research completeness and prevent wasted tool calls:
-
Deduplication: Never read the same URL or search the same query twice within a single
domain. Before each MCP call, check if the URL or query has already been used. If a URL
was read in a prior domain and the data is still in context, reuse it instead of re-reading.
-
Tool call safety cap: Maximum 50 MCP tool calls per domain (hard cap). If a domain
approaches 50 calls, prioritize mandatory reads/searches over exploratory queries. Stop
exploratory research at 40 calls and reserve remaining budget for mandatory items.
-
Mandatory coverage enforcement: Before concluding research for ANY domain, verify
that ALL mandatory reads and ALL mandatory searches from CLAUDE.md for that domain have
been completed. If any mandatory item is missing, execute it before moving to synthesis.
A domain with incomplete mandatory coverage is a research failure — do not proceed to
synthesis until all mandatory items are attempted.
Output Quality Self-Check
Before finalizing any output (assessment-summary.md or assessment-questionnaire.md), run
these checks. Fix any issues found before writing the file.
-
Terminology leak scan: Check every line against the terminology replacement table
in CLAUDE.md. If any leaked term is found (e.g., gateway_aws-documentation-mcp___,
mcp__aws-documentation__, pre-researched findings, TOOL FAILURE GUARD), replace
it with the corresponding user-facing term. This is a hard gate — do not write output
containing any leaked term.
-
Meta-commentary removal: Scan for and remove any lines containing: "As a synthesizer",
"Direction should be", "Note to reviewer", "Editorial note:", "Internal note:", "Tool
unavailable", "MCP session error", or similar pipeline self-references.
-
SAR count verification: If sar-facts.json was produced, verify that IAM domain
findings reference at least the same number of condition keys and resource types as
extracted from the SAR page. If the assessment mentions fewer items than sar-facts.json
contains, the synthesis is incomplete — add the missing items.
-
Answer direction normalization (questionnaire mode): Every [A#] answer MUST start
with exactly one of: Yes / No / Not found / Yes, partially / This is. Apply
the direction normalization table from CLAUDE.md. Common fixes:
- "No, partially" or "No with a nuance" -> "No."
- "Partially" or "Partial." -> "Yes, partially."
- Stuttered directions ("partially, partially" or "partially. partially") -> single "Yes, partially."
Verification Quality
When running Step 6 (Cross-Domain Consistency Check) or any verification pass:
-
Correction specificity: Every correction MUST include: (a) the specific answer number
or section being corrected, (b) what is currently wrong, and (c) what the correct value
should be with the authoritative source.
-
No contradictory corrections: Never propose two corrections for the same answer that
contradict each other. If two sources disagree, use the more authoritative source
(checkpoint pages > SAR page > documentation pages > KB results) and discard the other.
-
Source evidence requirement: If corrections conflict, keep only the correction backed
by the most authoritative source. Checkpoint results are the highest authority, followed
by SAR page data, then primary documentation, then KB results.
Step 2: Factual Checkpoint Verification
Read the 9 factual checkpoint pages and record results. These are authoritative — they
override any findings from Step 1 if they contradict.
Definitive checkpoints (marked Yes): These are exhaustive lists. If the service
is NOT listed, the answer is definitively "No" / "Not supported."
Write to .service-approval/<slug>/01-assess/checkpoint-results.json:
{
"service": "{service-name}",
"checkpoints": {
"vpce_network_events": {"supported": true, "detail": "listed as eventSource", "url": "..."},
"cloudtrail_data_events": {"supported": true, "resource_types": ["AWS::S3::Object"], "url": "..."},
"config_resource_types": {"supported": true, "resource_types": ["AWS::S3::Bucket"], "url": "..."},
"rcp_support": {"supported": false, "definitive": true, "detail": "not in RCP list", "url": "..."},
"declarative_policies": {"supported": false, "definitive": true, "detail": "not listed", "url": "..."},
"ram_shareable": {"supported": false, "definitive": true, "detail": "no shareable types", "url": "..."},
"fips_endpoints": {"supported": true, "definitive": true, "detail": "listed with FIPS URLs", "url": "..."},
"compliance_in_scope": {"supported": true, "programs": ["SOC 1", "SOC 2", "HIPAA", "PCI DSS", "ISO 27001"], "not_in_scope": ["FedRAMP High"], "detail": "service-specific notes", "url": "..."},
"scp_support": {"supported": true, "detail": "not excluded from SCP enforcement", "url": "..."}
}
}
Step 2b: IaC Provider Discovery
After checkpoint verification, discover which IaC providers support this service. This
determines what output formats Phase 4 (Generate) will produce. The user does NOT choose
formats — the pipeline discovers what's available and informs them.
Check 1: CloudFormation Support
Use aws-documentation MCP to read the CloudFormation Template Reference page:
URL: https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/AWS_{CfnPrefix}.html
Record:
- Number of
AWS::{CfnPrefix}::* resource types
- List of resource type names
- If the page doesn't exist or returns 404 → CloudFormation NOT supported
Check 2: Terraform Provider Support
Search for Terraform resources:
Search: "aws_{service_prefix}" OR "awscc_{cfn_prefix}" terraform provider
Determine:
- Does
hashicorp/aws have native resources (aws_{prefix}_*)?
- Does
hashicorp/awscc (Cloud Control) have resources (awscc_{prefix}_*)?
- If CloudFormation is supported, awscc is always available as a fallback
Check 3: CDK Construct Support
Search for CDK module:
Search: aws-cdk-lib aws_{service_prefix} OR aws-cdk-lib/aws-{service}
Determine:
- L2 constructs available? (high-level, opinionated)
- L1 only? (CfnResource — direct CloudFormation mapping)
- Alpha module? (
@aws-cdk/aws-{service}-alpha)
- If CloudFormation is supported, L1 constructs always exist
Check 4: CDKTF Support
CDKTF derives from Terraform providers:
- If
hashicorp/aws has resources → CDKTF supported via aws provider
- If only
hashicorp/awscc → CDKTF supported via awscc provider
- If neither → CDKTF NOT supported
Present Findings to User
After discovery, present the results clearly:
┌─────────────────────────────────────────────────────────────────┐
│ IaC Provider Support: {service_friendly_name} │
├─────────────────────────────────────────────────────────────────┤
│ CloudFormation ✓ {N} resource types (AWS::{Prefix}::*) │
│ Terraform ✓ awscc provider / ✓ native aws / ✗ None │
│ CDK TypeScript ✓ L2 constructs / △ L1 only / ✗ None │
│ CDKTF ✓ via {provider} / ✗ Not available │
├─────────────────────────────────────────────────────────────────┤
│ Formats that will be generated: │
│ {list of supported formats} │
│ │
│ Formats NOT available: │
│ {list with reason} │
├─────────────────────────────────────────────────────────────────┤
│ Override: enter formats to skip, or press Enter to accept all │
└─────────────────────────────────────────────────────────────────┘
If the user wants to skip a supported format (e.g., "skip CDKTF"), record that override.
If a format is NOT supported, it cannot be forced — the generator has no resources to work with.
Write Results
Write to .service-approval/<slug>/01-assess/iac-support.json:
{
"schema_version": "1.0.0",
"service": "{service-name}",
"discovered_at": "{ISO-8601 timestamp}",
"cloudformation": {
"supported": true,
"resource_type_count": 15,
"resource_types": ["AWS::Service::Resource1", "..."],
"template_reference_url": "https://docs.aws.amazon.com/...",
"notes": ""
},
"terraform": {
"supported": true,
"provider": "awscc|aws|both",
"native_resource_count": 0,
"cloud_control_available": true,
"notes": ""
},
"cdk": {
"supported": true,
"construct_level": "L1|L2|L2-alpha",
"module_name": "aws-cdk-lib/aws-{service}",
"notes": ""
},
"cdktf": {
"supported": true,
"derives_from": "aws|awscc",
"notes": ""
},
"generate_formats": {
"cloudformation": true,
"terraform": true,
"cdk_typescript": true,
"cdktf": true
},
"user_overrides": [],
"warnings": []
}
Phase 4 (Generate) reads state/iac-support.json to determine which formats to produce.
If the file is missing (e.g., assess was skipped), Generate defaults to CloudFormation only.
Step 3: Synthesize CAF Assessment
After all research completes, read the domain research files from .service-approval/<slug>/01-assess/domains/.
For EACH domain, produce a CAF section using this format:
## [DOMAIN] {Domain Label}
### {N}. {Topic}
{Detailed findings with inline source citations}
**Sources**: [source URLs]
### [DOMAIN_GAPS] Compliance Matrix
| # | Requirement | Status | Details |
|---|-------------|--------|---------|
| 1 | {requirement} | FULLY SUPPORTED | {details} |
| 2 | {requirement} | REQUIREMENT GAP | {details} |
| 3 | {requirement} | CUSTOMER ACTION REQUIRED | {details} |
Status values (EXACTLY these strings):
FULLY SUPPORTED — AWS provides this natively
PARTIALLY SUPPORTED — Available with configuration or limitations
REQUIREMENT GAP — Not available in the service
CUSTOMER ACTION REQUIRED — Available but requires customer setup
Apply SAR facts as a minimum floor for IAM/LOGGING/DATA domains.
Apply checkpoint results — override any synthesis that contradicts a checkpoint.
Follow ALL precision rules and synthesis rules from CLAUDE.md for each domain.
Follow ALL terminology rules — never leak internal tool names.
Domain-Specific Synthesis Addendums
These rules supplement the CLAUDE.md domain synthesis rules with additional precision
requirements derived from common accuracy failures:
IAM domain synthesis:
- ABAC support level: Explicitly state "full", "partial", or "none". Full = service supports
all 4 tag condition keys (RequestTag, ResourceTag, TagKeys, aws:TagKeys). Partial = some
but not all. None = no tag-based condition keys.
- SLR enumeration: Every service-linked role MUST include the full permission list (all IAM
actions the SLR is allowed to perform). A role name without permissions is incomplete.
- Permission-only actions: List ALL from the SAR page. These are actions with "[permission only]"
annotation — they have no API call and no CloudTrail event. Do not confuse with "actions not
evaluated by IAM" (which is the opposite).
- Declarative policies distinction: "Declarative policies" refers ONLY to the AWS Organizations
policy type (ec2/vpc-related controls). Do not conflate with Cedar policies, resource-based
policies, or any other policy mechanism.
LOGGING domain synthesis:
- VPCE network events: These are CloudTrail NetworkActivity events (eventCategory=NetworkActivity),
a separate category from management/data events. Do not merge with general CloudTrail support.
- Data events: ONLY use the CloudTrail data events authoritative page as the source. Do not
rely on service-specific documentation pages for data event resource types.
- CloudWatch namespace consistency: AWS vended metrics use
AWS/{ServiceName}. If the service
uses a different prefix or has subsystem-specific namespaces, list each one explicitly.
AWS/Usage is a legitimate cross-service namespace, not an error.
INFRA domain synthesis:
- RCPs != resource-based policies. RCPs are an AWS Organizations policy type. Resource-based
policies are JSON policies attached to individual resources. Never conflate the two.
- Declarative policies for INFRA = VPC block public access, EBS encryption defaults, and similar
account-level declarative settings. Not SCPs, not IAM policies.
- Quota verification: Every numerical quota claimed MUST have a documentation URL. Do not
estimate or infer quotas.
DATA domain synthesis:
- FIPS endpoints: These are specific service URLs (e.g.,
fips.us-east-1.service.amazonaws.com),
NOT references to FIPS 140-3 validated crypto modules. The FIPS endpoints page is the
authoritative source.
- Cross-region replication: Means data is physically copied to another region. Inference routing
(sending requests to models in other regions) is NOT replication.
- Per-resource encryption tiers: Each resource type may support different encryption options
(AWS-owned, AWS-managed, CMK). Do NOT merge them into a single statement like "supports KMS".
Break down by resource type.
INCIDENT domain synthesis:
- Only cite capabilities specific to this service. GuardDuty, Detective, and Security Hub are
generic AWS capabilities — do not list them unless the service has a specific integration.
- "Supports tagging" (has TagResource API) is NOT the same as TBAC (tag-based access control)
or ABAC. Tagging for incident tracking is about operational tagging, not IAM authorization.
Domain-Specific Validation Addendums
These checks run during Step 6 (Cross-Domain Consistency Check) and supplement the general
consistency check with domain-specific accuracy verification:
IAM validation:
- Verify condition key count: The number of condition keys mentioned in the IAM section must
be >= the count in sar-facts.json. If fewer, the synthesis missed keys.
- Verify SLR completeness: Every SLR in sar-facts.json must appear in the IAM section with
its full permission list. Missing SLRs or partial permission lists are validation failures.
- Verify ABAC level: If the service has all 4 tag condition keys in SAR, ABAC must be "full".
If fewer, it must be "partial". If none, "none".
LOGGING validation:
- Verify VPCE network events: Cross-check the LOGGING section's claim against
checkpoint-results.json
vpce_network_events. If the checkpoint says unsupported but the
synthesis claims support, fix the synthesis.
- Verify data event resource types: Cross-check against the checkpoint. The synthesis must not
claim data event types that are absent from the authoritative page.
- Verify namespace consistency: If the synthesis mentions
AWS/{X} and AWS/{Y} for the same
service, confirm both are real namespaces from documentation.
INFRA validation:
- Verify VPC endpoint service names: Every VPC endpoint service name mentioned must follow the
format
com.amazonaws.{region}.{service} (or variant). Fabricated service names are a
validation failure.
- Verify CloudFormation resource type count: The number of CFN resource types listed should
match the CFN template reference page for the service's prefix.
DATA validation:
- Verify FIPS endpoint: Cross-check the DATA section's FIPS claim against
checkpoint-results.json
fips_endpoints. If the definitive checkpoint says unsupported but
the synthesis claims FIPS support, fix the synthesis.
- Verify per-resource encryption tiers: Each resource type should have its own encryption
statement. A single "supports KMS encryption" for a multi-resource service is insufficient.
Step 4: Questionnaire (if --questions provided)
If --questions was provided:
- Load the questions file (use
python3 -c "from docx import Document; ..." for .docx)
- Number each as
[Q1], [Q2], etc.
- Detect section headers (short, no question mark, no question verbs) — give canned answers
- Classify each question into the 5 domains
- Synthesize answers from domain research:
[A{number}] {Direction}. {Detailed answer with citations}
Directions: Yes / No / Yes, partially / Not found in available AWS documentation
- Verify against checkpoints — override contradicted answers
- Write to
.service-approval/<slug>/01-assess/assessment-questionnaire.md
- Run post-processing:
python3 tools/post-process/fix_output.py .service-approval/<slug>/01-assess/assessment-questionnaire.md
Step 5: Assemble Summary and GAPS
Combine all domain sections in order: IAM, LOGGING, INFRA, DATA, INCIDENT.
Add a GAPS SUMMARY at the end. This table consolidates ALL non-FULLY SUPPORTED items
from ALL domain compliance matrices into a single table.
Inclusion rules:
- Include every row from every domain matrix that is NOT
FULLY SUPPORTED
- Include: REQUIREMENT GAP, CUSTOMER ACTION REQUIRED, and PARTIALLY SUPPORTED rows
- Each item appears ONCE. If the same requirement appears in multiple domain matrices
(e.g., RAM sharing in both IAM and INFRA), include it only in the most relevant domain
and remove the duplicate from the other domain's matrix before assembling the summary.
Counting rules:
{N} = count of rows with status REQUIREMENT GAP in the GAPS Summary table
{M} = count of rows with status CUSTOMER ACTION REQUIRED in the GAPS Summary table
{P} = count of rows with status PARTIALLY SUPPORTED in the GAPS Summary table
- PARTIALLY SUPPORTED rows are included in the table but NOT counted in either N or M
- COUNT THE ACTUAL TABLE ROWS AFTER WRITING. Do not estimate or pre-compute.
- Arithmetic verification: After writing the GAPS Summary, re-count the table rows by
status and verify: N + M + P = total rows in the table. Also verify that the banner line
"Total gaps: {N} | Total customer actions: {M}" matches the actual row counts. If they
don't match, recount and fix before finalizing. This is a common error source.
Consistency rule:
- Each item's status in the GAPS Summary table MUST exactly match its status in the
originating domain compliance matrix. If they differ, the domain matrix is authoritative.
## GAPS Summary
**Total gaps: {N} | Total customer actions: {M}**
| # | Domain | Requirement | Status | Details |
|---|--------|-------------|--------|---------|
Write to .service-approval/<slug>/01-assess/assessment-summary.md:
# Security Posture Assessment: {Service Name}
**Generated**: {YYYY-MM-DD}
**SAR Page**: {URL}
---
## [IAM] Identity and Access Management
{sections + compliance matrix}
## [LOGGING] Logging & Monitoring
{sections + compliance matrix}
## [INFRA] Infrastructure Protection
{sections + compliance matrix}
## [DATA] Data Protection
{sections + compliance matrix}
## [INCIDENT] Incident Response
{sections + compliance matrix}
## GAPS Summary
**Total gaps: {N} | Total customer actions: {M}**
{consolidated gaps table}
Run post-processing: python3 tools/post-process/fix_output.py .service-approval/<slug>/01-assess/assessment-summary.md
Step 6: Cross-Domain Consistency Check
Review the full output for contradictions:
- Does IAM say "supports CMK" while DATA says "only AWS-managed keys"?
- Are encryption tiers consistent between DATA and other domains?
- Are VPC/network claims consistent between INFRA and LOGGING?
- Do INFRA and DATA agree on regional availability? The service-specific regions page
(e.g.,
docs.aws.amazon.com/{service}/latest/devguide/*-regions.html) is authoritative.
FAQ pages may list a subset. Use the higher count with the specific regions page as source.
- Are compliance program claims in DATA consistent with checkpoint-results.json? The
checkpoint is authoritative.
- Does the same requirement appear in multiple domain matrices? Deduplicate — keep it in
the most relevant domain only.
Flag contradictions: > **CROSS-CHECK**: {description}
Output Verification
After all steps complete, verify:
Print Summary
Assessment complete:
Service: {service-name}
SAR: {total_actions} actions, {condition_keys} keys, {resource_types} resource types
Checkpoints: {passed}/{total} verified
IaC support: CFN {✓/✗} | TF {✓/✗} | CDK {L1/L2/✗} | CDKTF {✓/✗}
Domains: IAM, LOGGING, INFRA, DATA, INCIDENT
GAPS: {N} requirement gaps, {M} customer actions required
Artifacts:
- sar-facts.json
- checkpoint-results.json
- iac-support.json
- assessment-summary.md
[- assessment-questionnaire.md (if --questions)]
Output: .service-approval/<slug>/01-assess/