feat: expose run id in STS client user-agent
Closes #483. This commit modifies the user-agent string so that it includes the GITHUB_RUN_ID and the GITHUB_RUN_ATTEMPT, in the format typically used by the SDK. User agent strings are logged to CloudTrail, allowing users to correlate CloudTrail events with GHA runs. We took this approach instead of logging the ACCESS_KEY_ID as suggested in the issue to avoid logging sensitive information.
This commit is contained in:
parent
ef734cca81
commit
f8d102ad7d
5 changed files with 8552 additions and 13618 deletions
13
THIRD-PARTY
13
THIRD-PARTY
|
|
@ -431,12 +431,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The following npm packages may be included in this product:
|
The following npm package may be included in this product:
|
||||||
|
|
||||||
- @aws-crypto/crc32@5.2.0
|
|
||||||
- @aws-crypto/util@5.2.0
|
- @aws-crypto/util@5.2.0
|
||||||
|
|
||||||
These packages each contain the following license:
|
This package contains the following license:
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
|
|
@ -644,7 +643,7 @@ Apache License
|
||||||
|
|
||||||
The following npm packages may be included in this product:
|
The following npm packages may be included in this product:
|
||||||
|
|
||||||
- @aws-sdk/client-sts@3.1045.0
|
- @aws-sdk/client-sts@3.1044.0
|
||||||
- @aws-sdk/util-user-agent-browser@3.972.10
|
- @aws-sdk/util-user-agent-browser@3.972.10
|
||||||
- @aws-sdk/util-user-agent-node@3.973.24
|
- @aws-sdk/util-user-agent-node@3.973.24
|
||||||
- @smithy/middleware-retry@4.5.7
|
- @smithy/middleware-retry@4.5.7
|
||||||
|
|
@ -868,7 +867,7 @@ The following npm packages may be included in this product:
|
||||||
- @aws-sdk/middleware-sdk-s3@3.972.37
|
- @aws-sdk/middleware-sdk-s3@3.972.37
|
||||||
- @aws-sdk/middleware-user-agent@3.972.38
|
- @aws-sdk/middleware-user-agent@3.972.38
|
||||||
- @aws-sdk/signature-v4-multi-region@3.996.25
|
- @aws-sdk/signature-v4-multi-region@3.996.25
|
||||||
- @smithy/core@3.24.1
|
- @smithy/core@3.23.17
|
||||||
- @smithy/invalid-dependency@4.2.14
|
- @smithy/invalid-dependency@4.2.14
|
||||||
- @smithy/middleware-serde@4.2.20
|
- @smithy/middleware-serde@4.2.20
|
||||||
- @smithy/protocol-http@5.3.14
|
- @smithy/protocol-http@5.3.14
|
||||||
|
|
@ -1710,8 +1709,8 @@ The following npm packages may be included in this product:
|
||||||
- @smithy/middleware-content-length@4.2.14
|
- @smithy/middleware-content-length@4.2.14
|
||||||
- @smithy/middleware-endpoint@4.4.32
|
- @smithy/middleware-endpoint@4.4.32
|
||||||
- @smithy/middleware-stack@4.2.14
|
- @smithy/middleware-stack@4.2.14
|
||||||
- @smithy/node-http-handler@4.7.1
|
- @smithy/node-http-handler@4.6.1
|
||||||
- @smithy/property-provider@4.3.1
|
- @smithy/property-provider@4.2.14
|
||||||
- @smithy/shared-ini-file-loader@4.4.9
|
- @smithy/shared-ini-file-loader@4.4.9
|
||||||
- @smithy/signature-v4@5.3.14
|
- @smithy/signature-v4@5.3.14
|
||||||
- @smithy/util-base64@4.3.2
|
- @smithy/util-base64@4.3.2
|
||||||
|
|
|
||||||
16172
dist/index.js
generated
vendored
16172
dist/index.js
generated
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -3,10 +3,12 @@ import { STSClient } from '@aws-sdk/client-sts';
|
||||||
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
||||||
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
import { NodeHttpHandler } from '@smithy/node-http-handler';
|
||||||
import { ProxyAgent } from 'proxy-agent';
|
import { ProxyAgent } from 'proxy-agent';
|
||||||
import { errorMessage, getCallerIdentity } from './helpers';
|
import { buildCustomUserAgent, errorMessage, getCallerIdentity } from './helpers';
|
||||||
import { ProxyResolver } from './ProxyResolver';
|
import { ProxyResolver } from './ProxyResolver';
|
||||||
|
|
||||||
const USER_AGENT = 'configure-aws-credentials-for-github-actions';
|
if (!process.env.AWS_EXECUTION_ENV) {
|
||||||
|
process.env.AWS_EXECUTION_ENV = 'GitHubActions';
|
||||||
|
}
|
||||||
|
|
||||||
export interface CredentialsClientProps {
|
export interface CredentialsClientProps {
|
||||||
region?: string;
|
region?: string;
|
||||||
|
|
@ -51,16 +53,12 @@ export class CredentialsClient {
|
||||||
|
|
||||||
public get stsClient(): STSClient {
|
public get stsClient(): STSClient {
|
||||||
if (!this._stsClient || this.roleChaining) {
|
if (!this._stsClient || this.roleChaining) {
|
||||||
const config = { customUserAgent: USER_AGENT } as {
|
this._stsClient = new STSClient({
|
||||||
customUserAgent: string;
|
customUserAgent: buildCustomUserAgent(),
|
||||||
region?: string;
|
...(this.region !== undefined && { region: this.region }),
|
||||||
endpoint?: string;
|
...(this.stsEndpoint !== undefined && { endpoint: this.stsEndpoint }),
|
||||||
requestHandler?: NodeHttpHandler;
|
...(this.requestHandler !== undefined && { requestHandler: this.requestHandler }),
|
||||||
};
|
});
|
||||||
if (this.region !== undefined) config.region = this.region;
|
|
||||||
if (this.stsEndpoint !== undefined) config.endpoint = this.stsEndpoint;
|
|
||||||
if (this.requestHandler !== undefined) config.requestHandler = this.requestHandler;
|
|
||||||
this._stsClient = new STSClient(config);
|
|
||||||
}
|
}
|
||||||
return this._stsClient;
|
return this._stsClient;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,36 @@
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import type { Credentials, STSClient } from '@aws-sdk/client-sts';
|
import type { Credentials, STSClient } from '@aws-sdk/client-sts';
|
||||||
import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';
|
import { GetCallerIdentityCommand } from '@aws-sdk/client-sts';
|
||||||
|
import type { UserAgent } from '@smithy/types';
|
||||||
import type { CredentialsClient } from './CredentialsClient';
|
import type { CredentialsClient } from './CredentialsClient';
|
||||||
|
|
||||||
const MAX_TAG_VALUE_LENGTH = 256;
|
const MAX_TAG_VALUE_LENGTH = 256;
|
||||||
const SANITIZATION_CHARACTER = '_';
|
const SANITIZATION_CHARACTER = '_';
|
||||||
const SPECIAL_CHARS_REGEX = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]+/;
|
const SPECIAL_CHARS_REGEX = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]+/;
|
||||||
|
const USER_AGENT_PREFIX = 'configure-aws-credentials-for-github-actions';
|
||||||
|
const RUN_ID_PATTERN = /^[0-9]{1,20}$/;
|
||||||
|
const ATTEMPT_PATTERN = /^[0-9]{1,10}$/;
|
||||||
|
|
||||||
|
export function buildCustomUserAgent(): UserAgent {
|
||||||
|
const tokens: UserAgent = [[USER_AGENT_PREFIX]];
|
||||||
|
const runId = process.env.GITHUB_RUN_ID;
|
||||||
|
const attempt = process.env.GITHUB_RUN_ATTEMPT;
|
||||||
|
if (runId !== undefined) {
|
||||||
|
if (RUN_ID_PATTERN.test(runId)) {
|
||||||
|
tokens.push(['md', `run_id#${runId}`]);
|
||||||
|
} else {
|
||||||
|
core.warning('GITHUB_RUN_ID has unexpected format; omitting from User-Agent');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attempt !== undefined) {
|
||||||
|
if (ATTEMPT_PATTERN.test(attempt)) {
|
||||||
|
tokens.push(['md', `attempt#${attempt}`]);
|
||||||
|
} else {
|
||||||
|
core.warning('GITHUB_RUN_ATTEMPT has unexpected format; omitting from User-Agent');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
export function translateEnvVariables() {
|
export function translateEnvVariables() {
|
||||||
const envVars = [
|
const envVars = [
|
||||||
|
|
|
||||||
|
|
@ -1258,4 +1258,70 @@ describe('Configure AWS Credentials', {}, () => {
|
||||||
expect(core.setFailed).not.toHaveBeenCalled();
|
expect(core.setFailed).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('User-Agent enrichment', {}, () => {
|
||||||
|
async function getCustomUserAgent(): Promise<unknown> {
|
||||||
|
const { CredentialsClient: FreshClient } = await import('../src/CredentialsClient');
|
||||||
|
const client = new FreshClient({ region: 'fake-region-1', roleChaining: false });
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: SDK config readout
|
||||||
|
return (client.stsClient.config as any).customUserAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('includes run_id and attempt tokens when env vars are valid', async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
process.env.GITHUB_RUN_ID = '16412345678';
|
||||||
|
process.env.GITHUB_RUN_ATTEMPT = '1';
|
||||||
|
const ua = await getCustomUserAgent();
|
||||||
|
expect(ua).toEqual([
|
||||||
|
['configure-aws-credentials-for-github-actions'],
|
||||||
|
['md', 'run_id#16412345678'],
|
||||||
|
['md', 'attempt#1'],
|
||||||
|
]);
|
||||||
|
expect(core.warning).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits tokens when env vars are unset, with no warning', async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
const ua = await getCustomUserAgent();
|
||||||
|
expect(ua).toEqual([['configure-aws-credentials-for-github-actions']]);
|
||||||
|
expect(core.warning).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('warns and skips when env vars are malformed', async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
process.env.GITHUB_RUN_ID = '$(curl evil)';
|
||||||
|
process.env.GITHUB_RUN_ATTEMPT = '1; rm -rf /';
|
||||||
|
const ua = await getCustomUserAgent();
|
||||||
|
expect(ua).toEqual([['configure-aws-credentials-for-github-actions']]);
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
'GITHUB_RUN_ID has unexpected format; omitting from User-Agent',
|
||||||
|
);
|
||||||
|
expect(core.warning).toHaveBeenCalledWith(
|
||||||
|
'GITHUB_RUN_ATTEMPT has unexpected format; omitting from User-Agent',
|
||||||
|
);
|
||||||
|
expect(core.warning).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('warns and skips when env vars exceed the length bound', async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
process.env.GITHUB_RUN_ID = '1'.repeat(50);
|
||||||
|
process.env.GITHUB_RUN_ATTEMPT = '1'.repeat(50);
|
||||||
|
const ua = await getCustomUserAgent();
|
||||||
|
expect(ua).toEqual([['configure-aws-credentials-for-github-actions']]);
|
||||||
|
expect(core.warning).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets AWS_EXECUTION_ENV to GitHubActions when unset', async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
await import('../src/CredentialsClient');
|
||||||
|
expect(process.env.AWS_EXECUTION_ENV).toBe('GitHubActions');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves a pre-existing AWS_EXECUTION_ENV value', async () => {
|
||||||
|
vi.resetModules();
|
||||||
|
process.env.AWS_EXECUTION_ENV = 'CustomRunner';
|
||||||
|
await import('../src/CredentialsClient');
|
||||||
|
expect(process.env.AWS_EXECUTION_ENV).toBe('CustomRunner');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue