"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DETECTION_TIMEOUT_MS = void 0;
exports.getDetectedPlatforms = getDetectedPlatforms;
const node_http_1 = __importDefault(require("node:http"));
const client_sts_1 = require("@aws-sdk/client-sts");
const AWS_METADATA_BASE_URL = 'http://169.254.169.254';
const AZURE_METADATA_BASE_URL = 'http://169.254.169.254';
const GCE_METADATA_ROOT_URL = 'http://metadata.google.internal';
const GCE_METADATA_BASE_URL = 'http://metadata.google.internal/computeMetadata/v1';
exports.DETECTION_TIMEOUT_MS = 200;
const DETECTORS = {
    is_aws_lambda: isAwsLambda,
    is_azure_function: isAzureFunction,
    is_gce_cloud_run_service: isGceCloudRunService,
    is_gce_cloud_run_job: isGceCloudRunJob,
    is_github_action: isGithubAction,
    is_ec2_instance: isEc2Instance,
    has_aws_identity: hasAwsIdentity,
    is_azure_vm: isAzureVm,
    has_azure_managed_identity: hasAzureManagedIdentity,
    is_gce_vm: isGceVm,
    has_gcp_identity: hasGcpIdentity,
};
const DETECTION_RESULT = detectPlatforms();
function getDetectedPlatforms() {
    return DETECTION_RESULT;
}
async function detectPlatforms() {
    if (process.env['SNOWFLAKE_DISABLE_PLATFORM_DETECTION']?.toLowerCase() === 'true') {
        return ['disabled'];
    }
    const abortSignal = AbortSignal.timeout(exports.DETECTION_TIMEOUT_MS);
    const results = await Promise.all(Object.entries(DETECTORS).map(async ([name, detector]) => ({
        name,
        detected: await Promise.resolve(detector(abortSignal)).catch(() => false),
    })));
    return results.filter(({ detected }) => detected).map(({ name }) => name);
}
function isAwsLambda() {
    return envPresent('LAMBDA_TASK_ROOT');
}
function isAzureFunction() {
    return envPresent('FUNCTIONS_WORKER_RUNTIME', 'FUNCTIONS_EXTENSION_VERSION', 'AzureWebJobsStorage');
}
function isGceCloudRunService() {
    return envPresent('K_SERVICE', 'K_REVISION', 'K_CONFIGURATION');
}
function isGceCloudRunJob() {
    return envPresent('CLOUD_RUN_JOB', 'CLOUD_RUN_EXECUTION');
}
function isGithubAction() {
    return envPresent('GITHUB_ACTIONS');
}
// Intentionally not using the available AWS SDK, as it does not support abort signals and is easy to
// misconfigure retry logic.
async function isEc2Instance(abortSignal) {
    const tokenResult = await httpRequest(`${AWS_METADATA_BASE_URL}/latest/api/token`, abortSignal, { 'X-aws-ec2-metadata-token-ttl-seconds': '21600' }, 'PUT').catch(() => null);
    const token = tokenResult?.ok ? tokenResult.body.trim() : null;
    const result = await httpRequest(`${AWS_METADATA_BASE_URL}/latest/dynamic/instance-identity/document`, abortSignal, token ? { 'X-aws-ec2-metadata-token': token } : undefined);
    if (!result.ok)
        return false;
    const doc = JSON.parse(result.body);
    return !!doc.instanceId;
}
async function hasAwsIdentity(abortSignal) {
    const client = new client_sts_1.STSClient({});
    const response = await client.send(new client_sts_1.GetCallerIdentityCommand({}), {
        abortSignal,
    });
    if (!response.Arn)
        return false;
    return isValidArnForWif(response.Arn);
}
async function isAzureVm(abortSignal) {
    const result = await httpRequest(`${AZURE_METADATA_BASE_URL}/metadata/instance?api-version=2021-02-01`, abortSignal, { Metadata: 'true' });
    return result.ok;
}
async function hasAzureManagedIdentity(abortSignal) {
    if (isAzureFunction() && envPresent('IDENTITY_HEADER')) {
        return true;
    }
    const params = new URLSearchParams({
        'api-version': '2018-02-01',
        resource: 'https://management.azure.com',
    });
    const result = await httpRequest(`${AZURE_METADATA_BASE_URL}/metadata/identity/oauth2/token?${params}`, abortSignal, { Metadata: 'true' });
    return result.ok;
}
async function isGceVm(abortSignal) {
    const result = await httpRequest(GCE_METADATA_ROOT_URL, abortSignal);
    return result.headers['metadata-flavor'] === 'Google';
}
async function hasGcpIdentity(abortSignal) {
    const result = await httpRequest(`${GCE_METADATA_BASE_URL}/instance/service-accounts/default/email`, abortSignal, { 'Metadata-Flavor': 'Google' });
    return result.ok;
}
function envPresent(...envVars) {
    return envVars.every((v) => {
        const val = process.env[v];
        return val !== undefined && val !== '';
    });
}
/*
 * We intentionally avoid using fetch() here, since aborting fetch requests
 * can sometimes leave sockets open and prevent the Node.js process from exiting (tested on 22).
 */
function httpRequest(url, signal, headers, method = 'GET') {
    return new Promise((resolve, reject) => {
        const req = node_http_1.default.request(url, { method, signal, headers }, (res) => {
            let body = '';
            const statusCode = res.statusCode ?? 0;
            res.on('data', (chunk) => (body += chunk));
            res.on('end', () => resolve({ ok: statusCode >= 200 && statusCode < 300, headers: res.headers, body }));
            res.on('error', reject);
        });
        req.on('error', reject);
        req.end();
    });
}
function isValidArnForWif(arn) {
    return [/^arn:[^:]+:iam::[^:]+:user\/.+$/, /^arn:[^:]+:sts::[^:]+:assumed-role\/.+$/].some((pattern) => pattern.test(arn));
}
//# sourceMappingURL=platform_detection.js.map