diff --git a/.doc_gen/metadata/inspector_metadata.yaml b/.doc_gen/metadata/inspector_metadata.yaml new file mode 100644 index 00000000000..b06fd987e2f --- /dev/null +++ b/.doc_gen/metadata/inspector_metadata.yaml @@ -0,0 +1,188 @@ +# zexi 0.4.2 +inspector_Hello: + title: Hello &Inspector; + title_abbrev: Hello &Inspector; + synopsis: get started using &Inspector;. + category: Hello + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.hello.main + services: + inspector: {ListMembers} +inspector_BatchGetFindingDetails: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.finding.details.main + services: + inspector: {BatchGetFindingDetails} +inspector_DeleteFilter: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.delete.filter.main + services: + inspector: {DeleteFilter} +inspector_Disable: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.disable.main + services: + inspector: {Disable} +inspector_Enable: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.enable.main + services: + inspector: {Enable} +inspector_CreateFilter: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.create.filter.main + services: + inspector: {CreateFilter} +inspector_GetAccountStatus: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.get_account_status.main + services: + inspector: {BatchGetAccountStatus} +inspector_ListFindings: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.list_findings.main + services: + inspector: {ListFindings} +inspector_ListCoverageStatistics: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.list_coverage.stats.main + services: + inspector: {ListCoverageStatistics} +inspector_ListCoverage: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.list_coverage.main + services: + inspector: {ListCoverage} +inspector_ListUsageTotals: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.list_usage_totals.main + services: + inspector: {ListUsageTotals} +inspector_ListFilters: + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: + snippet_tags: + - inspector.java2.list_filters.main + services: + inspector: {ListFilters} +inspector_Scenario: + synopsis_list: + - Check Inspector account status. + - Ensure Inspector is enabled. + - Analyze security findings. + - Check scan coverage. + - Create a findings filter. + - List existing filters. + - Check usage and costs. + - Get coverage statistics. + - Delete a filter. + category: Basics + languages: + Java: + versions: + - sdk_version: 2 + github: javav2/example_code/inspector + sdkguide: + excerpts: + - description: Run an interactive scenario demonstrating &Inspector; features. + snippet_tags: + - inspector.java2_scenario.main + - description: A wrapper class for &Inspector; SDK methods. + snippet_tags: + - inspector.java2_actions.main + services: + inspector: {BatchGetAccountStatus, BatchGetFindingDetails, DeleteFilter, Enable, ListFindings, ListCoverage, CreateFilter, ListFilters, ListUsageTotals, ListCoverageStatistics} diff --git a/javav2/example_code/inspector/README.md b/javav2/example_code/inspector/README.md new file mode 100644 index 00000000000..39146582ba5 --- /dev/null +++ b/javav2/example_code/inspector/README.md @@ -0,0 +1,125 @@ +# Amazon Inspector code examples for the SDK for Java 2.x + +## Overview + +Shows how to use the AWS SDK for Java 2.x to work with Amazon Inspector. + + + + +_Amazon Inspector _ + +## ⚠ Important + +* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/). +* Running the tests might result in charges to your AWS account. +* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege). +* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services). + + + + +## Code examples + +### Prerequisites + +For prerequisites, see the [README](../../README.md#Prerequisites) in the `javav2` folder. + + + + + +### Get started + +- [Hello Amazon Inspector](src/main/java/com/java/inspector/HelloInspector.java#L29) (`ListMembers`) + + +### Basics + +Code examples that show you how to perform the essential operations within a service. + +- [Learn the basics](src/main/java/com/java/inspector/InspectorScenario.java) + + +### Single actions + +Code excerpts that show you how to call individual service functions. + +- [BatchGetAccountStatus](src/main/java/com/java/inspector/InspectorActions.java#L246) +- [BatchGetFindingDetails](src/main/java/com/java/inspector/InspectorActions.java#L587) +- [CreateFilter](src/main/java/com/java/inspector/InspectorActions.java#L371) +- [DeleteFilter](src/main/java/com/java/inspector/InspectorActions.java#L557) +- [Disable](src/main/java/com/java/inspector/InspectorActions.java#L670) +- [Enable](src/main/java/com/java/inspector/InspectorActions.java#L55) +- [ListCoverage](src/main/java/com/java/inspector/InspectorActions.java#L490) +- [ListCoverageStatistics](src/main/java/com/java/inspector/InspectorActions.java#L127) +- [ListFilters](src/main/java/com/java/inspector/InspectorActions.java#L325) +- [ListFindings](src/main/java/com/java/inspector/InspectorActions.java#L435) +- [ListUsageTotals](src/main/java/com/java/inspector/InspectorActions.java#L184) + + + + + +## Run the examples + +### Instructions + + + + + +#### Hello Amazon Inspector + +This example shows you how to get started using Amazon Inspector. + + +#### Learn the basics + +This example shows you how to do the following: + +- Check Inspector account status. +- Ensure Inspector is enabled. +- Analyze security findings. +- Check scan coverage. +- Create a findings filter. +- List existing filters. +- Check usage and costs. +- Get coverage statistics. +- Delete a filter. + + + + + + + + + +### Tests + +⚠ Running tests might result in charges to your AWS account. + + +To find instructions for running these tests, see the [README](../../README.md#Tests) +in the `javav2` folder. + + + + + + +## Additional resources + +- [Amazon Inspector User Guide](https://docs.aws.amazon.com/inspector/latest/user/what-is-inspector.html) +- [Amazon Inspector API Reference](https://docs.aws.amazon.com/inspector/latest/APIReference/Welcome.html) +- [SDK for Java 2.x Amazon Inspector reference](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/inspector/package-summary.html) + + + + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 diff --git a/javav2/example_code/inspector/pom.xml b/javav2/example_code/inspector/pom.xml new file mode 100644 index 00000000000..f559deabe0f --- /dev/null +++ b/javav2/example_code/inspector/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + org.example + Inspector + 1.0-SNAPSHOT + + + UTF-8 + 17 + 17 + 17 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 21 + 21 + --enable-preview + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.2 + + + + + + + software.amazon.awssdk + bom + 2.29.45 + pom + import + + + org.apache.logging.log4j + log4j-bom + 2.23.1 + pom + import + + + + + + org.junit.jupiter + junit-jupiter + 5.11.4 + test + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + secretsmanager + + + com.google.code.gson + gson + 2.10.1 + + + com.fasterxml.jackson.core + jackson-databind + 2.17.0 + + + software.amazon.awssdk + inspector2 + + + software.amazon.awssdk + sso + + + software.amazon.awssdk + ssooidc + + + org.apache.logging.log4j + log4j-core + + + org.slf4j + slf4j-api + 2.0.13 + + + org.apache.logging.log4j + log4j-slf4j2-impl + + + software.amazon.awssdk + acm + + + org.apache.logging.log4j + log4j-1.2-api + + + software.amazon.awssdk + url-connection-client + 2.18.13 + + + \ No newline at end of file diff --git a/javav2/example_code/inspector/src/main/java/com/java/inspector/HelloInspector.java b/javav2/example_code/inspector/src/main/java/com/java/inspector/HelloInspector.java new file mode 100644 index 00000000000..a457a90145f --- /dev/null +++ b/javav2/example_code/inspector/src/main/java/com/java/inspector/HelloInspector.java @@ -0,0 +1,92 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.java.inspector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.inspector2.Inspector2Client; +import software.amazon.awssdk.services.inspector2.model.BatchGetAccountStatusRequest; +import software.amazon.awssdk.services.inspector2.model.BatchGetAccountStatusResponse; +import software.amazon.awssdk.services.inspector2.model.AccountState; +import software.amazon.awssdk.services.inspector2.model.ListMembersRequest; +import software.amazon.awssdk.services.inspector2.model.ListMembersResponse; +import software.amazon.awssdk.services.inspector2.model.Member; +import software.amazon.awssdk.services.inspector2.model.ResourceState; +import software.amazon.awssdk.services.inspector2.model.State; +import software.amazon.awssdk.services.inspector2.model.ListFindingsRequest; +import software.amazon.awssdk.services.inspector2.model.ListFindingsResponse; +import software.amazon.awssdk.services.inspector2.model.Finding; +import software.amazon.awssdk.services.inspector2.model.ListUsageTotalsRequest; +import software.amazon.awssdk.services.inspector2.model.ListUsageTotalsResponse; +import software.amazon.awssdk.services.inspector2.model.UsageTotal; +import software.amazon.awssdk.services.inspector2.model.Inspector2Exception; +import software.amazon.awssdk.services.inspector2.paginators.ListFindingsIterable; +import software.amazon.awssdk.services.inspector2.paginators.ListUsageTotalsIterable; +import java.util.List; +import java.util.ArrayList; + +// snippet-start:[inspector.java2.hello.main] +/** + * Before running this Java V2 code example, set up your development + * environment, including your credentials. + * + * For more information, see the following documentation topic: + * + * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + */ +public class HelloInspector { + private static final Logger logger = LoggerFactory.getLogger(HelloInspector.class); + + public static void main(String[] args) { + logger.info("Hello Amazon Inspector!"); + + try (Inspector2Client inspectorClient = Inspector2Client.builder().build()) { + + logger.info("Listing member accounts for this Inspector administrator account..."); + listMembers(inspectorClient); + + logger.info("The Hello Inspector example completed successfully."); + + } catch (Inspector2Exception e) { + logger.error("Error: {}", e.getMessage()); + logger.info("Troubleshooting:"); + logger.info("1. Verify AWS credentials are configured"); + logger.info("2. Check IAM permissions for Inspector2"); + logger.info("3. Ensure Inspector2 is enabled in your account"); + logger.info("4. Verify you're using a supported region"); + } + } + + /** + * Lists all member accounts associated with the current Inspector administrator account. + * + * @param inspectorClient The Inspector2Client used to interact with AWS Inspector. + */ + public static void listMembers(Inspector2Client inspectorClient) { + try { + ListMembersRequest request = ListMembersRequest.builder() + .maxResults(50) // optional: limit results + .build(); + + ListMembersResponse response = inspectorClient.listMembers(request); + List members = response.members(); + + if (members == null || members.isEmpty()) { + logger.info("No member accounts found for this Inspector administrator account."); + return; + } + + logger.info("Found {} member account(s):", members.size()); + for (Member member : members) { + logger.info(" - Account ID: {}, Status: {}", + member.accountId(), + member.relationshipStatusAsString()); + } + + } catch (Inspector2Exception e) { + logger.error("Failed to list members: {}", e.awsErrorDetails().errorMessage()); + } + } +} +// snippet-end:[inspector.java2.hello.main] \ No newline at end of file diff --git a/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorActions.java b/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorActions.java new file mode 100644 index 00000000000..d8e91de990e --- /dev/null +++ b/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorActions.java @@ -0,0 +1,748 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.java.inspector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.services.inspector2.Inspector2AsyncClient; +import software.amazon.awssdk.services.inspector2.model.*; +import software.amazon.awssdk.services.inspector2.paginators.ListCoveragePublisher; +import software.amazon.awssdk.services.inspector2.paginators.ListFiltersPublisher; +import software.amazon.awssdk.services.inspector2.paginators.ListFindingsPublisher; +import software.amazon.awssdk.services.inspector2.paginators.ListUsageTotalsPublisher; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.stream.Collectors; + +// snippet-start:[inspector.java2_actions.main] +public class InspectorActions { + private static Inspector2AsyncClient inspectorAsyncClient; + private static final Logger logger = LoggerFactory.getLogger(InspectorActions.class); + + private static Inspector2AsyncClient getAsyncClient() { + if (inspectorAsyncClient == null) { + SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder() + .maxConcurrency(100) + .connectionTimeout(Duration.ofSeconds(60)) + .readTimeout(Duration.ofSeconds(60)) + .writeTimeout(Duration.ofSeconds(60)) + .build(); + + ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() + .apiCallTimeout(Duration.ofMinutes(2)) + .apiCallAttemptTimeout(Duration.ofSeconds(90)) + .retryStrategy(RetryMode.STANDARD) + .build(); + + inspectorAsyncClient = Inspector2AsyncClient.builder() + .httpClient(httpClient) + .overrideConfiguration(overrideConfig) + .build(); + } + return inspectorAsyncClient; + } + + // snippet-start:[inspector.java2.enable.main] + + /** + * Enables AWS Inspector for the provided account(s) and default resource types. + * + * @param accountIds Optional list of AWS account IDs. + */ + public CompletableFuture enableInspectorAsync(List accountIds) { + + // The resource types to enable. + List resourceTypes = List.of( + ResourceScanType.EC2, + ResourceScanType.ECR, + ResourceScanType.LAMBDA, + ResourceScanType.LAMBDA_CODE + ); + + // Build the request. + EnableRequest.Builder requestBuilder = EnableRequest.builder() + .resourceTypes(resourceTypes); + + if (accountIds != null && !accountIds.isEmpty()) { + requestBuilder.accountIds(accountIds); + } + + EnableRequest request = requestBuilder.build(); + return getAsyncClient().enable(request) + .whenComplete((response, exception) -> { + if (exception != null) { + Throwable cause = exception.getCause(); + if (cause instanceof ValidationException) { + throw new CompletionException( + "Inspector may already be enabled for this account: %s".formatted(cause.getMessage()), + cause + ); + + } + + if (cause instanceof Inspector2Exception) { + Inspector2Exception e = (Inspector2Exception) cause; + throw new CompletionException( + "AWS Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), + cause + ); + } + + throw new CompletionException( + "Failed to enable Inspector: %s".formatted(exception.getMessage()), + exception + ); + } + }) + .thenApply(response -> { + StringBuilder summary = new StringBuilder("Enable results:\n"); + + if (response.accounts() == null || response.accounts().isEmpty()) { + summary.append("Inspector may already be enabled for all target accounts."); + return summary.toString(); + } + + for (Account account : response.accounts()) { + String accountId = account.accountId() != null ? account.accountId() : "Unknown"; + String status = account.status() != null ? account.statusAsString() : "Unknown"; + summary.append(" • Account: ").append(accountId) + .append(" → Status: ").append(status).append("\n"); + } + + return summary.toString(); + }); + } + // snippet-end:[inspector.java2.enable.main] + + // snippet-start:[inspector.java2.list_coverage.stats.main] + + /** + * Retrieves and prints the coverage statistics using a paginator. + */ + public CompletableFuture listCoverageStatisticsAsync() { + ListCoverageStatisticsRequest request = ListCoverageStatisticsRequest.builder() + .build(); + + return getAsyncClient().listCoverageStatistics(request) + .whenComplete((response, exception) -> { + if (exception != null) { + Throwable cause = exception.getCause(); + + if (cause instanceof ValidationException) { + throw new CompletionException( + "Validation error listing coverage statistics: %s".formatted(cause.getMessage()), + cause + ); + } + + if (cause instanceof Inspector2Exception) { + Inspector2Exception e = (Inspector2Exception) cause; + + throw new CompletionException( + "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), + e + ); + } + + throw new CompletionException( + "Unexpected error listing coverage statistics: %s".formatted(exception.getMessage()), + exception + ); + } + }) + .thenApply(response -> { + List countsList = response.countsByGroup(); + StringBuilder sb = new StringBuilder(); + + if (countsList == null || countsList.isEmpty()) { + sb.append("No coverage statistics available.\n"); + return sb.toString(); + } + + sb.append("Coverage Statistics:\n"); + + for (Counts c : countsList) { + sb.append(" Group: ").append(c.groupKey()).append("\n") + .append(" Total Count: ").append(c.count()).append("\n\n"); + } + + return sb.toString(); + }); + } + // snippet-end:[inspector.java2.list_coverage.stats.main] + + // snippet-start:[inspector.java2.list_usage_totals.main] + + /** + * Asynchronously lists Inspector2 usage totals using a paginator. + * + * @param accountIds optional list of account IDs + * @param maxResults maximum results per page + * @return CompletableFuture completed with formatted summary text + */ + public CompletableFuture listUsageTotalsAsync( + List accountIds, + int maxResults) { + + logger.info("Starting usage totals paginator…"); + + ListUsageTotalsRequest.Builder builder = ListUsageTotalsRequest.builder() + .maxResults(maxResults); + + if (accountIds != null && !accountIds.isEmpty()) { + builder.accountIds(accountIds); + } + + ListUsageTotalsRequest request = builder.build(); + ListUsageTotalsPublisher paginator = getAsyncClient().listUsageTotalsPaginator(request); + StringBuilder summaryBuilder = new StringBuilder(); + + return paginator.subscribe(response -> { + if (response.totals() != null && !response.totals().isEmpty()) { + response.totals().forEach(total -> { + if (total.usage() != null) { + total.usage().forEach(usage -> { + logger.info("Usage: {} = {}", usage.typeAsString(), usage.total()); + summaryBuilder.append(usage.typeAsString()) + .append(": ") + .append(usage.total()) + .append("\n"); + }); + } + }); + } else { + logger.info("Page contained no usage totals."); + } + }).thenRun(() -> logger.info("Successfully listed usage totals.")) + .thenApply(v -> { + String summary = summaryBuilder.toString(); + return summary.isEmpty() ? "No usage totals found." : summary; + }).exceptionally(ex -> { + Throwable cause = ex.getCause() != null ? ex.getCause() : ex; + + if (cause instanceof ValidationException ve) { + throw new CompletionException( + "Validation error listing usage totals: %s".formatted(ve.getMessage()), + ve + ); + } + + throw new CompletionException("Failed to list usage totals", cause); + }); + } + + // snippet-end:[inspector.java2.list_usage_totals.main] + + // snippet-start:[inspector.java2.get_account_status.main] + + /** + * Retrieves the account status using the Inspector2Client. + */ + public CompletableFuture getAccountStatusAsync() { + BatchGetAccountStatusRequest request = BatchGetAccountStatusRequest.builder() + .accountIds(Collections.emptyList()) + .build(); + + return getAsyncClient().batchGetAccountStatus(request) + .whenComplete((response, exception) -> { + if (exception != null) { + Throwable cause = exception.getCause(); + if (cause instanceof AccessDeniedException) { + throw new CompletionException( + "You do not have sufficient access: %s".formatted(cause.getMessage()), + cause + ); + + } + + if (cause instanceof Inspector2Exception) { + Inspector2Exception e = (Inspector2Exception) cause; + + throw new CompletionException( + "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), + e + ); + } + + throw new CompletionException( + "Unexpected error getting account status: %s".formatted(exception.getMessage()), + exception + ); + } + }) + .thenApply(response -> { + + StringBuilder sb = new StringBuilder(); + List accounts = response.accounts(); + + if (accounts == null || accounts.isEmpty()) { + sb.append("No account status returned.\n"); + return sb.toString(); + } + + sb.append("Inspector Account Status:\n"); + for (AccountState account : accounts) { + + String accountId = account.accountId() != null + ? account.accountId() + : "Unknown"; + + sb.append(" Account ID: ").append(accountId).append("\n"); + + // Overall account state + if (account.state() != null && account.state().status() != null) { + sb.append(" Overall State: ") + .append(account.state().status()) + .append("\n"); + } else { + sb.append(" Overall State: Unknown\n"); + } + + // Resource state (only status available) + ResourceState resources = account.resourceState(); + if (resources != null) { + sb.append(" Resource Status: available\n"); + } + + sb.append("\n"); + } + + return sb.toString(); + }); + } + // snippet-end:[inspector.java2.get_account_status.main] + + // snippet-start:[inspector.java2.list_filters.main] + + /** + * Asynchronously lists Inspector2 filters using a paginator. + * + * @param maxResults maximum filters per page (nullable) + * @return CompletableFuture completed with summary text + */ + public CompletableFuture listFiltersAsync(Integer maxResults) { + logger.info("Starting async filters paginator…"); + + ListFiltersRequest.Builder builder = ListFiltersRequest.builder(); + if (maxResults != null) { + builder.maxResults(maxResults); + } + + ListFiltersRequest request = builder.build(); + + // Paginator from SDK + ListFiltersPublisher paginator = getAsyncClient().listFiltersPaginator(request); + StringBuilder collectedFilterIds = new StringBuilder(); + + return paginator.subscribe(response -> { + response.filters().forEach(filter -> { + logger.info("Filter: " + filter.arn()); + collectedFilterIds.append(filter.arn()).append("\n"); + }); + }).thenApply(v -> { + String result = collectedFilterIds.toString(); + logger.info("Successfully listed all filters."); + return result.isEmpty() ? "No filters found." : result; + }).exceptionally(ex -> { + Throwable cause = ex.getCause() != null ? ex.getCause() : ex; + + if (cause instanceof ValidationException ve) { + throw new CompletionException( + "Validation error listing filters: %s".formatted(ve.getMessage()), + ve + ); + } + + throw new RuntimeException("Failed to list filters", ex); + }); + } + // snippet-end:[inspector.java2.list_filters.main] + + // snippet-start:[inspector.java2.create.filter.main] + + /** + * Creates a new LOW severity filter in AWS Inspector2 to suppress findings. + * + * @param filterName the name of the filter to create + * @param description a descriptive string explaining the purpose of the filter + * @return a CompletableFuture that completes with the ARN of the created filter + * @throws CompletionException wraps any validation, Inspector2 service, or unexpected errors + */ + public CompletableFuture createLowSeverityFilterAsync( + String filterName, + String description) { + + // Define a filter to match LOW severity findings. + StringFilter severityFilter = StringFilter.builder() + .value(Severity.LOW.toString()) + .comparison(StringComparison.EQUALS) + .build(); + + // Create filter criteria. + FilterCriteria filterCriteria = FilterCriteria.builder() + .severity(Collections.singletonList(severityFilter)) + .build(); + + // Build the filter creation request. + CreateFilterRequest request = CreateFilterRequest.builder() + .name(filterName) + .filterCriteria(filterCriteria) + .action(FilterAction.SUPPRESS) + .description(description) + .build(); + + return getAsyncClient().createFilter(request) + .whenComplete((response, exception) -> { + if (exception != null) { + Throwable cause = exception.getCause() != null ? exception.getCause() : exception; + + if (cause instanceof ValidationException ve) { + throw new CompletionException( + "Validation error creating filter: %s".formatted(ve.getMessage()), + ve + ); + } + + if (cause instanceof Inspector2Exception e) { + throw new CompletionException( + "Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), + e + ); + } + + // Unexpected async error + throw new CompletionException( + "Unexpected error creating filter: %s".formatted(exception.getMessage()), + exception + ); + } + }) + // Extract and return the ARN of the created filter. + .thenApply(CreateFilterResponse::arn); + } + // snippet-end:[inspector.java2.create.filter.main] + + // snippet-start:[inspector.java2.list_findings.main] + + /** + * Lists all AWS Inspector findings of LOW severity asynchronously. + * + * @return CompletableFuture containing a List of finding ARNs. + * Returns an empty list if no LOW severity findings are found. + */ + public CompletableFuture> listLowSeverityFindingsAsync() { + logger.info("Starting async LOW severity findings paginator…"); + + // Build a filter criteria for LOW severity. + StringFilter severityFilter = StringFilter.builder() + .value(Severity.LOW.toString()) + .comparison(StringComparison.EQUALS) + .build(); + + FilterCriteria filterCriteria = FilterCriteria.builder() + .severity(Collections.singletonList(severityFilter)) + .build(); + + // Build the request. + ListFindingsRequest request = ListFindingsRequest.builder() + .filterCriteria(filterCriteria) + .build(); + + ListFindingsPublisher paginator = getAsyncClient().listFindingsPaginator(request); + List allArns = Collections.synchronizedList(new ArrayList<>()); + + return paginator.subscribe(response -> { + if (response.findings() != null && !response.findings().isEmpty()) { + response.findings().forEach(finding -> { + logger.info("Finding ARN: {}", finding.findingArn()); + allArns.add(finding.findingArn()); + }); + } else { + logger.info("Page contained no findings."); + } + }) + .thenRun(() -> logger.info("Successfully listed all LOW severity findings.")) + .thenApply(v -> new ArrayList<>(allArns)) // Return list instead of a formatted string + .exceptionally(ex -> { + Throwable cause = ex.getCause() != null ? ex.getCause() : ex; + if (cause instanceof ValidationException ve) { + throw new CompletionException( + "Validation error listing LOW severity findings: %s".formatted(ve.getMessage()), + ve + ); + } + throw new RuntimeException("Failed to list LOW severity findings", ex); + }); + } + + // snippet-end:[inspector.java2.list_findings.main] + + // snippet-start:[inspector.java2.list_coverage.main] + + /** + * Lists AWS Inspector2 coverage details for scanned resources using a paginator. + * + * @param maxResults Maximum number of resources to return. + */ + public CompletableFuture listCoverageAsync(int maxResults) { + ListCoverageRequest initialRequest = ListCoverageRequest.builder() + .maxResults(maxResults) + .build(); + + ListCoveragePublisher paginator = getAsyncClient().listCoveragePaginator(initialRequest); + StringBuilder summary = new StringBuilder(); + + return paginator.subscribe(response -> { + List coveredResources = response.coveredResources(); + + if (coveredResources == null || coveredResources.isEmpty()) { + summary.append("No coverage information available for this page.\n"); + return; + } + + Map> byType = coveredResources.stream() + .collect(Collectors.groupingBy(CoveredResource::resourceTypeAsString)); + + byType.forEach((type, list) -> + summary.append(" ").append(type) + .append(": ").append(list.size()) + .append(" resource(s)\n") + ); + + // Include up to 3 sample resources per page + for (int i = 0; i < Math.min(coveredResources.size(), 3); i++) { + CoveredResource r = coveredResources.get(i); + summary.append(" - ").append(r.resourceTypeAsString()) + .append(": ").append(r.resourceId()).append("\n"); + summary.append(" Scan Type: ").append(r.scanTypeAsString()).append("\n"); + if (r.scanStatus() != null) { + summary.append(" Status: ").append(r.scanStatus().statusCodeAsString()).append("\n"); + } + if (r.accountId() != null) { + summary.append(" Account ID: ").append(r.accountId()).append("\n"); + } + summary.append("\n"); + } + + }).thenApply(v -> { + if (summary.length() == 0) { + return "No coverage information found across all pages."; + } else { + return "Coverage Information:\n" + summary.toString(); + } + }).exceptionally(ex -> { + Throwable cause = ex.getCause(); + if (cause instanceof ValidationException) { + throw new CompletionException( + "Validation error listing coverage: " + cause.getMessage(), cause); + } else if (cause instanceof Inspector2Exception e) { + throw new CompletionException( + "Inspector2 service error: " + e.awsErrorDetails().errorMessage(), e); + } + throw new CompletionException("Unexpected error listing coverage: " + ex.getMessage(), ex); + }); + } + // snippet-end:[inspector.java2.list_coverage.main] + + // snippet-start:[inspector.java2.delete.filter.main] + + /** + * Deletes an AWS Inspector2 filter. + * + * @param filterARN The ARN of the filter to delete. + */ + public CompletableFuture deleteFilterAsync(String filterARN) { + return getAsyncClient().deleteFilter( + DeleteFilterRequest.builder() + .arn(filterARN) + .build() + ) + .handle((response, exception) -> { + if (exception != null) { + Throwable cause = exception.getCause() != null ? exception.getCause() : exception; + + if (cause instanceof ResourceNotFoundException rnfe) { + String msg = "Filter not found for ARN: %s".formatted(filterARN); + logger.warn(msg, rnfe); + throw new CompletionException(msg, rnfe); + } + + throw new RuntimeException("Failed to delete the filter: " + cause, cause); + } + return null; + }); + } + // snippet-end:[inspector.java2.delete.filter.main] + + // snippet-start:[inspector.java2.finding.details.main] + /** + * Retrieves detailed information about a specific AWS Inspector2 finding asynchronously. + * + * @param findingArn The ARN of the finding to look up. + * @return A {@link CompletableFuture} that, when completed, provides a formatted string + * containing all available details for the finding. + * @throws RuntimeException if the async call to Inspector2 fails. + */ + public CompletableFuture getFindingDetailsAsync(String findingArn) { + BatchGetFindingDetailsRequest request = BatchGetFindingDetailsRequest.builder() + .findingArns(findingArn) + .build(); + + return getAsyncClient().batchGetFindingDetails(request) + .thenApply(response -> { + if (response.findingDetails() == null || response.findingDetails().isEmpty()) { + return String.format("No details found for ARN: ", findingArn); + } + + StringBuilder sb = new StringBuilder(); + response.findingDetails().forEach(detail -> { + sb.append("Finding ARN: ").append(detail.findingArn()).append("\n") + .append("Risk Score: ").append(detail.riskScore()).append("\n"); + + // ExploitObserved timings + if (detail.exploitObserved() != null) { + sb.append("Exploit First Seen: ").append(detail.exploitObserved().firstSeen()).append("\n") + .append("Exploit Last Seen: ").append(detail.exploitObserved().lastSeen()).append("\n"); + } + + // Reference URLs + if (detail.hasReferenceUrls()) { + sb.append("Reference URLs:\n"); + detail.referenceUrls().forEach(url -> sb.append(" • ").append(url).append("\n")); + } + + // Tools + if (detail.hasTools()) { + sb.append("Tools:\n"); + detail.tools().forEach(tool -> sb.append(" • ").append(tool).append("\n")); + } + + // TTPs + if (detail.hasTtps()) { + sb.append("TTPs:\n"); + detail.ttps().forEach(ttp -> sb.append(" • ").append(ttp).append("\n")); + } + + // CWEs + if (detail.hasCwes()) { + sb.append("CWEs:\n"); + detail.cwes().forEach(cwe -> sb.append(" • ").append(cwe).append("\n")); + } + + // Evidence + if (detail.hasEvidences()) { + sb.append("Evidence:\n"); + detail.evidences().forEach(ev -> { + sb.append(" - Severity: ").append(ev.severity()).append("\n"); + + }); + } + + sb.append("\n"); + }); + + return sb.toString(); + }) + .exceptionally(ex -> { + Throwable cause = ex.getCause() != null ? ex.getCause() : ex; + + if (cause instanceof ResourceNotFoundException rnfe) { + return "Finding not found: %s".formatted(findingArn); + } + + // Fallback for other exceptions + throw new RuntimeException("Failed to get finding details for ARN: " + findingArn, cause); + }); + } + // snippet-end:[inspector.java2.finding.details.main] + + + // snippet-start:[inspector.java2.disable.main] + /** + * Asynchronously disables AWS Inspector for the specified accounts and resource types. + * + * @param accountIds a {@link List} of AWS account IDs for which to disable Inspector; + * may be {@code null} or empty to target the current account + * @return a {@link CompletableFuture} that, when completed, returns a {@link String} + * summarizing the disable results for each account + * @throws CompletionException if the disable operation fails due to validation errors, + * service errors, or other exceptions + * @see + * AWS Inspector2 Disable API + */ + public CompletableFuture disableInspectorAsync(List accountIds) { + + // The resource types to disable. + List resourceTypes = List.of( + ResourceScanType.EC2, + ResourceScanType.ECR, + ResourceScanType.LAMBDA, + ResourceScanType.LAMBDA_CODE + ); + + // Build the request. + DisableRequest.Builder requestBuilder = DisableRequest.builder() + .resourceTypes(resourceTypes); + + if (accountIds != null && !accountIds.isEmpty()) { + requestBuilder.accountIds(accountIds); + } + + DisableRequest request = requestBuilder.build(); + + return getAsyncClient().disable(request) + .whenComplete((response, exception) -> { + if (exception != null) { + Throwable cause = exception.getCause(); + if (cause instanceof ValidationException) { + throw new CompletionException( + "Inspector may already be disabled for this account: %s".formatted(cause.getMessage()), + cause + ); + } + + if (cause instanceof Inspector2Exception) { + Inspector2Exception e = (Inspector2Exception) cause; + throw new CompletionException( + "AWS Inspector2 service error: %s".formatted(e.awsErrorDetails().errorMessage()), + cause + ); + } + + throw new CompletionException( + "Failed to disable Inspector: %s".formatted(exception.getMessage()), + exception + ); + } + }) + .thenApply(response -> { + StringBuilder summary = new StringBuilder("Disable results:\n"); + + if (response.accounts() == null || response.accounts().isEmpty()) { + summary.append("Inspector may already be disabled for all target accounts."); + return summary.toString(); + } + + for (Account account : response.accounts()) { + String accountId = account.accountId() != null ? account.accountId() : "Unknown"; + String status = account.status() != null ? account.statusAsString() : "Unknown"; + summary.append(" • Account: ").append(accountId) + .append(" → Status: ").append(status).append("\n"); + } + + return summary.toString(); + }); + } + // snippet-end:[inspector.java2.disable.main] +} +// snippet-end:[inspector.java2_actions.main] \ No newline at end of file diff --git a/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorScenario.java b/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorScenario.java new file mode 100644 index 00000000000..dcbbad9c4a1 --- /dev/null +++ b/javav2/example_code/inspector/src/main/java/com/java/inspector/InspectorScenario.java @@ -0,0 +1,208 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.java.inspector; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Scanner; + +// snippet-start:[inspector.java2_scenario.main] + +/** + * Before running this Java V2 code example, set up your development + * environment, including your credentials. + *

+ * For more information, see the following documentation topic: + *

+ * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html + */ +public class InspectorScenario { + + public static final String DASHES = new String(new char[80]).replace("\0", "-"); + private static final Logger logger = LoggerFactory.getLogger(InspectorScenario.class); + private static final Scanner scanner = new Scanner(System.in); + + public static void main(String[] args) { + InspectorActions inspectorActions = new InspectorActions(); + logger.info("Amazon Inspector Basics Scenario"); + + logger.info(""" + Amazon Inspector is a security assessment service provided by Amazon Web Services (AWS) that helps + improve the security and compliance of applications deployed on AWS. It automatically assesses + applications for vulnerabilities or deviations from best practices. By leveraging Amazon Inspector, + users can gain insights into the overall security state of their application and identify potential + security risks. + + This service operates by conducting both network and host-based assessments, allowing it to detect a + wide range of security issues, including those related to operating systems, network configurations, + and application dependencies. + """); + + waitForInputToContinue(); + + try { + runScenario(inspectorActions); + + logger.info(""); + logger.info("Scenario completed successfully!"); + logger.info(""); + logger.info("What you learned:"); + logger.info(" - How to check Inspector account status"); + logger.info(" - How to enable Inspector"); + logger.info(" - How to list and analyze findings"); + logger.info(" - How to check coverage information"); + logger.info(" - How to create and manage filters"); + logger.info(" - How to track usage and costs"); + logger.info(" - How to clean up resources"); + logger.info(""); + + } catch (Exception ex) { + logger.error("Scenario failed due to unexpected error: {}", ex.getMessage(), ex); + + } finally { + scanner.close(); + logger.info("Exiting..."); + } + } + + /** + * Runs the Inspector scenario in a step-by-step sequence. + * + * All InspectorActions methods are asynchronous and return CompletableFutures. + * Each step ends with .join(). Any async exception thrown during .join() will bubble up + * + */ + public static void runScenario(InspectorActions actions) { + String filterArn = null; + boolean inspectorEnabled = false; + + try { + // Step 1 + logger.info(DASHES); + logger.info("Step 1: Checking Inspector account status..."); + String status = actions.getAccountStatusAsync().join(); + logger.info(status); + waitForInputToContinue(); + + // Step 2 + logger.info(DASHES); + logger.info("Step 2: Enabling Inspector..."); + String message = actions.enableInspectorAsync(null).join(); + logger.info(message); + inspectorEnabled = true; // track that Inspector was enabled + waitForInputToContinue(); + + // Step 3 + logger.info(DASHES); + logger.info("Step 3: Listing LOW severity findings..."); + + // Call the service method + List allFindings = actions.listLowSeverityFindingsAsync().join(); + + if (!allFindings.isEmpty()) { + // Only proceed if there are findings + String lastArn = allFindings.get(allFindings.size() - 1); + logger.info("Look up details on: {}", lastArn); + waitForInputToContinue(); + String details = actions.getFindingDetailsAsync(lastArn).join(); + logger.info(details); + } else { + logger.info("No LOW severity findings found."); + } + + waitForInputToContinue(); + + // Step 4 + logger.info(DASHES); + logger.info("Step 4: Listing coverage..."); + String coverage = actions.listCoverageAsync(5).join(); + logger.info(coverage); + waitForInputToContinue(); + + // Step 5 + logger.info(DASHES); + logger.info("Step 5: Creating filter..."); + String filterName = "suppress-low-" + System.currentTimeMillis(); + filterArn = actions.createLowSeverityFilterAsync(filterName, "Suppress low severity findings").join(); + logger.info("Created filter: {}", filterArn); + waitForInputToContinue(); + + // Step 6 + logger.info(DASHES); + logger.info("Step 6: Listing filters..."); + String filters = actions.listFiltersAsync(10).join(); + logger.info(filters); + waitForInputToContinue(); + + // Step 7 + logger.info(DASHES); + logger.info("Step 7: Usage totals..."); + String usage = actions.listUsageTotalsAsync(null, 10).join(); + logger.info(usage); + waitForInputToContinue(); + + // Step 8 + logger.info(DASHES); + logger.info("Step 8: Coverage statistics..."); + String stats = actions.listCoverageStatisticsAsync().join(); + logger.info(stats); + waitForInputToContinue(); + + // Step 9 + logger.info(DASHES); + logger.info("Step 9: Delete filter?"); + logger.info("Filter ARN: {}", filterArn); + logger.info("Delete the filter and disable Inspector? (y/n)"); + + if (scanner.nextLine().trim().equalsIgnoreCase("y")) { + actions.deleteFilterAsync(filterArn).join(); + logger.info("Filter deleted."); + String disableMsg = actions.disableInspectorAsync(null).join(); + logger.info(disableMsg); + inspectorEnabled = false; // track that Inspector was disabled + } + + waitForInputToContinue(); + + } catch (Exception ex) { + logger.error("Scenario encountered an error: {}", ex.getMessage(), ex); + // Rethrow the exception + throw ex; + + } finally { + // Cleanup in case of an exception + if (filterArn != null) { + try { + actions.deleteFilterAsync(filterArn).join(); + logger.info("Cleanup: Filter deleted."); + } catch (Exception e) { + logger.warn("Failed to delete filter during cleanup: {}", e.getMessage(), e); + } + } + + if (inspectorEnabled) { + try { + actions.disableInspectorAsync(null).join(); + logger.info("Cleanup: Inspector disabled."); + } catch (Exception e) { + logger.warn("Failed to disable Inspector during cleanup: {}", e.getMessage(), e); + } + } + } + } + + // Utility Method + private static void waitForInputToContinue() { + while (true) { + logger.info(""); + logger.info("Enter 'c' to continue:"); + String input = scanner.nextLine().trim(); + if (input.equalsIgnoreCase("c")) break; + logger.info("Invalid input, try again."); + } + } +} +// snippet-end:[inspector.java2_scenario.main] \ No newline at end of file diff --git a/javav2/example_code/inspector/src/main/resources/log4j2.xml b/javav2/example_code/inspector/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..914470047e7 --- /dev/null +++ b/javav2/example_code/inspector/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/javav2/example_code/inspector/src/test/java/InspectorTests.java b/javav2/example_code/inspector/src/test/java/InspectorTests.java new file mode 100644 index 00000000000..e5ff85ca7cb --- /dev/null +++ b/javav2/example_code/inspector/src/test/java/InspectorTests.java @@ -0,0 +1,148 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import com.java.inspector.InspectorScenario; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.java.inspector.HelloInspector; +import com.java.inspector.InspectorActions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestMethodOrder; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.inspector2.Inspector2Client; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class InspectorTests { + private static Inspector2Client inspector; + private static InspectorActions inspectorActions; + private static final Logger logger = LoggerFactory.getLogger(InspectorTests.class); + @BeforeAll + public static void setUp() { + inspector = Inspector2Client.builder() + .region(Region.US_EAST_1) + .build() ; + + inspectorActions = new InspectorActions(); + } + + @Test + @Tag("IntegrationTest") + @Order(1) + public void testHelloService() { + assertDoesNotThrow(() -> { + HelloInspector.listMembers(inspector); + }); + logger.info("Test 1 passed"); + } + + + /** + * Integration test for InspectorActions Async methods. + * + * This test validates that all async action methods complete successfully and + * return expected values (like filter ARN). + * + * Note that it will fail the test if any .join() throws a CompletionException. + */ + + @Test + @Tag("IntegrationTest") + @Order(2) + public void testInspectorActionsIntegration() { + assertDoesNotThrow(() -> { + int maxResults = 10; + + String filterName = "suppress-low-severity-" + System.currentTimeMillis(); + + inspectorActions.getAccountStatusAsync().join(); + + inspectorActions.enableInspectorAsync(null).join(); + + List allFindings = inspectorActions.listLowSeverityFindingsAsync().join(); + if (!allFindings.isEmpty()) { + // Only proceed if there are findings + String lastArn = allFindings.get(allFindings.size() - 1); + logger.info("Fetching details for last finding ARN: {}", lastArn); + + String details = inspectorActions.getFindingDetailsAsync(lastArn).join(); + + // Check details are not empty/null + assertNotNull(details, "Finding details should not be null"); + assertFalse(details.isEmpty(), "Finding details should not be empty"); + } else { + logger.info("No LOW severity findings found."); + } + + maxResults = 5; + inspectorActions.listCoverageAsync(maxResults).join(); + + String filterARN = inspectorActions.createLowSeverityFilterAsync(filterName,"Suppress low severity findings for demo purposes").join(); + + // Assert it returned a valid ARN + assertNotNull(filterARN, "Filter ARN should not be null"); + assertFalse(filterARN.isBlank(), "Filter ARN should not be empty"); + + inspectorActions.listFiltersAsync(10).join(); + + inspectorActions.listUsageTotalsAsync(null, 10).join(); + + inspectorActions.listCoverageStatisticsAsync().join(); + + inspectorActions.deleteFilterAsync(filterARN).join(); + + inspectorActions.disableInspectorAsync(null).join(); + }); + + logger.info("Test 2 passed"); + } + + + @Test + @Tag("IntegrationTest") + @Order(3) + public void testInspectorScenarioEndToEnd() { + assertDoesNotThrow(() -> { + // The scenario calls scanner.nextLine() repeatedly. + // We simulate user input by providing many "c" lines. + String simulatedInput = String.join("\n", + Collections.nCopies(20, "c")) + "\n"; + + InputStream originalIn = System.in; + PrintStream originalOut = System.out; + + try { + // Redirect System.in to simulated input + ByteArrayInputStream testIn = new ByteArrayInputStream(simulatedInput.getBytes()); + System.setIn(testIn); + + // Capture System.out so logs don’t spam the console + System.setOut(new PrintStream(new ByteArrayOutputStream())); + + // Run the scenario + InspectorScenario.main(new String[]{}); + + } finally { + // Restore original I/O streams + System.setIn(originalIn); + System.setOut(originalOut); + } + }); + + logger.info("Test 3 (Scenario end-to-end) passed"); + } +} \ No newline at end of file diff --git a/scenarios/basics/inspector/SPECIFICATION.md b/scenarios/basics/inspector/SPECIFICATION.md index 51eece5c95f..784cf2ba868 100644 --- a/scenarios/basics/inspector/SPECIFICATION.md +++ b/scenarios/basics/inspector/SPECIFICATION.md @@ -1,6 +1,9 @@ # Amazon Inspector Specification -This document contains a draft proposal for an *Amazon Inspector Basics Scenario*, generated by the Code Examples SpecGen AI tool. The specifications describe a potential code example scenario based on research, usage data, service information, and AI-assistance. The following should be reviewed for accuracy and correctness before proceeding on to a final specification. +This SDK Basics scenario demonstrates how to interact with Amazon Inspector, a basics scenario that showcases AWS services and SDKs. It is primarily intended for the AWS code examples team to use while developing this example in additional languages. + +## Resources +This Basics scenario does not require any additional AWS resources. ### Relevant documentation @@ -11,79 +14,122 @@ This document contains a draft proposal for an *Amazon Inspector Basics Scenario ### API Actions Used +* [CreateFilter](https://docs.aws.amazon.com/inspector/v2/APIReference/API_CreateFilter.html) + * [Enable](https://docs.aws.amazon.com/inspector/v2/APIReference/API_Enable.html) + +* [ListCoverageStatistics](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListCoverageStatistics.html) + +* [ListUsageTotals](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListUsageTotals.html) + * [BatchGetAccountStatus](https://docs.aws.amazon.com/inspector/v2/APIReference/API_BatchGetAccountStatus.html) + +* [ListFilters](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListFilters.html) + * [ListFindings](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListFindings.html) + * [BatchGetFindingDetails](https://docs.aws.amazon.com/inspector/v2/APIReference/API_BatchGetFindingDetails.html) + * [ListCoverage](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListCoverage.html) -* [Disable](https://docs.aws.amazon.com/inspector/v2/APIReference/API_Disable.html) -## Proposed example structure +* [DeleteFilter](https://docs.aws.amazon.com/inspector/v2/APIReference/API_DeleteFilter.html) -The output below demonstrates how this example would run for the customer. It includes a Hello service example (included for all services), and the scenario description. The scenario code would also be presented as Action snippets, with a code snippet for each SDK action. +- [Disable](https://docs.aws.amazon.com/inspector/v2/APIReference/API_Disable.html) -### Hello -The Hello example is a separate runnable example. - Set up the Inspector service client - Check the current account status for Inspector - Display available scan types and regions +## Hello Amazon Inspector + +The Hello example is designed for users who are new to Amazon Inspector. It demonstrates how to set up the Inspector service client and retrieve a list of all member accounts associated with the current Inspector administrator account. ## Scenario -#### Setup +This scenario demonstrates the basic usage of **Amazon Inspector** using a Java program. It focuses on checking account status, enabling Inspector, listing findings, reviewing coverage, and managing filters. + +--- + +### Setup -* Enable Amazon Inspector for the account -* Verify Inspector is successfully activated -* Display account status and enabled scan types +* Check Amazon Inspector account status +* Enable Inspector for available resource types (if not already enabled) +* Display account status summary -#### Coverage Assessment +--- -* List coverage statistics for EC2 instances, ECR repositories, and Lambda functions -* Display resource coverage details -* Show scanning status for different resource types +### Coverage Assessment -#### Findings Management +* List coverage details for scanned resources +* Display overall coverage statistics +* Review scan status for resources (general overview) + +--- + +### Findings Management * List security findings across all resource types -* Filter findings by severity level (CRITICAL, HIGH, MEDIUM, LOW) -* Retrieve detailed information for specific findings +* Create an example filter to suppress low-severity findings +* List existing filters + +--- + +### Usage and Costs + +* Check usage totals and metrics for Inspector +* Review coverage statistics + +--- -#### Vulnerability Analysis +### Cleanup -* Display vulnerability details including CVE information -* Show affected resources and remediation guidance -* Filter findings by resource type (EC2, ECR, Lambda) +* Delete the filter +* Diable inspector -#### Cleanup +--- -* Optionally disable Inspector scanning (with user confirmation) -* Display final account status +### Outcome + +By following this scenario, users learn how to: + +* Check Inspector account status and configuration +* Enable Inspector for different resource types +* List and analyze security findings +* Monitor scan coverage +* Create and manage filters +* Track usage and coverage statistics +* Cleanup the resources ## Errors -SDK Code examples include basic exception handling for each action used. The table below describes an appropriate exception which will be handled in the code for each service action. - -|Action |Error |Handling | -|--- |--- |--- | -|`Enable` |ValidationException |Validate resource types and account permissions. | -|`Enable` |AccessDeniedException |Notify user of insufficient permissions and exit. | -|`BatchGetAccountStatus` |ValidationException |Validate account IDs format. | -|`BatchGetAccountStatus` |AccessDeniedException |Handle permission errors gracefully. | -|`ListFindings` |ValidationException |Validate filter criteria and pagination parameters. | -|`ListFindings` |InternalServerException |Retry operation with exponential backoff. | -|`BatchGetFindingDetails` |ValidationException |Validate finding ARNs format. | -|`BatchGetFindingDetails` |AccessDeniedException |Handle access denied for specific findings. | -|`ListCoverage` |ValidationException |Validate filter and pagination parameters. | -|`Disable` |ValidationException |Validate resource types for disabling. | -|`Disable` |ConflictException |Handle cases where Inspector cannot be disabled. | +The table below describes the exceptions handled in the program for each action. -## Metadata +| Action | Exception | Handling | +|-------------------------------|---------------------------|--------------------------------------------------------------------------| +| `Enable` | `ValidationException` | Prints a message indicating Inspector may already be enabled. | +| `Disable` | `ValidationException` | Prints a message indicating Inspector may already be disabled. | +| `listUsageTotals` | `ValidationException` | Validation error listing usage totals. +| `BatchGetAccountStatus` | `AccessDeniedException` | Prints AWS service error details and rethrows the exception. | +| `ListFindings` | `ValidationException` | Prints validation error details. | +| `ListCoverage` | `ValidationException` | Prints validation error details. | +| `ListCoverageStatistics` | `ValidationException` | Prints validation error details. | +| `createFilter` | `ValidationException` | Prints validation error details. | +| `ListFilters` | `ValidationException` | Prints AWS service error details and rethrows the exception. | +| `deleteFilter` | `ResourceNotFoundException` | Prints AWS service error details and rethrows the exception. | +| `batchGetFindingDetails` | `ResourceNotFoundException` | Prints AWS service error details and rethrows the exception. | -|action / scenario |metadata file |metadata key | -|--- |--- |--- | -|`Enable` |inspector_metadata.yaml |inspector_Enable | -|`BatchGetAccountStatus` |inspector_metadata.yaml |inspector_BatchGetAccountStatus | -|`ListFindings` |inspector_metadata.yaml |inspector_ListFindings | -|`BatchGetFindingDetails` |inspector_metadata.yaml |inspector_BatchGetFindingDetails | -|`ListCoverage` |inspector_metadata.yaml |inspector_ListCoverage | -|`Disable` |inspector_metadata.yaml |inspector_Disable | -|`Amazon Inspector Basics Scenario` |inspector_metadata.yaml |inspector_Scenario | +## Metadata + +| Action / Scenario | Metadata File | Metadata Key | +|-----------------------------------------|------------------------|-------------------------------| +| `Enable` | inspector_metadata.yaml | inspector_Enable | +| `Disable` | inspector_metadata.yaml | inspector_Disable | +| `BatchGetAccountStatus` | inspector_metadata.yaml | inspector_GetAccountStatus | +| `ListFindings` | inspector_metadata.yaml | inspector_ListFindings | +| `ListCoverage` | inspector_metadata.yaml | inspector_ListCoverage | +| `ListCoverageStatistics` | inspector_metadata.yaml | inspector_ListCoverageStatistics | +| `ListUsageTotals` | inspector_metadata.yaml | inspector_ListUsageTotals | +| `CreateFilter` | inspector_metadata.yaml | inspector_CreateFilter | +| `ListFilters` | inspector_metadata.yaml | inspector_ListFilters | +| `DeleteFilter` | inspector_metadata.yaml | inspector_DeleteFilter | +| `BatchGetFindingDetails` | inspector_metadata.yaml | inspector_BatchGetFindingDetails | +| `Amazon Inspector Hello` | inspector_metadata.yaml | inspector_Hello | +| `Amazon Inspector Basics Scenario` | inspector_metadata.yaml | inspector_Scenario \ No newline at end of file