@@ -7,6 +7,12 @@ import {
77} from '@aws-sdk/client-securityhub' ;
88import type { SecurityFinding } from '../cloud-security.service' ;
99
10+ type AwsCredentials = {
11+ accessKeyId : string ;
12+ secretAccessKey : string ;
13+ sessionToken ?: string ;
14+ } ;
15+
1016@Injectable ( )
1117export class AWSSecurityService {
1218 private readonly logger = new Logger ( AWSSecurityService . name ) ;
@@ -15,10 +21,10 @@ export class AWSSecurityService {
1521 credentials : Record < string , unknown > ,
1622 variables : Record < string , unknown > ,
1723 ) : Promise < SecurityFinding [ ] > {
18- // Determine auth method
19- const isRoleAuth = credentials . roleArn && credentials . externalId ;
20- const isKeyAuth =
21- credentials . access_key_id && credentials . secret_access_key ;
24+ const isRoleAuth = Boolean ( credentials . roleArn && credentials . externalId ) ;
25+ const isKeyAuth = Boolean (
26+ credentials . access_key_id && credentials . secret_access_key ,
27+ ) ;
2228
2329 if ( ! isRoleAuth && ! isKeyAuth ) {
2430 throw new Error (
@@ -31,54 +37,85 @@ export class AWSSecurityService {
3137 ( variables . region as string ) ||
3238 'us-east-1' ;
3339
34- let awsCredentials : {
35- accessKeyId : string ;
36- secretAccessKey : string ;
37- sessionToken ?: string ;
38- } ;
40+ let awsCredentials : AwsCredentials ;
3941
4042 if ( isRoleAuth ) {
41- // IAM Role assumption
42- const roleArn = credentials . roleArn as string ;
43+ const customerRoleArn = credentials . roleArn as string ;
4344 const externalId = credentials . externalId as string ;
4445
45- this . logger . log ( `Assuming role ${ roleArn } in region ${ region } ` ) ;
46+ const roleAssumerArn = process . env . SECURITY_HUB_ROLE_ASSUMER_ARN ;
47+ if ( ! roleAssumerArn ) {
48+ throw new Error (
49+ 'Missing SECURITY_HUB_ROLE_ASSUMER_ARN (our roleAssumer ARN).' ,
50+ ) ;
51+ }
52+
53+ // Hop 1: task role -> roleAssumer
54+ const baseSts = new STSClient ( { region } ) ;
55+ const roleAssumerResp = await baseSts . send (
56+ new AssumeRoleCommand ( {
57+ RoleArn : roleAssumerArn ,
58+ RoleSessionName : 'CompRoleAssumer' ,
59+ DurationSeconds : 3600 ,
60+ } ) ,
61+ ) ;
62+
63+ const roleAssumerCreds = roleAssumerResp . Credentials ;
64+ if ( ! roleAssumerCreds ?. AccessKeyId || ! roleAssumerCreds . SecretAccessKey ) {
65+ throw new Error (
66+ 'Failed to assume roleAssumer - no credentials returned' ,
67+ ) ;
68+ }
69+
70+ const roleAssumerAwsCreds : AwsCredentials = {
71+ accessKeyId : roleAssumerCreds . AccessKeyId ,
72+ secretAccessKey : roleAssumerCreds . SecretAccessKey ,
73+ sessionToken : roleAssumerCreds . SessionToken ,
74+ } ;
75+
76+ // Hop 2: roleAssumer -> customer role (ExternalId enforced by customer trust policy)
77+ const roleAssumerSts = new STSClient ( {
78+ region,
79+ credentials : roleAssumerAwsCreds ,
80+ } ) ;
81+
82+ this . logger . log (
83+ `Assuming customer role ${ customerRoleArn } in region ${ region } ` ,
84+ ) ;
4685
47- const sts = new STSClient ( { region } ) ;
48- const assumeRoleResponse = await sts . send (
86+ const customerResp = await roleAssumerSts . send (
4987 new AssumeRoleCommand ( {
50- RoleArn : roleArn ,
88+ RoleArn : customerRoleArn ,
5189 ExternalId : externalId ,
5290 RoleSessionName : 'CompSecurityAudit' ,
5391 DurationSeconds : 3600 ,
5492 } ) ,
5593 ) ;
5694
57- if ( ! assumeRoleResponse . Credentials ) {
58- throw new Error ( 'Failed to assume role - no credentials returned' ) ;
95+ const customerCreds = customerResp . Credentials ;
96+ if ( ! customerCreds ?. AccessKeyId || ! customerCreds . SecretAccessKey ) {
97+ throw new Error (
98+ 'Failed to assume customer role - no credentials returned' ,
99+ ) ;
59100 }
60101
61102 awsCredentials = {
62- accessKeyId : assumeRoleResponse . Credentials . AccessKeyId ! ,
63- secretAccessKey : assumeRoleResponse . Credentials . SecretAccessKey ! ,
64- sessionToken : assumeRoleResponse . Credentials . SessionToken ! ,
103+ accessKeyId : customerCreds . AccessKeyId ,
104+ secretAccessKey : customerCreds . SecretAccessKey ,
105+ sessionToken : customerCreds . SessionToken ,
65106 } ;
66107 } else {
67- // Direct access keys
68108 awsCredentials = {
69109 accessKeyId : credentials . access_key_id as string ,
70110 secretAccessKey : credentials . secret_access_key as string ,
71111 } ;
72112 }
73113
74- // Create Security Hub client
75114 const securityHub = new SecurityHubClient ( {
76115 region,
77116 credentials : awsCredentials ,
78117 } ) ;
79118
80- this . logger . log ( `Scanning AWS Security Hub in region ${ region } ` ) ;
81-
82119 try {
83120 const findings = await this . fetchSecurityHubFindings ( securityHub ) ;
84121 this . logger . log ( `Found ${ findings . length } AWS security findings` ) ;
0 commit comments