Security scanning for serverless Java applications involves checking code, dependencies, configuration, and cloud resources for vulnerabilities and misconfigurations.
1. Serverless Java Project Setup with Security Scanning
Sample Serverless Framework Template (serverless.yml)
service: java-serverless-api plugins: - serverless-offline - serverless-dependency-check - serverless-prune-plugin - serverless-iam-roles-per-function provider: name: aws runtime: java11 region: us-east-1 stage: ${opt:stage, 'dev'} memorySize: 2048 timeout: 30 versionFunctions: false # Security settings tracing: apiGateway: true lambda: true logs: restApi: true environment: STAGE: ${self:provider.stage} SECURITY_SCAN_ENABLED: true DEPENDENCY_CHECK_ENABLED: true package: individually: true exclude: - '**/*.log' - '**/*.md' - '**/*.txt' - '.git/**' - '.github/**' - '.vscode/**' - 'node_modules/**' - '.serverless/**' functions: apiHandler: handler: com.example.ApiHandler memorySize: 1024 timeout: 15 events: - http: path: /api/{proxy+} method: ANY cors: true environment: FUNCTION_NAME: apiHandler package: include: - build/libs/** - dependency-check-report.html securityScanner: handler: com.example.SecurityScanner memorySize: 1536 timeout: 300 events: - schedule: rate(1 day) environment: SCAN_TYPE: full REPORT_BUCKET: security-reports-${self:provider.stage} resources: Resources: SecurityReportsBucket: Type: AWS::S3::Bucket Properties: BucketName: security-reports-${self:provider.stage} VersioningConfiguration: Status: Enabled LoggingConfiguration: DestinationBucketName: !Ref AccessLogsBucket LogFilePrefix: security-reports/ PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true AccessLogsBucket: Type: AWS::S3::Bucket Properties: BucketName: access-logs-${self:provider.stage} PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true 2. Dependency Security Scanning
OWASP Dependency Check Maven Configuration (pom.xml)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>serverless-java-api</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- Security Scanning Versions --> <owasp.dependency.check.version>8.2.1</owasp.dependency.check.version> <spotbugs.version>4.7.3</spotbugs.version> <pmd.version>6.55.0</pmd.version> <checkstyle.version>10.9.3</checkstyle.version> <cyclonedx.version>2.7.9</cyclonedx.version> </properties> <dependencies> <!-- AWS Lambda Dependencies --> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> <version>3.11.1</version> </dependency> <!-- Web Framework --> <dependency> <groupId>com.amazonaws.serverless</groupId> <artifactId>aws-serverless-java-container-core</artifactId> <version>1.9.1</version> </dependency> <!-- Security Dependencies --> <dependency> <groupId>org.owasp.encoder</groupId> <artifactId>encoder</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>com.github.java-json-tools</groupId> <artifactId>json-schema-validator</artifactId> <version>2.2.14</version> </dependency> </dependencies> <build> <plugins> <!-- Maven Shade Plugin for Lambda --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> </plugin> <!-- OWASP Dependency Check --> <plugin> <groupId>org.owasp</groupId> <artifactId>dependency-check-maven</artifactId> <version>${owasp.dependency.check.version}</version> <executions> <execution> <goals> <goal>check</goal> </goals> </execution> </executions> <configuration> <format>HTML</format> <formats>HTML,JSON,XML</formats> <failBuildOnAnyVulnerability>true</failBuildOnAnyVulnerability> <failOnError>true</failOnError> <suppressionFile>dependency-check-suppressions.xml</suppressionFile> <cveValidForHours>24</cveValidForHours> <skipTestScope>true</skipTestScope> <assemblyAnalyzerEnabled>false</assemblyAnalyzerEnabled> <nodeAnalyzerEnabled>false</nodeAnalyzerEnabled> <nodeAuditAnalyzerEnabled>false</nodeAuditAnalyzerEnabled> <retireJsAnalyzerEnabled>false</retireJsAnalyzerEnabled> </configuration> </plugin> <!-- SpotBugs Security Scanner --> <plugin> <groupId>com.github.spotbugs</groupId> <artifactId>spotbugs-maven-plugin</artifactId> <version>${spotbugs.version}</version> <configuration> <effort>Max</effort> <threshold>Low</threshold> <failOnError>true</failOnError> <includeFilterFile>spotbugs-security-include.xml</includeFilterFile> <excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile> </configuration> <executions> <execution> <phase>verify</phase> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> <!-- PMD Static Analysis --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <version>${pmd.version}</version> <configuration> <rulesets> <ruleset>category/java/security.xml</ruleset> <ruleset>category/java/bestpractices.xml</ruleset> </rulesets> <failOnViolation>true</failOnViolation> <printFailingErrors>true</printFailingErrors> </configuration> <executions> <execution> <phase>verify</phase> <goals> <goal>check</goal> </goals> </execution> </executions> </plugin> <!-- CycloneDX SBOM Generation --> <plugin> <groupId>org.cyclonedx</groupId> <artifactId>cyclonedx-maven-plugin</artifactId> <version>${cyclonedx.version}</version> <executions> <execution> <phase>package</phase> <goals> <goal>makeAggregateBom</goal> </goals> </execution> </executions> <configuration> <projectType>library</projectType> <schemaVersion>1.4</schemaVersion> <includeBomSerialNumber>true</includeBomSerialNumber> <includeCompileScope>true</includeCompileScope> <includeRuntimeScope>true</includeRuntimeScope> <includeTestScope>false</includeTestScope> <outputFormat>all</outputFormat> <outputName>sbom</outputName> </configuration> </plugin> </plugins> </build> </project> Dependency Check Suppression File (dependency-check-suppressions.xml)
<?xml version="1.0" encoding="UTF-8"?> <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd"> <!-- Suppress false positives --> <suppress> <notes><![CDATA[ False positive - CVE affects different component ]]></notes> <cve>CVE-2022-00000</cve> </suppress> <suppress base="true"> <notes><![CDATA[ Suppress vulnerabilities in test scope dependencies ]]></notes> <gav regex="true">.*:.*:.*:.*:test</gav> </suppress> <suppress> <notes><![CDATA[ Accept risk for specific library with no available fix ]]></notes> <cve>CVE-2023-12345</cve> </suppress> </suppressions>
3. Code Security Scanning
SpotBugs Security Configuration (spotbugs-security-include.xml)
<?xml version="1.0" encoding="UTF-8"?> <FindBugsFilter> <Match> <Bug category="SECURITY"/> </Match> <Match> <Bug category="MALICIOUS_CODE"/> </Match> <Match> <Bug pattern="SQL_INJECTION"/> </Match> <Match> <Bug pattern="COMMAND_INJECTION"/> </Match> <Match> <Bug pattern="PATH_TRAVERSAL_IN"/> </Match> <Match> <Bug pattern="PATH_TRAVERSAL_OUT"/> </Match> <Match> <Bug pattern="LDAP_INJECTION"/> </Match> <Match> <Bug pattern="XXE_XMLSTREAMREADER"/> </Match> <Match> <Bug pattern="WEAK_MESSAGE_DIGEST"/> </Match> <Match> <Bug pattern="CIPHER_INTEGRITY"/> </Match> </FindBugsFilter>
Security-Focused Lambda Handler
package com.example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import org.owasp.encoder.Encode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; public class ApiHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { private static final Logger logger = LoggerFactory.getLogger(ApiHandler.class); private final SecurityScanner securityScanner; public ApiHandler() { this.securityScanner = new SecurityScanner(); } @Override public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { try { // Input validation and security checks SecurityScanResult scanResult = securityScanner.scanInput(input); if (!scanResult.isSafe()) { return createErrorResponse(400, "Security validation failed: " + scanResult.getIssues()); } // Sanitize headers and parameters Map<String, String> sanitizedHeaders = sanitizeHeaders(input.getHeaders()); Map<String, String> sanitizedQueryParams = sanitizeQueryParameters(input.getQueryStringParameters()); // Process request String response = processRequest(input.getBody(), sanitizedHeaders, sanitizedQueryParams); return createSuccessResponse(response); } catch (SecurityException e) { logger.warn("Security violation detected", e); return createErrorResponse(403, "Security violation"); } catch (Exception e) { logger.error("Unexpected error", e); return createErrorResponse(500, "Internal server error"); } } private Map<String, String> sanitizeHeaders(Map<String, String> headers) { if (headers == null) return new HashMap<>(); Map<String, String> sanitized = new HashMap<>(); for (Map.Entry<String, String> entry : headers.entrySet()) { String key = Encode.forHtml(entry.getKey()); String value = Encode.forHtml(entry.getValue()); sanitized.put(key, value); } return sanitized; } private Map<String, String> sanitizeQueryParameters(Map<String, String> queryParams) { if (queryParams == null) return new HashMap<>(); Map<String, String> sanitized = new HashMap<>(); for (Map.Entry<String, String> entry : queryParams.entrySet()) { String key = Encode.forJava(entry.getKey()); String value = Encode.forJava(entry.getValue()); sanitized.put(key, value); } return sanitized; } private APIGatewayProxyResponseEvent createSuccessResponse(String body) { APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); response.setStatusCode(200); response.setBody(body); Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("X-Content-Type-Options", "nosniff"); headers.put("X-Frame-Options", "DENY"); headers.put("X-XSS-Protection", "1; mode=block"); response.setHeaders(headers); return response; } private APIGatewayProxyResponseEvent createErrorResponse(int statusCode, String message) { APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent(); response.setStatusCode(statusCode); String safeMessage = Encode.forHtml(message); response.setBody("{\"error\": \"" + safeMessage + "\"}"); Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); response.setHeaders(headers); return response; } private String processRequest(String body, Map<String, String> headers, Map<String, String> queryParams) { // Business logic implementation return "{\"status\": \"success\"}"; } } Security Scanner Implementation
package com.example; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; public class SecurityScanner { private static final ObjectMapper mapper = new ObjectMapper(); // Security patterns private static final Pattern SQL_INJECTION_PATTERN = Pattern.compile("('(''|[^'])*')|(;)|(\\b(ALTER|CREATE|DELETE|DROP|EXEC(UTE){0,1}|INSERT( +INTO){0,1}|MERGE|SELECT|UPDATE|UNION( +ALL){0,1})\\b)", Pattern.CASE_INSENSITIVE); private static final Pattern XSS_PATTERN = Pattern.compile("<script|javascript:|onload=|onerror=", Pattern.CASE_INSENSITIVE); private static final Pattern PATH_TRAVERSAL_PATTERN = Pattern.compile("(\\.\\./|\\.\\.\\\\)"); public SecurityScanResult scanInput(APIGatewayProxyRequestEvent input) { List<String> issues = new ArrayList<>(); // Scan headers if (input.getHeaders() != null) { input.getHeaders().forEach((key, value) -> { if (detectMaliciousContent(key) || detectMaliciousContent(value)) { issues.add("Malicious content in header: " + key); } }); } // Scan query parameters if (input.getQueryStringParameters() != null) { input.getQueryStringParameters().forEach((key, value) -> { if (detectSQLInjection(value) || detectXSS(value) || detectPathTraversal(value)) { issues.add("Malicious content in query parameter: " + key); } }); } // Scan request body if (input.getBody() != null) { try { JsonNode body = mapper.readTree(input.getBody()); scanJsonNode(body, issues, "body"); } catch (Exception e) { issues.add("Invalid JSON in request body"); } } // Scan path parameters if (input.getPathParameters() != null) { input.getPathParameters().forEach((key, value) -> { if (detectPathTraversal(value)) { issues.add("Path traversal attempt in path parameter: " + key); } }); } return new SecurityScanResult(issues.isEmpty(), issues); } private boolean detectMaliciousContent(String input) { return detectSQLInjection(input) || detectXSS(input) || detectPathTraversal(input); } private boolean detectSQLInjection(String input) { return SQL_INJECTION_PATTERN.matcher(input).find(); } private boolean detectXSS(String input) { return XSS_PATTERN.matcher(input).find(); } private boolean detectPathTraversal(String input) { return PATH_TRAVERSAL_PATTERN.matcher(input).find(); } private void scanJsonNode(JsonNode node, List<String> issues, String path) { if (node.isObject()) { node.fields().forEachRemaining(entry -> { String fieldPath = path + "." + entry.getKey(); if (entry.getValue().isTextual()) { String value = entry.getValue().asText(); if (detectMaliciousContent(value)) { issues.add("Malicious content in " + fieldPath); } } else { scanJsonNode(entry.getValue(), issues, fieldPath); } }); } else if (node.isArray()) { for (int i = 0; i < node.size(); i++) { scanJsonNode(node.get(i), issues, path + "[" + i + "]"); } } } public static class SecurityScanResult { private final boolean safe; private final List<String> issues; public SecurityScanResult(boolean safe, List<String> issues) { this.safe = safe; this.issues = issues; } public boolean isSafe() { return safe; } public List<String> getIssues() { return issues; } } } 4. Infrastructure Security Scanning
CFN-NAG Security Scanning
cfn-nag-rules.yml
Rules: - id: W1 reason: "Security groups found with cidr open to world" rule: W2 reason: "Security groups found ingress with port range instead of just a single port" rule: W9 reason: "Security groups found with cidr open to world on port 22" rule: W12 reason: "Security groups found ingress with port range instead of just a single port" rule: W27 reason: "Security groups found with cidr open to world on port 3389" rule: W33 reason: "Security groups found egress with port range instead of just a single port"
Serverless Configuration with Security Hardening
# serverless-secure.yml service: secure-java-serverless provider: name: aws runtime: java11 stage: ${opt:stage, 'prod'} # Enhanced security settings apiGateway: apiKeys: - name: apiKey-${self:provider.stage} usagePlan: quota: limit: 1000 offset: 2 period: DAY throttle: burstLimit: 200 rateLimit: 100 iamRoleStatements: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - arn:aws:logs:*:*:* - Effect: Deny Action: - s3:DeleteBucket - s3:DeleteBucketPolicy - s3:DeleteObject - s3:DeleteObjectVersion Resource: "*" stackTags: Environment: ${self:provider.stage} SecurityLevel: High DataClassification: Confidential vpc: securityGroupIds: - sg-secure123 subnetIds: - subnet-private1 - subnet-private2 functions: secureApi: handler: com.example.SecureApiHandler memorySize: 1024 timeout: 10 environment: ENCRYPTION_KEY: ${ssm:/aws/reference/secretsmanager/encryption-key} DB_PASSWORD: ${ssm:/aws/reference/secretsmanager/db-password} events: - http: path: /secure/{proxy+} method: ANY private: true authorizer: name: customAuthorizer type: TOKEN layers: - arn:aws:lambda:us-east-1:123456789012:layer:security-layer:1 resources: Resources: CustomAuthorizer: Type: AWS::Lambda::Function Properties: FunctionName: custom-authorizer Runtime: java11 Handler: com.example.CustomAuthorizer Code: S3Bucket: security-lambdas S3Key: authorizer.jar MemorySize: 512 Timeout: 5 SecurityLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /aws/lambda/secure-java-api RetentionInDays: 365 Outputs: ApiUrl: Description: "Secure API URL" Value: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/${self:provider.stage}" SecurityScanReport: Description: "Security scan report location" Value: !Ref SecurityReportsBucket 5. CI/CD Security Scanning Pipeline
GitHub Actions Workflow (.github/workflows/security-scan.yml)
name: Serverless Java Security Scan on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: - cron: '0 2 * * 1' # Weekly on Monday at 2 AM jobs: security-scan: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up JDK 11 uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' cache: maven - name: Cache Maven dependencies uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: OWASP Dependency Check run: | mvn org.owasp:dependency-check-maven:check -DfailBuildOnAnyVulnerability=true continue-on-error: false - name: Generate SBOM run: | mvn org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom cat target/sbom.json - name: SpotBugs Security Scan run: | mvn com.github.spotbugs:spotbugs-maven-plugin:check continue-on-error: true - name: PMD Static Analysis run: | mvn org.apache.maven.plugins:maven-pmd-plugin:check continue-on-error: true - name: Install Serverless Framework run: | npm install -g serverless@3 npm install -g serverless-dependency-check serverless plugin install -n serverless-prune-plugin - name: Serverless Configuration Audit run: | npx serverless-scan --config serverless.yml --output-format json > serverless-scan-report.json cat serverless-scan-report.json - name: CFN-NAG Security Scan run: | gem install cfn-nag serverless package cfn_nag_scan --input-path .serverless/cloudformation-template-update-stack.json \ --blacklist-path cfn-nag-rules.yml \ --output-format json > cfn-nag-report.json - name: Upload Security Reports uses: actions/upload-artifact@v3 with: name: security-reports path: | dependency-check-report.html target/spotbugs.xml target/pmd.xml serverless-scan-report.json cfn-nag-report.json retention-days: 30 - name: Run Trivy for Container Scanning uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy Results uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' deploy-security-scanner: runs-on: ubuntu-latest needs: security-scan if: github.ref == 'refs/heads/main' steps: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - name: Deploy Security Scanner Lambda run: | mvn clean package serverless deploy --stage prod --verbose 6. Runtime Security Monitoring
Security Monitoring Lambda
package com.example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import software.amazon.awssdk.services.securityhub.SecurityHubClient; import software.amazon.awssdk.services.securityhub.model.*; import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest; import java.time.Instant; import java.util.List; public class SecurityScanner implements RequestHandler<ScheduledEvent, String> { private final SecurityHubClient securityHub; private final CloudWatchClient cloudWatch; public SecurityScanner() { this.securityHub = SecurityHubClient.create(); this.cloudWatch = CloudWatchClient.create(); } @Override public String handleRequest(ScheduledEvent event, Context context) { try { // Scan for vulnerabilities scanDependencies(); // Check configuration compliance checkInfrastructureCompliance(); // Monitor runtime security monitorRuntimeMetrics(); return "Security scan completed successfully"; } catch (Exception e) { context.getLogger().log("Security scan failed: " + e.getMessage()); throw new RuntimeException("Security scan failed", e); } } private void scanDependencies() { // Integrate with dependency scanning tools // This could call OWASP DC API or similar services // Report findings to Security Hub List<String> vulnerabilities = List.of("CVE-2023-12345", "CVE-2023-67890"); for (String cve : vulnerabilities) { AwsSecurityFinding finding = AwsSecurityFinding.builder() .title("Vulnerable Dependency Found") .description("Dependency with " + cve + " found in Lambda function") .severity(Severity.builder().label(SeverityLabel.HIGH).build()) .productArn("arn:aws:securityhub:us-east-1:123456789012:product/123456789012/default") .generatorId("java-serverless-scanner") .awsAccountId("123456789012") .schemaVersion("2018-10-08") .types(List.of("Software and Configuration Checks/Vulnerabilities/CVE")) .createdAt(Instant.now()) .updatedAt(Instant.now()) .build(); BatchImportFindingsRequest request = BatchImportFindingsRequest.builder() .findings(finding) .build(); securityHub.batchImportFindings(request); } } private void checkInfrastructureCompliance() { // Check Lambda function configurations // Verify encryption, networking, permissions etc. PutMetricDataRequest metricRequest = PutMetricDataRequest.builder() .namespace("ServerlessSecurity") .metricData(metric -> metric .metricName("ComplianceScore") .value(95.0) .timestamp(Instant.now())) .build(); cloudWatch.putMetricData(metricRequest); } private void monitorRuntimeMetrics() { // Monitor runtime security metrics // - Function invocation patterns // - Error rates // - Resource usage // - Network activity context.getLogger().log("Runtime security monitoring completed"); } } 7. Security Reporting and Dashboards
Security Dashboard Configuration
package com.example; import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; import software.amazon.awssdk.services.cloudwatch.model.DashboardValidationMessage; import software.amazon.awssdk.services.cloudwatch.model.PutDashboardRequest; public class SecurityDashboard { private final CloudWatchClient cloudWatch; public SecurityDashboard() { this.cloudWatch = CloudWatchClient.create(); } public void createSecurityDashboard() { String dashboardJson = """ { "widgets": [ { "type": "metric", "x": 0, "y": 0, "width": 12, "height": 6, "properties": { "metrics": [ [ "ServerlessSecurity", "VulnerabilityCount" ], [ ".", "CriticalVulnerabilities" ], [ ".", "HighVulnerabilities" ] ], "period": 300, "stat": "Maximum", "region": "us-east-1", "title": "Security Vulnerabilities" } }, { "type": "metric", "x": 0, "y": 6, "width": 12, "height": 6, "properties": { "metrics": [ [ "ServerlessSecurity", "ComplianceScore" ] ], "period": 300, "stat": "Average", "region": "us-east-1", "title": "Security Compliance Score" } } ] } """; PutDashboardRequest request = PutDashboardRequest.builder() .dashboardName("ServerlessJavaSecurity") .dashboardBody(dashboardJson) .build(); try { cloudWatch.putDashboard(request); System.out.println("Security dashboard created successfully"); } catch (Exception e) { System.err.println("Failed to create dashboard: " + e.getMessage()); } } } This comprehensive security scanning setup for Serverless Java applications covers dependency scanning, code analysis, infrastructure security, runtime monitoring, and CI/CD integration, providing end-to-end security assurance for your serverless workloads.