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:
decolua
2026-06-19 15:09:18 +07:00
parent f2a7ae2030
commit 126aa244c5
4 changed files with 27 additions and 3 deletions

View File

@@ -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 }
);
}
}

View File

@@ -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 = {

View File

@@ -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",

View File

@@ -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, {