feat: depenency update and feature cleanup (#1414)

* chore: add clean script

* chore: run npm audit fix

* chore: unblock update to vitest 3.x

* chore(deps-dev): update vitest dependencies

Closes #1275
Closes #1276

* chore(deps): update https-proxy-agent and types to match

Closes #1407.

* chore: add lint:fix script

* feat: support HTTPS_PROXY environment variable

Closes #1061
Closes #861

* feat: output ARN of authenticated prinicpal

Closes #1062
Closes #1191
This commit is contained in:
Tom Keller
2025-08-04 10:39:42 -07:00
committed by kellertk
parent be798e597e
commit 87ec0e2f9a
6 changed files with 701 additions and 484 deletions
+600 -467
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -5,8 +5,10 @@
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"lint": "biome check --error-on-warnings ./src", "lint": "biome check --error-on-warnings ./src",
"lint:fix": "biome check --write ./src",
"package": "npm run build && ncc build --license THIRD-PARTY -o dist && ncc build src/cleanup/index.ts -o dist/cleanup && cpy dist/THIRD-PARTY . && del-cli dist/THIRD-PARTY", "package": "npm run build && ncc build --license THIRD-PARTY -o dist && ncc build src/cleanup/index.ts -o dist/cleanup && cpy dist/THIRD-PARTY . && del-cli dist/THIRD-PARTY",
"test": "npm run lint && vitest run && npm run build" "test": "npm run lint && vitest run && npm run build",
"clean": "del-cli coverage test-reports node_modules"
}, },
"author": { "author": {
"name": "Amazon.com, Inc. or its affiliates", "name": "Amazon.com, Inc. or its affiliates",
@@ -17,9 +19,9 @@
"@aws-sdk/credential-provider-env": "^3.858.0", "@aws-sdk/credential-provider-env": "^3.858.0",
"@biomejs/biome": "2.1.3", "@biomejs/biome": "2.1.3",
"@smithy/property-provider": "^4.0.3", "@smithy/property-provider": "^4.0.3",
"@types/node": "^24.0.3", "@types/node": "^24.1.0",
"@vercel/ncc": "^0.38.3", "@vercel/ncc": "^0.38.3",
"@vitest/coverage-v8": "^3.1.2", "@vitest/coverage-v8": "^3.2.4",
"aws-sdk-client-mock": "^4.1.0", "aws-sdk-client-mock": "^4.1.0",
"cpy-cli": "^5.0.0", "cpy-cli": "^5.0.0",
"del-cli": "^6.0.0", "del-cli": "^6.0.0",
@@ -27,13 +29,13 @@
"memfs": "^4.25.1", "memfs": "^4.25.1",
"standard-version": "^9.5.0", "standard-version": "^9.5.0",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vitest": "^3.1.2" "vitest": "^3.2.4"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.11.1", "@actions/core": "^1.11.1",
"@aws-sdk/client-sts": "^3.858.0", "@aws-sdk/client-sts": "^3.858.0",
"@smithy/node-http-handler": "^4.1.0", "@smithy/node-http-handler": "^4.1.0",
"https-proxy-agent": "^5.0.1" "https-proxy-agent": "^7.0.6"
}, },
"keywords": [ "keywords": [
"aws", "aws",
+7 -2
View File
@@ -29,6 +29,8 @@ export function translateEnvVariables() {
'SPECIAL_CHARACTERS_WORKAROUND', 'SPECIAL_CHARACTERS_WORKAROUND',
'USE_EXISTING_CREDENTIALS', 'USE_EXISTING_CREDENTIALS',
]; ];
// Treat HTTPS_PROXY as HTTP_PROXY. Precedence is HTTPS_PROXY > HTTP_PROXY
process.env.HTTP_PROXY = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || undefined;
for (const envVar of envVars) { for (const envVar of envVars) {
if (process.env[envVar]) { if (process.env[envVar]) {
const inputKey = `INPUT_${envVar.replace(/_/g, '-')}`; const inputKey = `INPUT_${envVar.replace(/_/g, '-')}`;
@@ -111,13 +113,16 @@ export async function exportAccountId(credentialsClient: CredentialsClient, mask
const client = credentialsClient.stsClient; const client = credentialsClient.stsClient;
const identity = await client.send(new GetCallerIdentityCommand({})); const identity = await client.send(new GetCallerIdentityCommand({}));
const accountId = identity.Account; const accountId = identity.Account;
if (!accountId) { const arn = identity.Arn;
throw new Error('Could not get Account ID from STS. Did you set credentials?'); if (!accountId || !arn) {
throw new Error('Could not get Account ID or ARN from STS. Did you set credentials?');
} }
if (maskAccountId) { if (maskAccountId) {
core.setSecret(accountId); core.setSecret(accountId);
core.setSecret(arn);
} }
core.setOutput('aws-account-id', accountId); core.setOutput('aws-account-id', accountId);
core.setOutput('authenticated-arn', arn);
return accountId; return accountId;
} }
+1 -1
View File
@@ -47,7 +47,7 @@ export async function run() {
const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false'; const roleSkipSessionTaggingInput = core.getInput('role-skip-session-tagging', { required: false }) || 'false';
const roleSkipSessionTagging = roleSkipSessionTaggingInput.toLowerCase() === 'true'; const roleSkipSessionTagging = roleSkipSessionTaggingInput.toLowerCase() === 'true';
const transitiveTagKeys = core.getMultilineInput('transitive-tag-keys', { required: false }); const transitiveTagKeys = core.getMultilineInput('transitive-tag-keys', { required: false });
const proxyServer = core.getInput('http-proxy', { required: false }); const proxyServer = core.getInput('http-proxy', { required: false }) || process.env.HTTP_PROXY;
const inlineSessionPolicy = core.getInput('inline-session-policy', { const inlineSessionPolicy = core.getInput('inline-session-policy', {
required: false, required: false,
}); });
+2 -2
View File
@@ -11,9 +11,9 @@ describe('Configure AWS Credentials helpers', {}, () => {
const actor = 'actor[bot]'; const actor = 'actor[bot]';
expect(helpers.sanitizeGitHubVariables(actor)).toBe('actor_bot_'); expect(helpers.sanitizeGitHubVariables(actor)).toBe('actor_bot_');
}); });
it('can sleep', {}, () => { it('can sleep', {}, async () => {
const sleep = helpers.defaultSleep(10); const sleep = helpers.defaultSleep(10);
expect(Promise.race([sleep, new Promise((_, reject) => setTimeout(reject, 20))])).resolves.toBe(undefined); await expect(Promise.race([sleep, new Promise((_, reject) => setTimeout(reject, 20))])).resolves.toBe(undefined);
}); });
it('removes special characters from workflow names', {}, () => { it('removes special characters from workflow names', {}, () => {
expect(helpers.sanitizeGitHubVariables('sdf234@#$%$^&*()_+{}|:"<>?')).toEqual('sdf234@__________+___:____'); expect(helpers.sanitizeGitHubVariables('sdf234@#$%$^&*()_+{}|:"<>?')).toEqual('sdf234@__________+___:____');
+84 -7
View File
@@ -47,7 +47,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID'); expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID');
expect(core.info).toHaveBeenCalledTimes(2); expect(core.info).toHaveBeenCalledTimes(2);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledOnce(); expect(core.setOutput).toHaveBeenCalledTimes(2);
expect(core.setSecret).toHaveBeenCalledWith('STSAWSACCESSKEYID'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSACCESSKEYID');
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSECRETACCESSKEY'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSECRETACCESSKEY');
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN');
@@ -71,7 +71,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID'); expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID');
expect(core.info).toHaveBeenCalledTimes(3); expect(core.info).toHaveBeenCalledTimes(3);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledOnce(); expect(core.setOutput).toHaveBeenCalledTimes(2);
expect(core.setSecret).toHaveBeenCalledWith('STSAWSACCESSKEYID'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSACCESSKEYID');
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSECRETACCESSKEY'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSECRETACCESSKEY');
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN');
@@ -106,7 +106,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.setSecret).toHaveBeenCalledWith('MYAWSSECRETACCESSKEY'); expect(core.setSecret).toHaveBeenCalledWith('MYAWSSECRETACCESSKEY');
expect(core.setSecret).toHaveBeenCalledTimes(2); expect(core.setSecret).toHaveBeenCalledTimes(2);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledOnce(); expect(core.setOutput).toHaveBeenCalledTimes(2);
expect(core.info).toHaveBeenCalledWith('Proceeding with IAM user credentials'); expect(core.info).toHaveBeenCalledWith('Proceeding with IAM user credentials');
expect(core.info).toHaveBeenCalledOnce(); expect(core.info).toHaveBeenCalledOnce();
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
@@ -140,7 +140,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.setSecret).toHaveBeenCalledWith('MYAWSSECRETACCESSKEY'); expect(core.setSecret).toHaveBeenCalledWith('MYAWSSECRETACCESSKEY');
expect(core.setSecret).toHaveBeenCalledTimes(5); expect(core.setSecret).toHaveBeenCalledTimes(5);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledTimes(2); expect(core.setOutput).toHaveBeenCalledTimes(4);
expect(core.info).toHaveBeenCalledWith('Assuming role with user credentials'); expect(core.info).toHaveBeenCalledWith('Assuming role with user credentials');
expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID'); expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID');
expect(core.info).toHaveBeenCalledTimes(2); expect(core.info).toHaveBeenCalledTimes(2);
@@ -173,7 +173,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN');
expect(core.setSecret).toHaveBeenCalledTimes(3); expect(core.setSecret).toHaveBeenCalledTimes(3);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledTimes(1); expect(core.setOutput).toHaveBeenCalledTimes(2);
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
}); });
@@ -204,7 +204,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN');
expect(core.setSecret).toHaveBeenCalledTimes(3); expect(core.setSecret).toHaveBeenCalledTimes(3);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledTimes(2); expect(core.setOutput).toHaveBeenCalledTimes(4);
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
it('exports environment variables from inputs', {}, async () => { it('exports environment variables from inputs', {}, async () => {
@@ -237,7 +237,7 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN'); expect(core.setSecret).toHaveBeenCalledWith('STSAWSSESSIONTOKEN');
expect(core.setSecret).toHaveBeenCalledTimes(6); expect(core.setSecret).toHaveBeenCalledTimes(6);
expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111'); expect(core.setOutput).toHaveBeenCalledWith('aws-account-id', '111111111111');
expect(core.setOutput).toHaveBeenCalledTimes(2); expect(core.setOutput).toHaveBeenCalledTimes(4);
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
}); });
@@ -333,4 +333,81 @@ describe('Configure AWS Credentials', {}, () => {
expect(core.setFailed).not.toHaveBeenCalled(); expect(core.setFailed).not.toHaveBeenCalled();
}); });
}); });
describe('HTTP Proxy Configuration', {}, () => {
beforeEach(() => {
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.GH_OIDC_INPUTS));
vi.spyOn(core, 'getIDToken').mockResolvedValue('testoidctoken');
mockedSTSClient.on(GetCallerIdentityCommand).resolvesOnce({ ...mocks.outputs.GET_CALLER_IDENTITY });
mockedSTSClient.on(AssumeRoleWithWebIdentityCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS);
process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN = 'fake-token';
});
it('configures proxy from http-proxy input', async () => {
const infoSpy = vi.spyOn(core, 'info');
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://proxy.example.com:8080'
})
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
it('configures proxy from HTTP_PROXY environment variable', async () => {
const infoSpy = vi.spyOn(core, 'info');
process.env.HTTP_PROXY = 'http://proxy.example.com:8080';
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
it('configures proxy from HTTPS_PROXY environment variable', async () => {
const infoSpy = vi.spyOn(core, 'info');
process.env.HTTPS_PROXY = 'https://proxy.example.com:8080';
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
it('prioritizes http-proxy input over environment variables', async () => {
const infoSpy = vi.spyOn(core, 'info');
process.env.HTTP_PROXY = 'http://env-proxy.example.com:8080';
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://input-proxy.example.com:8080'
})
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
it('properly configures proxy agent in STS client', async () => {
const infoSpy = vi.spyOn(core, 'info');
vi.spyOn(core, 'getInput').mockImplementation(
mocks.getInput({
...mocks.GH_OIDC_INPUTS,
'http-proxy': 'http://proxy.example.com:8080'
})
);
await run();
expect(infoSpy).toHaveBeenCalledWith('Configuring proxy handler for STS client');
expect(core.setFailed).not.toHaveBeenCalled();
});
});
}); });