fix(kiro): validate region to prevent SSRF (GHSA-6mwv-4mrm-5p3m)
Reject non-AWS region values before interpolating them into upstream URLs and stop reflecting upstream response bodies to the client. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -58,6 +58,10 @@ export async function POST(request) {
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Kiro API key import error:", error);
|
||||
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||
// Do not reflect upstream response body to the client (SSRF hardening)
|
||||
return NextResponse.json(
|
||||
{ error: "API key validation failed" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,17 @@ export const GITHUB_CONFIG = { ...PROVIDER_OAUTH["github"] };
|
||||
// Kiro OAuth Configuration (multi-method: AWS Builder ID / IDC / Social / Import Token)
|
||||
export const KIRO_CONFIG = { ...PROVIDER_OAUTH["kiro"] };
|
||||
|
||||
// AWS region allowlist pattern — prevents SSRF via region injection into upstream URLs (GHSA-6mwv-4mrm-5p3m)
|
||||
export const AWS_REGION_PATTERN = /^[a-z]{2}-[a-z]+-\d{1,2}$/;
|
||||
|
||||
// Reject any region that is not a valid AWS region before interpolating it into a URL
|
||||
export function assertValidAwsRegion(region) {
|
||||
if (typeof region !== "string" || !AWS_REGION_PATTERN.test(region)) {
|
||||
throw new Error("Invalid region");
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
// Cursor OAuth Configuration (Import Token from Cursor IDE)
|
||||
// tokenStoragePaths: user-reference only, not stored in registry
|
||||
export const CURSOR_CONFIG = {
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
ANTIGRAVITY_CONFIG,
|
||||
GITHUB_CONFIG,
|
||||
KIRO_CONFIG,
|
||||
assertValidAwsRegion,
|
||||
CURSOR_CONFIG,
|
||||
KIMI_CODING_CONFIG,
|
||||
KILOCODE_CONFIG,
|
||||
@@ -792,6 +793,7 @@ const PROVIDERS = {
|
||||
requestDeviceCode: async (config, codeChallenge, options = {}) => {
|
||||
const trimmedRegion = typeof options.region === "string" ? options.region.trim() : "";
|
||||
const region = trimmedRegion || "us-east-1";
|
||||
assertValidAwsRegion(region);
|
||||
const trimmedStartUrl = typeof options.startUrl === "string" ? options.startUrl.trim() : "";
|
||||
const startUrl = trimmedStartUrl || config.startUrl;
|
||||
const authMethod = options.authMethod === "idc" ? "idc" : "builder-id";
|
||||
@@ -860,6 +862,7 @@ const PROVIDERS = {
|
||||
},
|
||||
pollToken: async (config, deviceCode, codeVerifier, extraData) => {
|
||||
const region = extraData?._region || "us-east-1";
|
||||
assertValidAwsRegion(region);
|
||||
const tokenUrl = `https://oidc.${region}.amazonaws.com/token`;
|
||||
const response = await fetch(tokenUrl, {
|
||||
method: "POST",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { KIRO_CONFIG } from "../constants/oauth.js";
|
||||
import { KIRO_CONFIG, assertValidAwsRegion } from "../constants/oauth.js";
|
||||
|
||||
/**
|
||||
* Kiro OAuth Service
|
||||
@@ -17,6 +17,7 @@ export class KiroService {
|
||||
* Returns clientId and clientSecret for device code flow
|
||||
*/
|
||||
async registerClient(region = "us-east-1") {
|
||||
assertValidAwsRegion(region);
|
||||
const endpoint = `https://oidc.${region}.amazonaws.com/client/register`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
@@ -50,6 +51,7 @@ export class KiroService {
|
||||
* Start device authorization for AWS Builder ID or IDC
|
||||
*/
|
||||
async startDeviceAuthorization(clientId, clientSecret, startUrl, region = "us-east-1") {
|
||||
assertValidAwsRegion(region);
|
||||
const endpoint = `https://oidc.${region}.amazonaws.com/device_authorization`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
@@ -84,6 +86,7 @@ export class KiroService {
|
||||
* Poll for token using device code (AWS Builder ID/IDC)
|
||||
*/
|
||||
async pollDeviceToken(clientId, clientSecret, deviceCode, region = "us-east-1") {
|
||||
assertValidAwsRegion(region);
|
||||
const endpoint = `https://oidc.${region}.amazonaws.com/token`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
@@ -176,7 +179,9 @@ export class KiroService {
|
||||
|
||||
// AWS SSO OIDC refresh (Builder ID or IDC)
|
||||
if (clientId && clientSecret) {
|
||||
const endpoint = `https://oidc.${region || "us-east-1"}.amazonaws.com/token`;
|
||||
const safeRegion = region || "us-east-1";
|
||||
assertValidAwsRegion(safeRegion);
|
||||
const endpoint = `https://oidc.${safeRegion}.amazonaws.com/token`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
@@ -262,6 +267,7 @@ export class KiroService {
|
||||
* JSON-1.0 surface returns `arn`).
|
||||
*/
|
||||
async listAvailableProfiles(accessToken, region = "us-east-1") {
|
||||
assertValidAwsRegion(region);
|
||||
const endpoint = `https://codewhisperer.${region}.amazonaws.com`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
|
||||
Reference in New Issue
Block a user