"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.redteamRouter = void 0;
const decimal_js_1 = __importDefault(require("decimal.js"));
const express_1 = require("express");
const cliState_1 = __importDefault(require("../../cliState"));
const constants_1 = require("../../constants");
const database_1 = require("../../database");
const tables_1 = require("../../database/tables");
const logger_1 = __importDefault(require("../../logger"));
const remoteGeneration_1 = require("../../redteam/remoteGeneration");
const permissions_1 = require("../../util/config/permissions");
const evalJobsCache_1 = require("../../util/evalJobsCache");
const get_redteam_result_details_1 = require("../controller/red-team/get-redteam-result-details");
const get_redteam_report_metrics_1 = require("../controller/red-team/get-redteam-report-metrics");
const get_redteam_report_drawer_results_1 = require("../controller/red-team/get-redteam-report-drawer-results");
const get_redteam_result_list_1 = require("../controller/red-team/get-redteam-result-list");
const create_security_eval_1 = require("../controller/red-team/create-security-eval");
const generate_redteam_config_1 = require("../controller/red-team/generate-redteam-config");
const security_config_1 = require("../controller/red-team/security-config");
const get_redteam_filter_dropdown_1 = require("../controller/red-team/get-redteam-filter-dropdown");
const permission_1 = require("../middlewares/permission");
const activityLogService_1 = require("../services/activityLogService");
const time_1 = require("../../util/time");
const callControlPlaneApi_1 = require("../../util/callContolPlane/callControlPlaneApi");
const get_upgrade_meta_1 = require("../../util/get-upgrade-meta");
const drizzle_orm_1 = require("drizzle-orm");
const constants_2 = require("../../constants");
exports.redteamRouter = (0, express_1.Router)();
// Track the current running job
let currentJobId = null;
const currentProjectId = null;
let currentAbortController = null;
exports.redteamRouter.post('/run', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), create_security_eval_1.createSecurityEval);
exports.redteamRouter.post('/generate-config', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), generate_redteam_config_1.generateRedteamConfig);
exports.redteamRouter.post('/cancel', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), async (req, res) => {
    const jobId = req?.body?.testRunId ?? currentJobId;
    if (currentAbortController) {
        currentAbortController.abort();
    }
    const job = await (0, evalJobsCache_1.getEvalJob)(jobId);
    if (job) {
        await (0, evalJobsCache_1.updateEvalJob)(jobId, {
            status: 'error',
            logs: [...(job.logs || []), 'Job cancelled by user'],
        });
    }
    // Clear state
    cliState_1.default.webUI = false;
    currentJobId = null;
    currentAbortController = null;
    const db = await (0, database_1.getDb)();
    await db.transaction(async (tx) => {
        await tx
            .update(tables_1.testRunsTable)
            .set({ status: constants_2.TEST_RUN_STATUS.ABORTED })
            .where((0, drizzle_orm_1.eq)(tables_1.testRunsTable.id, jobId));
    });
    // Wait a moment to ensure cleanup
    await new Promise((resolve) => setTimeout(resolve, 100));
    res.json({ message: 'Job cancelled' });
});
// Security Config CRUD routes
exports.redteamRouter.post('/config', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), async (req, res) => {
    try {
        const { name, status, config, projectId, orgId, applicationId } = req.body;
        const parsedStatus = status === undefined ? status : Number.parseInt(status, 10);
        const result = await (0, security_config_1.createSecurityConfig)({
            name,
            status: parsedStatus,
            config,
            projectId,
            orgId,
            applicationId,
        });
        // Log activity after successful creation
        try {
            const token = req.headers['x-access-token'] || req.headers.authorization;
            const { userId, userName } = (0, activityLogService_1.getUserInfo)(req);
            if (token && orgId && typeof orgId === 'string') {
                // Get application name from config.target.label or fetch from database
                let applicationName = config?.target?.label || '';
                if (!applicationName && applicationId) {
                    applicationName = await (0, activityLogService_1.fetchApplicationName)(applicationId);
                }
                const [orgName, projectName] = await Promise.all([
                    (0, activityLogService_1.fetchOrgName)(token, orgId),
                    projectId && typeof projectId === 'string' ? (0, activityLogService_1.fetchProjectName)(token, orgId, projectId) : Promise.resolve(''),
                ]);
                await (0, activityLogService_1.logActivity)(token, {
                    type: constants_1.ActivityType.CREATE_SECURITY_CONFIG,
                    params: (0, activityLogService_1.buildActivityParams)({
                        application_name: { value: applicationName || (applicationId || ''), _id: applicationId || undefined },
                        user_name: { value: userName, _id: userId },
                        org_name: { value: orgName || orgId, _id: orgId },
                        project_name: { value: projectName || (projectId && typeof projectId === 'string' ? projectId : ''), _id: projectId && typeof projectId === 'string' ? projectId : undefined },
                    }),
                    project_id: projectId && typeof projectId === 'string' ? projectId : undefined,
                    org_id: orgId,
                    created_by: userId,
                    updated_by: userId,
                });
            }
        }
        catch (activityError) {
            logger_1.default.error(`Error logging activity for create security config: ${activityError}`);
        }
        res.status(201).json(result);
    }
    catch (error) {
        logger_1.default.error('Error creating security config: ' +
            (error instanceof Error ? error.message : String(error)));
        res.status(500).json({
            error: error instanceof Error ? error.message : 'Failed to create security config',
        });
    }
});
exports.redteamRouter.patch('/config/:id', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), async (req, res) => {
    try {
        const { id } = req.params;
        const { name, status, config, projectId, applicationId } = req.body;
        const parsedStatus = status === undefined ? status : Number.parseInt(status, 10);
        // Get security config details before updating for activity log
        const db = await (0, database_1.getDb)();
        const [securityConfig] = await db
            .select()
            .from(tables_1.securityConfigTable)
            .where((0, drizzle_orm_1.eq)(tables_1.securityConfigTable.id, id));
        const result = await (0, security_config_1.updateSecurityConfig)({
            id,
            name,
            status: parsedStatus,
            config,
            projectId,
            applicationId,
        });
        // Log activity after successful update
        try {
            const token = req.headers['x-access-token'] || req.headers.authorization;
            const { userId, userName } = (0, activityLogService_1.getUserInfo)(req);
            if (token && securityConfig && securityConfig.workspaceId && typeof securityConfig.workspaceId === 'string') {
                const orgId = securityConfig.workspaceId;
                const effectiveProjectId = (projectId && typeof projectId === 'string')
                    ? projectId
                    : (securityConfig.projectId && typeof securityConfig.projectId === 'string'
                        ? securityConfig.projectId
                        : '');
                // Get application name from config.target.label, applicationId, or existing config
                // applicationId can be in request body, config.target.id, or config.target.applicationId
                let applicationName = config?.target?.label || '';
                const effectiveApplicationId = applicationId
                    || config?.target?.id
                    || config?.target?.applicationId
                    || securityConfig.config?.target?.id
                    || securityConfig.config?.target?.applicationId;
                if (!applicationName && effectiveApplicationId) {
                    applicationName = await (0, activityLogService_1.fetchApplicationName)(effectiveApplicationId);
                }
                const [orgName, projectName] = await Promise.all([
                    (0, activityLogService_1.fetchOrgName)(token, orgId),
                    effectiveProjectId ? (0, activityLogService_1.fetchProjectName)(token, orgId, effectiveProjectId) : Promise.resolve(''),
                ]);
                await (0, activityLogService_1.logActivity)(token, {
                    type: constants_1.ActivityType.UPDATE_SECURITY_CONFIG,
                    params: (0, activityLogService_1.buildActivityParams)({
                        application_name: { value: applicationName || (effectiveApplicationId || ''), _id: effectiveApplicationId || undefined },
                        user_name: { value: userName, _id: userId },
                        org_name: { value: orgName || orgId, _id: orgId },
                        project_name: { value: projectName || effectiveProjectId, _id: effectiveProjectId || undefined },
                    }),
                    project_id: effectiveProjectId || undefined,
                    org_id: orgId,
                    created_by: userId,
                    updated_by: userId,
                });
            }
        }
        catch (activityError) {
            logger_1.default.error(`Error logging activity for update security config: ${activityError}`);
        }
        res.json(result);
    }
    catch (error) {
        logger_1.default.error('Error updating security config: ' +
            (error instanceof Error ? error.message : String(error)));
        res.status(500).json({
            error: error instanceof Error ? error.message : 'Failed to update security config',
        });
    }
});
exports.redteamRouter.get('/config/details', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    try {
        const { id, name } = req.query;
        const projectId = req.query.projectId;
        const params = {
            id,
            name,
            projectId,
        };
        const result = await (0, security_config_1.getSecurityConfigByIdOrName)(params);
        res.json(result);
    }
    catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        const errorStack = error instanceof Error && error.stack ? `\n${error.stack}` : '';
        logger_1.default.error('Error fetching security config: ' + errorMessage + errorStack);
        // Return 429 for rate limit errors, 404 for not found, 500 for others
        if (errorMessage.includes('rate limit') || errorMessage.includes('Rate limit')) {
            res.status(429).json({
                error: errorMessage,
                retryAfter: 'Please wait a few moments before retrying',
            });
        }
        else if (errorMessage.includes('not found')) {
            res.status(404).json({
                error: errorMessage,
            });
        }
        else {
            res.status(500).json({
                error: errorMessage,
            });
        }
    }
});
exports.redteamRouter.get('/filter-dropdown', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    try {
        const projectId = typeof req.query.projectId === 'string' ? req.query.projectId.trim() : '';
        if (!projectId) {
            res.status(400).json({ error: 'projectId is required' });
            return;
        }
        const search = typeof req.query.search === 'string' && req.query.search.trim() !== ''
            ? req.query.search.trim()
            : undefined;
        const rawLimit = req.query.limit;
        const parsedLimit = typeof rawLimit === 'string'
            ? Number.parseInt(rawLimit, 10)
            : typeof rawLimit === 'number'
                ? Math.floor(rawLimit)
                : Number.NaN;
        if (!Number.isFinite(parsedLimit) || parsedLimit < 1 || parsedLimit > 100) {
            res
                .status(400)
                .json({ error: 'limit is required and must be an integer from 1 to 100' });
            return;
        }
        const result = await (0, get_redteam_filter_dropdown_1.getRedteamFilterDropdown)({
            projectId,
            search,
            applicationLimit: parsedLimit,
        });
        res.json(result);
    }
    catch (error) {
        logger_1.default.error('Error fetching red team filter dropdown: ' +
            (error instanceof Error ? error.message : String(error)));
        res.status(500).json({
            error: error instanceof Error ? error.message : 'Failed to fetch filter dropdown',
        });
    }
});
exports.redteamRouter.get('/config', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    try {
        const projectId = req.query.projectId;
        const page = req.query.page ? Number(req.query.page) : undefined;
        const limit = req.query.limit ? Number(req.query.limit) : undefined;
        const archived_data_required = req.query.archived_data_required;
        const excludeCustomProviders = req.query.excludeCustomProviders === 'true' || req.query.excludeCustomProviders === '1'
            ? true
            : undefined;
        const search = typeof req.query.search === 'string' && req.query.search.trim() !== ''
            ? req.query.search.trim()
            : undefined;
        const result = await (0, security_config_1.getAllSecurityConfigs)(projectId, page, limit, archived_data_required, excludeCustomProviders, search);
        res.json(result ?? []);
    }
    catch (error) {
        logger_1.default.error('Error fetching security configs: ' +
            (error instanceof Error ? error.message : String(error)));
        res.status(500).json({
            error: error instanceof Error ? error.message : 'Failed to fetch security configs',
        });
    }
});
exports.redteamRouter.delete('/config/:id', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), async (req, res) => {
    try {
        const { id } = req.params;
        const result = await (0, security_config_1.deleteSecurityConfig)(id);
        res.json(result);
    }
    catch (error) {
        logger_1.default.error('Error deleting security config: ' +
            (error instanceof Error ? error.message : String(error)));
        res.status(500).json({
            error: error instanceof Error ? error.message : 'Failed to delete security config',
        });
    }
});
exports.redteamRouter.patch('/config/:id/archive', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), async (req, res) => {
    try {
        const { id } = req.params;
        const result = await (0, security_config_1.archiveSecurityConfig)(id);
        res.json(result);
    }
    catch (error) {
        logger_1.default.error('Error archiving security config: ' +
            (error instanceof Error ? error.message : String(error)));
        res.status(500).json({
            error: error instanceof Error ? error.message : 'Failed to archive security config',
        });
    }
});
// NOTE: This comes last, so the other routes take precedence
exports.redteamRouter.post('/:task', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    const { task } = req.params;
    const cloudFunctionUrl = (0, remoteGeneration_1.getRemoteGenerationUrl)();
    logger_1.default.debug(`Received ${task} task request: ${JSON.stringify({
        method: req.method,
        url: req.url,
        body: req.body,
    })}`);
    try {
        logger_1.default.debug(`Sending request to cloud function: ${cloudFunctionUrl}`);
        const response = await fetch(cloudFunctionUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                task,
                ...req.body,
            }),
        });
        if (!response.ok) {
            logger_1.default.error(`Cloud function responded with status ${response.status}`);
            throw new Error(`Cloud function responded with status ${response.status}`);
        }
        const data = await response.json();
        logger_1.default.debug(`Received response from cloud function: ${JSON.stringify(data)}`);
        res.json(data);
    }
    catch (error) {
        logger_1.default.error(`Error in ${task} task: ${error}`);
        res.status(500).json({ error: `Failed to process ${task} task` });
    }
});
exports.redteamRouter.get('/status', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_WRITE), async (req, res) => {
    res.json({
        hasRunningJob: currentJobId !== null && currentProjectId === req.query.projectId,
        jobId: currentJobId,
    });
});
exports.redteamRouter.get('/resultlist', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    try {
        const { orgId, projectId, page = 1, limit = 10, applicationIds, securityConfigIds } = req.query;
        const db = await (0, database_1.getDb)();
        // Calculate offset for pagination
        const offset = (Number(page) - 1) * Number(limit);
        const result = await (0, get_redteam_result_list_1.fetchRedteamResultList)({
            db,
            projectId,
            orgId,
            limit: Number(limit),
            offset,
            applicationIds,
            securityConfigIds,
        });
        res.json(result);
    }
    catch (error) {
        console.error('Error fetching redteam results:', error);
        res.status(500).json({ error: 'Failed to fetch redteam results' });
    }
});
exports.redteamRouter.get('/can-run-security-test', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    const { orgId } = req.query;
    const db = await (0, database_1.getDb)();
    const { restrictionsData, isEnterprise, wallet, isStarter, usages, notifications, thresholds } = await (0, get_upgrade_meta_1.getUpgradeMetaByOrgId)(orgId, {
        fetchMetricUsage: true,
    });
    // block run for reaching the functional evals limit
    if ((isStarter || !isEnterprise) && restrictionsData?.security_evals > 0) {
        const whereConditions = (0, time_1.createTimespanWhereConditions)({
            table: tables_1.testRunsTable,
            timeColumn: 'createdAt',
            orgId: req.body.orgId,
            timespan: restrictionsData?.security_evals_timespan || 'lifetime',
            extraConditions: (0, drizzle_orm_1.and)((0, drizzle_orm_1.sql) `${tables_1.testRunsTable.type} = ${constants_2.TEST_RUN_TYPE.SECURITY}`, (0, drizzle_orm_1.sql) `${tables_1.testRunsTable.status} = ${constants_2.TEST_RUN_STATUS.COMPLETED}`),
        });
        const allCount = await db
            .select({ count: (0, drizzle_orm_1.sql) `count(*)` })
            .from(tables_1.testRunsTable)
            .where(whereConditions)
            .then((result) => Number(result[0]?.count ?? 0));
        if (isStarter
            && new decimal_js_1.default(allCount).gt(0)
            && new decimal_js_1.default(allCount).gte(restrictionsData?.security_evals ?? 0)) {
            res.status(200).json({
                success: true,
                canRun: false,
                title: 'Upgrade your plan to unlock this feature.',
                message: 'Your current plan offers limited access. Upgrade now to unlock the complete set of tools and features needed to fully evaluate your application.',
                canRunUpgrade: true,
                canRunTopUp: false,
            });
            return;
        }
    }
    // block run for reaching the wallet limit
    if (!isEnterprise
        && new decimal_js_1.default(wallet?.credits ?? 0).lte(new decimal_js_1.default(0))) {
        if (notifications?.canSendWalletUsageExceeded) {
            await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/limit-reached`, {
                method: 'POST',
                params: {
                    orgId: req.body.orgId,
                    type: 'Wallet',
                    currentUsage: wallet?.credits ?? 0,
                },
            }, '');
        }
        res.status(200).json({
            success: true,
            canRun: false,
            title: 'You are low on TRU CREDITS!',
            message: 'Kindly recharge your wallet and continue evaluating your application to make it production ready!',
            canRunUpgrade: false,
            canRunTopUp: true,
        });
        return;
    }
    // send notification for low wallet balance
    if (!isEnterprise
        && new decimal_js_1.default(wallet?.credits ?? 0).lte(thresholds?.walletNotificationThreshold ?? 0)
        && notifications?.canSendLowWalletBalance) {
        await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/limit-reaching`, {
            method: 'POST',
            params: {
                orgId: req.body.orgId,
                type: 'Wallet',
                currentUsage: wallet?.credits ?? 0,
            },
        }, '');
    }
    // check for metric usage limit
    if (!isEnterprise
        && restrictionsData?.metrics > 0
        && new decimal_js_1.default(usages?.metric ?? 0).gte(restrictionsData?.metrics ?? 0)) {
        if (isStarter) {
            if (notifications?.canSendMetricUsageExceeded) {
                await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/limit-reached`, {
                    method: 'POST',
                    params: {
                        orgId: req.body.orgId,
                        type: 'Metric',
                        currentUsage: usages?.metric ?? 0,
                    },
                }, '');
            }
            res.status(200).json({
                success: true,
                canRun: false,
                title: 'You\'ve reached your metric limit.',
                message: 'Upgrade your plan to keep analyzing and improving your application.',
                canRunUpgrade: true,
                canRunTopUp: false,
            });
            return;
        }
        else if (notifications?.canSendMetricRenewal) {
            await (0, callControlPlaneApi_1.callControlPlane)(`/trusys-emails/limit-renewal`, {
                method: 'POST',
                params: {
                    orgId: req.body.orgId,
                    type: 'Metric',
                    currentUsage: usages?.metric ?? 0,
                },
            }, '');
        }
    }
    res.status(200).json({
        success: true,
        canRun: true,
        title: 'You can run the security test.',
        message: 'You can run the security test.',
        canRunUpgrade: false,
        canRunTopUp: false,
    });
});
exports.redteamRouter.get('/:resultId/report/metrics', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), get_redteam_report_metrics_1.getRedteamReportMetrics);
exports.redteamRouter.get('/:resultId/report/drawer-results', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), get_redteam_report_drawer_results_1.getRedteamReportDrawerResults);
exports.redteamRouter.get('/:resultId', (0, permission_1.hasPermission)(permissions_1.Permission.SECURITY_EVALUATION_READ), async (req, res) => {
    try {
        const { resultId } = req.params;
        const { page = 1, limit = 10, orgId, accessKey = null } = req.query;
        const db = await (0, database_1.getDb)();
        const result = await (0, get_redteam_result_details_1.fetchRedteamResultDetails)({
            db,
            resultId,
            page: page ? Number(page) : undefined,
            limit: limit ? Number(limit) : undefined,
            orgId: orgId,
            accessKey: accessKey,
        });
        if (!result) {
            res.status(404).json({ error: 'Redteam result not found' });
            return;
        }
        res.json({ data: result });
    }
    catch (error) {
        console.error('Error fetching redteam result details:', error);
        res.status(500).json({ error: 'Failed to fetch redteam result details' });
    }
});
//# sourceMappingURL=redteam.js.map