🧱 fix: Validate Bedrock User Credentials (#13277)

This commit is contained in:
Danny Avila 2026-05-23 16:46:15 -04:00 committed by GitHub
parent 7cd467a528
commit af902118c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 104 additions and 15 deletions

View file

@ -483,6 +483,26 @@ describe('initializeBedrock', () => {
expect(result.llmConfig).not.toHaveProperty('credentials');
});
it('should reject a non-string user-provided Bedrock API key', async () => {
process.env.BEDROCK_AWS_BEARER_TOKEN = AuthType.USER_PROVIDED;
process.env.BEDROCK_AWS_ACCESS_KEY_ID = AuthType.USER_PROVIDED;
process.env.BEDROCK_AWS_SECRET_ACCESS_KEY = AuthType.USER_PROVIDED;
const params = createMockParams();
(params.db.getUserKey as jest.Mock).mockResolvedValue(
JSON.stringify({
apiKey: JSON.stringify({
bearerToken: {},
accessKeyId: 'user-access-key',
secretAccessKey: 'user-secret-key',
}),
}),
);
await expect(initializeBedrock(params)).rejects.toThrow(
'Bedrock credentials not provided. Please provide them again.',
);
});
it('should not use stored access keys when only bearer token mode is configured', async () => {
process.env.BEDROCK_AWS_BEARER_TOKEN = AuthType.USER_PROVIDED;
const params = createMockParams();
@ -513,6 +533,24 @@ describe('initializeBedrock', () => {
});
expect(result.llmConfig).not.toHaveProperty('client');
});
it('should reject non-string user-provided access key values', async () => {
process.env.BEDROCK_AWS_ACCESS_KEY_ID = AuthType.USER_PROVIDED;
process.env.BEDROCK_AWS_SECRET_ACCESS_KEY = AuthType.USER_PROVIDED;
const params = createMockParams();
(params.db.getUserKey as jest.Mock).mockResolvedValue(
JSON.stringify({
apiKey: JSON.stringify({
accessKeyId: {},
secretAccessKey: 'user-secret-key',
}),
}),
);
await expect(initializeBedrock(params)).rejects.toThrow(
'Bedrock credentials not provided. Please provide them again.',
);
});
});
describe('Credentials Edge Cases', () => {

View file

@ -15,12 +15,61 @@ import type {
BaseInitializeParams,
InitializeResultBase,
BedrockCredentials,
BedrockUserCredentials,
GuardrailConfiguration,
InferenceProfileConfig,
} from '~/types';
import { checkUserKeyExpiry } from '~/utils';
const BEDROCK_CREDENTIALS_ERROR = 'Bedrock credentials not provided. Please provide them again.';
type UserCredentialKey = 'accessKeyId' | 'secretAccessKey' | 'sessionToken' | 'bearerToken';
type UserCredentialValue = string | number | boolean | object | null;
type ParsedBedrockUserCredentials = Partial<Record<UserCredentialKey, UserCredentialValue>> & {
apiKey?: string;
};
function isParsedBedrockUserCredentials(value: unknown): value is ParsedBedrockUserCredentials {
return value != null && typeof value === 'object' && !Array.isArray(value);
}
function parseBedrockUserCredentials(userKey: string): ParsedBedrockUserCredentials {
const storedCredentials = JSON.parse(userKey) as unknown;
if (!isParsedBedrockUserCredentials(storedCredentials)) {
throw new Error(BEDROCK_CREDENTIALS_ERROR);
}
if (typeof storedCredentials.apiKey !== 'string') {
return storedCredentials;
}
const nestedCredentials = JSON.parse(storedCredentials.apiKey) as unknown;
if (!isParsedBedrockUserCredentials(nestedCredentials)) {
throw new Error(BEDROCK_CREDENTIALS_ERROR);
}
return nestedCredentials;
}
function getUserCredentialValue(
credentials: ParsedBedrockUserCredentials,
key: UserCredentialKey,
): string | undefined {
if (!Object.prototype.hasOwnProperty.call(credentials, key)) {
return undefined;
}
const value = credentials[key];
if (value === '') {
return undefined;
}
if (typeof value !== 'string') {
throw new Error(BEDROCK_CREDENTIALS_ERROR);
}
return value;
}
/**
* Initializes Bedrock endpoint configuration.
*
@ -101,35 +150,37 @@ export async function initializeBedrock({
});
if (!userKey) {
throw new Error('Bedrock credentials not provided. Please provide them again.');
throw new Error(BEDROCK_CREDENTIALS_ERROR);
}
let userCredentials: BedrockUserCredentials;
let userCredentials: ParsedBedrockUserCredentials;
try {
const storedCredentials = JSON.parse(userKey) as BedrockUserCredentials & { apiKey?: string };
userCredentials =
typeof storedCredentials.apiKey === 'string'
? (JSON.parse(storedCredentials.apiKey) as BedrockUserCredentials)
: storedCredentials;
userCredentials = parseBedrockUserCredentials(userKey);
} catch {
throw new Error('Bedrock credentials not provided. Please provide them again.');
throw new Error(BEDROCK_CREDENTIALS_ERROR);
}
if (userProvidesBearerToken && userCredentials.bearerToken) {
bearerToken = userCredentials.bearerToken;
const userBearerToken = userProvidesBearerToken
? getUserCredentialValue(userCredentials, 'bearerToken')
: undefined;
if (userBearerToken) {
bearerToken = userBearerToken;
} else {
const canUseAccessKeys =
userProvidesAccessKeyId || userProvidesSecretAccessKey || userProvidesSessionToken;
const accessKeyId = userProvidesAccessKeyId ? userCredentials.accessKeyId : staticAccessKeyId;
const accessKeyId = userProvidesAccessKeyId
? getUserCredentialValue(userCredentials, 'accessKeyId')
: staticAccessKeyId;
const secretAccessKey = userProvidesSecretAccessKey
? userCredentials.secretAccessKey
? getUserCredentialValue(userCredentials, 'secretAccessKey')
: staticSecretAccessKey;
const sessionToken = userProvidesSessionToken
? userCredentials.sessionToken
? getUserCredentialValue(userCredentials, 'sessionToken')
: staticSessionToken;
if (!canUseAccessKeys || !accessKeyId || !secretAccessKey) {
throw new Error('Bedrock credentials not provided. Please provide them again.');
throw new Error(BEDROCK_CREDENTIALS_ERROR);
}
credentials = {