AWS reporters¶
The ash_aws_plugins package that ships with ASH has four reporters that send scan data
to AWS services. Each one is off until an operator loads the package and turns that
reporter on, and only an operator can choose where they send data and which credentials
they use.
| Reporter | Sends to | What leaves the machine |
|---|---|---|
s3 |
An S3 bucket | The scan summary and the resolved ASH configuration |
cloudwatch-logs |
A CloudWatch Logs log group | The same summary and configuration |
bedrock-summary-reporter |
Amazon Bedrock | Finding messages, file paths, and severity counts |
aws-security-hub |
AWS Security Hub | Nothing yet: the reporter checks its connection and writes a placeholder file (details) |
The S3 and CloudWatch Logs reporters do not send individual findings. To collect findings
centrally, copy a findings file such as reports/ash.ocsf.json or reports/ash.sarif to
your storage yourself (Export reports).
Load the package¶
Loading the package turns none of the reporters on: each one runs only when the operator
sets enabled: true in its section. A section that sets a reporter's options but leaves
enabled out, or sets it to anything but the boolean true, such as the string "true",
leaves the reporter off, and ASH logs a warning that says so, unless --output-formats or
output_formats leaves that reporter out anyway. Name the package in one of these places, all of which
belong to the operator:
--ash-plugin-modules automated_security_helper.plugin_modules.ash_aws_pluginsASH_PLUGIN_MODULES=automated_security_helper.plugin_modules.ash_aws_plugins--config-overrides 'ash_plugin_modules=[automated_security_helper.plugin_modules.ash_aws_plugins]'ash_plugin_modulesin a config file outside every git checkout and the scanned directory (Configuration trust model)
When a config file inside any git checkout or the scanned directory names the package, or a
module inside it, ASH drops that entry, logs a warning that says why, and runs the scan without it. Under
its default policy, the MCP server refuses a config an MCP client delivered whose
ash_plugin_modules list names it; in any other case it drops the entry the same way.
For ash scan, turn on the reporters you use in the same operator config. This one sends
to S3 and CloudWatch Logs only:
ash_plugin_modules:
- automated_security_helper.plugin_modules.ash_aws_plugins
reporters:
s3:
enabled: true
options:
aws_region: us-west-2
bucket_name: example-security-scan-results
key_prefix: ash-reports/
cloudwatch-logs:
enabled: true
options:
aws_region: us-west-2
log_group_name: /security/ash
Only the operator can turn these reporters on or off, with --config-overrides, such as
--config-overrides 'reporters.s3.enabled=true', or in a config file outside every git
checkout and the scanned directory. A config file inside any git checkout or the scanned
directory, an ops repository of your own included, turns none of them on or off: each keeps
the operator's enabled, or its own default where the operator sets none, and a different
value in the file is ignored with a warning
(Configuration trust model).
For a repository's own config that sets reporters.aws-security-hub.enabled: true, the
warning reads:
Ignoring reporters.aws-security-hub.enabled from /home/me/repo/.ash/.ash.yaml: that config is inside a git checkout or the scanned tree, or was delivered by an MCP client, and these options choose whether findings are sent, where they are sent or written, and which credentials send them. To keep a setting, move the file outside every git checkout, or pass the setting with --config-overrides. A file inside the scanned directory has to move out of it as well.
Under its default policy, the MCP server refuses a delivered config that turns one of these reporters on.
All of this is how ash scan reads the reporters' settings. ash report and ash merge
take whether these reporters run, and where they send, from --config-overrides only, and
ignore those settings in any config file (Rendering a reporter from saved
results).
A repository's own config file can still set the reporters' options that are not
operator-only (below) when a scan reads it. If you load the package with
--ash-plugin-modules and let ASH read the repository's config, the repository can change
those options of your reporters. Pass your config with --config to prevent that; in a
scan of one directory, ASH then does not read the repository's config file.
In a scan, a reporter that has no credentials, or fails its pre-flight call, is dropped
from the run without changing the exit code. An enabled reporter that is missing a setting it needs
does not run either, and ASH logs an error that names the setting and how to set it, once
per run for each reporter and setting: the bucket for S3, the log group for CloudWatch
Logs, and the region (aws_region, else AWS_REGION or AWS_DEFAULT_REGION) for S3,
CloudWatch Logs, and Security Hub. A run is one ash scan, ash report, or ash merge,
and a workspace run gives each error once for all its projects.
Bedrock's region defaults to us-east-1, so it has no region only when a config sets
aws_region to null. For S3 with no region set:
ERROR Reporter 's3' is enabled but does not run: no aws_region is set. Set it with --config-overrides reporters.s3.options.aws_region=<value>, or set AWS_REGION or AWS_DEFAULT_REGION in the environment, which is read only when no config sets aws_region.
The exit code does not change, so check after each run that reports/ash.s3.json and
reports/ash.cwlog.json exist. A scan with
--output-formats runs only the reporters it names, so name the AWS reporters in that
list too, as in --output-formats sarif,s3.
Destinations and credentials¶
These options choose where data goes, with which identity, what the Bedrock reporter
sends. ASH takes them only from operator
sources: the defaults below, an operator config file, or --config-overrides. ash report
and ash merge take them from the defaults and --config-overrides only.
The operator-only options of s3:
| Option | Default |
|---|---|
aws_region |
from AWS_REGION, else from AWS_DEFAULT_REGION, else null |
aws_profile |
from AWS_PROFILE, else null |
bucket_name |
from ASH_S3_BUCKET_NAME, else null |
key_prefix |
ash-reports/ |
Of cloudwatch-logs:
| Option | Default |
|---|---|
aws_region |
from AWS_REGION, else from AWS_DEFAULT_REGION, else null |
log_group_name |
from ASH_CLOUDWATCH_LOG_GROUP_NAME, else null |
log_stream_name |
ASHScanResults |
Of bedrock-summary-reporter:
| Option | Default |
|---|---|
aws_region |
from AWS_REGION, else from AWS_DEFAULT_REGION, else us-east-1 |
aws_profile |
from AWS_PROFILE, else null |
model_id |
from ASH_BEDROCK_MODEL_ID, else us.anthropic.claude-3-7-sonnet-20250219-v1:0 |
output_file |
ash.bedrock.summary.md |
output_executive_file |
bedrock-executive.md |
output_technical_file |
bedrock-technical.md |
include_code_snippets |
false |
custom_prompt |
null |
Of aws-security-hub:
| Option | Default |
|---|---|
aws_region |
from AWS_REGION, else from AWS_DEFAULT_REGION, else null |
aws_profile |
from AWS_PROFILE, else null |
account_id |
null |
With no account_id, the Security Hub reporter uses the account of its credentials.
Turning a reporter on, enabled, is the operator's too (above).
When a config file inside any git checkout or the scanned directory sets one of these options, under any spelling of the reporter's section, ASH ignores it, logs a warning that names the option, and uses the operator's value. Under an MCP server's default policy, a delivered config that sets any option of these four reporters to a value other than the server's is refused.
Credentials come from the AWS SDK's default chain: environment variables, the shared
config and credentials files (with aws_profile where the reporter has it), and the
container or instance role. cloudwatch-logs has no profile option. Bedrock's region
falls back to us-east-1, so with only ambient credentials and no region set, the
Bedrock reporter still sends data.
In container mode, the scan runs inside the container, and the host's credential files and
credential variables, such as AWS_ACCESS_KEY_ID, are not passed in
(Run ASH in a container). AWS_REGION,
AWS_DEFAULT_REGION, and AWS_PROFILE are passed in only when your own config file
reads them and they are set (The config file).
A role the container can reach through the instance metadata service may still be used;
use --offline, or a network that blocks the metadata service, if that matters.
What each reporter sends¶
The S3, CloudWatch Logs, and Bedrock reporters first call sts:GetCallerIdentity, which
needs no IAM permission. The Security Hub reporter calls it in its pre-flight check when
account_id is not set.
s3¶
Uploads one object per scan to <key_prefix>ash-report-<generated_at>.json (in workspace
mode <key_prefix><project>/ash-report-<generated_at>.json), or .yaml with
file_format: yaml. The object holds the scan summary: the report metadata and summary
counts, each scanner's and converter's status and finding counts, and the resolved ASH
configuration, which includes your suppressions and ignore paths. In the configuration and
the metadata, a value or key a config file read from the environment is the reference the
file wrote (Read values from the environment). It holds no
individual findings.
Before the upload it calls HeadBucket. Locally it writes the uploaded content to
reports/s3-report.json and a receipt with the object URL to reports/ash.s3.json. When
ASH cannot write the local copy, the receipt's local_copy is null, ASH logs a warning,
and the upload still counts.
IAM: s3:PutObject on arn:aws:s3:::<bucket>/<key_prefix>*, and s3:ListBucket on the
bucket, which HeadBucket requires.
cloudwatch-logs¶
Sends one log event per scan with the same summary and configuration as the S3 object,
to the stream log_stream_name in log_group_name. It creates the stream if needed;
the log group must already exist. Locally it writes the event and the API response to
reports/ash.cwlog.json.
IAM: logs:CreateLogStream and logs:PutLogEvents on the log group.
bedrock-summary-reporter¶
Asks a Bedrock model, through the Converse API, for an executive summary, a risk
assessment, recommendations, and a technical analysis, and writes them to
reports/ash.bedrock.summary.md, reports/bedrock-executive.md, and
reports/bedrock-technical.md. output_file, output_executive_file, and
output_technical_file change those names, and must be plain file names in reports/:
ASH does not write a file whose name leads anywhere else, and logs a warning. A test scan of a small project with one finding made six
Converse calls; more severity levels in the results mean more calls.
The prompts contain the names of every scanner in the results, severity counts, and, for
each finding sent, its level, its message text, and its file paths. Line numbers are sent
where a scanner's message includes them. With actionable_only (default true) the
reporter sends only actionable findings: those that are not suppressed and whose severity
is at or above the threshold of the scanner that reported them. In a scan of one directory
those are the findings the exit code counts; a workspace verdict uses one threshold per
project, so there the two can differ. With actionable_only: false it sends every
finding, suppressed ones included. It sends a sample of them: up to
max_findings_to_analyze (default 10) per request and up to max_findings_per_severity
(default 5) per severity. With batch_processing: true and group_by_severity: false it
sends every actionable finding, in batches. custom_prompt, custom_context,
industry_context, and compliance_frameworks are added to the prompts. The Finding
Details section at the end of the summary lists every finding; ASH writes it from the
results, not from the model's answer.
Findings from secret scanners are sent with the others: the type of secret, the file and,
in the message, the line. Source code is sent only with include_code_snippets: true;
leave it off when the scanned code may hold credentials.
The reporter sends every request to model_id. Set it to a model or inference profile
your role can invoke in that Region.
IAM: bedrock:ListFoundationModels and bedrock:ListInferenceProfiles, which it calls
to check the model, and bedrock:InvokeModel, which the Converse API requires. The
default model is a cross-Region inference profile, so allow bedrock:InvokeModel on the
inference profile and on the foundation model in each Region the profile routes to, as
the Bedrock inference profile prerequisites
describe.
aws-security-hub¶
The reporter is off until the operator turns it on. Security Hub must be enabled in the account and Region you name. In the operator's config file:
ash_plugin_modules:
- automated_security_helper.plugin_modules.ash_aws_plugins
reporters:
aws-security-hub:
enabled: true
options:
aws_region: us-east-1
This version of the reporter does not import findings yet. Its pre-flight check needs
the Region, calls DescribeHub, and calls sts:GetCallerIdentity when account_id is not
set; when one fails, ASH logs an error and the reporter does not run. When the checks pass,
report() logs a warning that the Security Hub import is not implemented, sends nothing to
Security Hub, and writes a placeholder file, reports/ash.aws-security-hub.asff.json,
that holds a message and the number of findings in the results. In a workspace scan the
reporter runs once per project.
IAM: securityhub:DescribeHub.
Rendering a reporter from saved results¶
ash report --format s3, or the name of any of the other three, runs that reporter
against the results of a previous scan, which sends the data again. Stored results do not
record which tree was scanned, so a config file ash report reads could be the scanned
repository's own, even one outside every git checkout. So ash report takes whether the
reporter runs, and its destination options, from --config-overrides only:
ash report --format s3 \
--config-overrides 'ash_plugin_modules+=[automated_security_helper.plugin_modules.ash_aws_plugins]' \
--config-overrides reporters.s3.enabled=true \
--config-overrides reporters.s3.options.bucket_name=my-bucket
The config file that --config or ASH_CONFIG names still applies its other settings.
Its enabled and destination options for these reporters are ignored with a warning, and
a reporter that --config-overrides does not turn on is refused with exit code 1. With the
same settings in a file, ash report --format s3 --config /etc/ash/ash.yaml sends nothing
and prints:
WARNING Ignoring reporters.s3.options.aws_region, reporters.s3.options.bucket_name, reporters.s3.enabled from /etc/ash/ash.yaml: ash report cannot tell that config from the scanned repository's own, since stored results do not record which tree was scanned, and these options choose whether findings are sent, where they are sent or written, and which credentials send them. To send findings off the host from ash report, pass each of these settings with --config-overrides instead.
Error: reporter 's3' sends findings off the host and is not enabled. ash report takes whether it runs, and where it sends, from --config-overrides: pass --config-overrides reporters.s3.enabled=true and its destination options.
ash merge holds its --config file to the same rule, since the shards do not record the
tree either: the merged findings go off the host only with the reporter's enabled and
destination options in ash merge's --config-overrides, and the file's values are
ignored with the same warning, naming ash merge. A reporter a plugin module adds is held
the same way for the options it declares as destinations. ash merge has these reporters
only when the package is named for the merge itself, in its --config file's
ash_plugin_modules or with
--config-overrides 'ash_plugin_modules+=[automated_security_helper.plugin_modules.ash_aws_plugins]':
a package that only the shards' scan loaded is not loaded. It runs them only when
--output-formats is not given.
ash report runs the reporter's pre-flight check first, as a scan does. When the check
refuses, for a missing setting or for credentials it cannot find, the reporter sends
nothing, and ash report logs the reason and exits 1. For S3 with no region set:
ERROR Reporter 's3' is enabled but does not run: no aws_region is set. Set it with --config-overrides reporters.s3.options.aws_region=<value>, or set AWS_REGION or AWS_DEFAULT_REGION in the environment, which is read only when no config sets aws_region.
Error: reporter 's3' cannot run: its dependency check refused. See the log above for the reason.
ash report has no --ash-plugin-modules option and does
not read ASH_PLUGIN_MODULES, and without --config it reads the config file of the
directory you run it from. Run it from outside the repository, and load the package with
your config file (--config) or with
--config-overrides 'ash_plugin_modules+=[automated_security_helper.plugin_modules.ash_aws_plugins]'.
Example IAM policy¶
A policy for the configuration above (S3 and CloudWatch Logs only), with your account, bucket, and log group:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::example-security-scan-results/ash-reports/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-security-scan-results"
},
{
"Effect": "Allow",
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:us-west-2:111122223333:log-group:/security/ash:*"
}
]
}
Each reporter's full option list is in the reporters reference.