From 65715d74582c31b2daa3041f37f4437e2914d8c2 Mon Sep 17 00:00:00 2001 From: Tom Keller Date: Mon, 11 May 2026 13:37:52 -0700 Subject: [PATCH] feat: add regex validation to role-session-name Previously invalid role session names would get errors from the STS API instead of this action rejecting them, causing unnecessary retries. Now we check them and fail early. Closes #1656. That FR recommended that we sanitize the name before sending to STS, but instead we error to not silently change the user's selected session name (avoiding the potential security sharp edge) --- dist/index.js | 11 +++++++++++ src/index.ts | 12 ++++++++++++ test/index.test.ts | 21 +++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/dist/index.js b/dist/index.js index 5e77544..6d713b3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -72805,6 +72805,7 @@ function writeProfileFiles(profileName, credentials, region, overwriteAwsProfile var DEFAULT_ROLE_DURATION = 3600; var ROLE_SESSION_NAME = "GitHubActions"; var REGION_REGEX = /^[a-z0-9-]+$/g; +var ROLE_SESSION_NAME_REGEX = /^[\w+=,.@-]*$/; async function run() { try { translateEnvVariables(); @@ -72878,6 +72879,16 @@ async function run() { if (!region.match(REGION_REGEX)) { throw new Error(`Region is not valid: ${region}`); } + if (roleSessionName.length < 2 || roleSessionName.length > 64) { + throw new Error( + `Role session name must be between 2 and 64 characters, got ${roleSessionName.length}: '${roleSessionName}'` + ); + } + if (!roleSessionName.match(ROLE_SESSION_NAME_REGEX)) { + throw new Error( + `Role session name is not valid: '${roleSessionName}'. Must satisfy regular expression pattern: [\\w+=,.@-]*` + ); + } exportRegion(region, outputEnvCredentials); const clientProps = { region, diff --git a/src/index.ts b/src/index.ts index a53de0a..40d6ae3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,7 @@ import { writeProfileFiles } from './profileManager'; const DEFAULT_ROLE_DURATION = 3600; // One hour (seconds) const ROLE_SESSION_NAME = 'GitHubActions'; const REGION_REGEX = /^[a-z0-9-]+$/g; +const ROLE_SESSION_NAME_REGEX = /^[\w+=,.@-]*$/; export async function run() { try { @@ -129,6 +130,17 @@ export async function run() { throw new Error(`Region is not valid: ${region}`); } + if (roleSessionName.length < 2 || roleSessionName.length > 64) { + throw new Error( + `Role session name must be between 2 and 64 characters, got ${roleSessionName.length}: '${roleSessionName}'`, + ); + } + if (!roleSessionName.match(ROLE_SESSION_NAME_REGEX)) { + throw new Error( + `Role session name is not valid: '${roleSessionName}'. Must satisfy regular expression pattern: [\\w+=,.@-]*`, + ); + } + exportRegion(region, outputEnvCredentials); // Instantiate credentials client diff --git a/test/index.test.ts b/test/index.test.ts index bdccc1f..9c351d5 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -351,6 +351,27 @@ describe('Configure AWS Credentials', {}, () => { await run(); expect(core.setFailed).toHaveBeenCalled(); }); + it('fails with a role-session-name containing invalid characters', {}, async () => { + vi.mocked(core.getInput).mockImplementation( + mocks.getInput({ ...mocks.IAM_ASSUMEROLE_INPUTS, 'role-session-name': 'invalid session!' }), + ); + await run(); + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('Role session name is not valid')); + }); + it('fails with a role-session-name that is too short', {}, async () => { + vi.mocked(core.getInput).mockImplementation( + mocks.getInput({ ...mocks.IAM_ASSUMEROLE_INPUTS, 'role-session-name': 'a' }), + ); + await run(); + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('must be between 2 and 64 characters')); + }); + it('fails with a role-session-name that is too long', {}, async () => { + vi.mocked(core.getInput).mockImplementation( + mocks.getInput({ ...mocks.IAM_ASSUMEROLE_INPUTS, 'role-session-name': 'a'.repeat(65) }), + ); + await run(); + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('must be between 2 and 64 characters')); + }); it('fails if access key id is provided without secret access key', {}, async () => { vi.mocked(core.getInput).mockImplementation( mocks.getInput({ ...mocks.IAM_USER_INPUTS, 'aws-secret-access-key': '' }),