mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
Reverts the following commits from main: -d7a8b8f2perf: fix CI build + lazy-load heavy libs + parallelize DB queries (#2601) -48b5e2b4fix: make tree-sitter WASM build failure non-fatal when cache exists (#2602) -c372b6bcMerge pull request #2603 from codeflash-ai/fix/deploy-build-common -b656bb1dfix: cf-api deploy broken by pnpm workspace migration -c1b0076cfix: align TypeScript versions to deduplicate @prisma/client in pnpm -09ed4d4bfix: use redirect instead of throw for auth failures during prerender -71127055fix: redirect remaining auth throws that crash prerendering PR #2601 introduced 18 bugs including 5 authorization bypass vulnerabilities: - Cross-org data access via forged currentOrganizationId cookie - Cross-repo/cross-org member role escalation and deletion (unscoped lookups) - Missing replayTests/concolicTests in approval flow - repository_id filter silently broken for personal accounts - Tests mocking wrong Prisma method ($queryRawUnsafe vs $queryRaw) The subsequent PRs (#2602, #2603, and follow-up commits) were dependent fixes for issues caused by #2601 and are reverted together. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
232 lines
7.7 KiB
TypeScript
232 lines
7.7 KiB
TypeScript
import { prisma } from "@codeflash-ai/common"
|
|
import { Request, Response } from "express"
|
|
import { githubApp } from "../github/github-app.js"
|
|
import { getInstallationOctokitByOwner, isUserCollaborator } from "../github/github-utils.js"
|
|
import { userNickname } from "../auth0-mgmt.js"
|
|
import { posthog } from "../analytics.js"
|
|
import { logger } from "../utils/logger.js"
|
|
import {
|
|
missingRequiredFields,
|
|
unauthorized,
|
|
githubNotCollaborator,
|
|
validationFailure,
|
|
internalServerError,
|
|
conflict,
|
|
} from "../exceptions/index.js"
|
|
|
|
export async function is_code_being_optimized_again(req: Request, res: Response) {
|
|
try {
|
|
const { owner, repo, pr_number, code_contexts } = req.body
|
|
const userId = (req as any).userId
|
|
|
|
if (!repo || !owner || !pr_number || !code_contexts) {
|
|
throw missingRequiredFields("repo, owner, pr_number, code_contexts")
|
|
}
|
|
const nickname: string | null = await userNickname(userId)
|
|
if (nickname == null) {
|
|
throw unauthorized("")
|
|
}
|
|
const octokit = await getInstallationOctokitByOwner(githubApp, owner, repo, userId)
|
|
if (octokit instanceof Error) {
|
|
throw internalServerError(octokit.message)
|
|
}
|
|
|
|
// Check collaborator status with error handling
|
|
try {
|
|
const isCollaborator = await isUserCollaborator(octokit, owner, repo, nickname)
|
|
if (!isCollaborator) {
|
|
logger.warn("User is not a collaborator on repository", req, {
|
|
nickname,
|
|
repo: `${owner}/${repo}`,
|
|
})
|
|
throw githubNotCollaborator(`${owner}/${repo}`)
|
|
}
|
|
} catch (error) {
|
|
if (error && typeof error === "object" && "getHttpStatus" in error) {
|
|
throw error
|
|
}
|
|
logger.error("Error checking collaborator status", req, {}, error as Error)
|
|
throw internalServerError("Failed to verify collaborator status")
|
|
}
|
|
|
|
// Validate pr_number is an integer above 0
|
|
const pr_number_int = parseInt(pr_number, 10)
|
|
if (isNaN(pr_number_int) || pr_number_int <= 0) {
|
|
throw validationFailure("pr_number must be a positive integer")
|
|
}
|
|
|
|
// Validate code_contexts is an array
|
|
if (!Array.isArray(code_contexts)) {
|
|
throw validationFailure("code_contexts must be an array of objects")
|
|
}
|
|
|
|
if (code_contexts.length === 0) {
|
|
throw validationFailure("code_contexts cannot be empty")
|
|
}
|
|
|
|
// Validate each code context object has required fields
|
|
for (let i = 0; i < code_contexts.length; i++) {
|
|
const context = code_contexts[i]
|
|
|
|
if (typeof context !== "object" || context === null) {
|
|
throw validationFailure(`code_contexts[${i}] must be an object`)
|
|
}
|
|
|
|
const { file_path, function_name, code_hash } = context
|
|
|
|
if (typeof file_path !== "string" || file_path.trim().length === 0) {
|
|
throw validationFailure(`code_contexts[${i}].file_path must be a non-empty string`)
|
|
}
|
|
|
|
if (typeof function_name !== "string" || function_name.trim().length === 0) {
|
|
throw validationFailure(`code_contexts[${i}].function_name must be a non-empty string`)
|
|
}
|
|
|
|
if (typeof code_hash !== "string" || !/^[a-f0-9]{64}$/.test(code_hash)) {
|
|
throw validationFailure(`code_contexts[${i}].code_hash must be a valid SHA-256 hash`)
|
|
}
|
|
}
|
|
|
|
// Build the compound keys for batch querying
|
|
const compoundKeys = code_contexts.map(({ code_hash }) => ({
|
|
repo,
|
|
owner,
|
|
pr_number: pr_number_int,
|
|
context_hash: code_hash,
|
|
}))
|
|
|
|
const existingRecords = await prisma.pr_code_context_hash_cache.findMany({
|
|
where: {
|
|
OR: compoundKeys.map(key => ({
|
|
repo: key.repo,
|
|
owner: key.owner,
|
|
pr_number: key.pr_number,
|
|
context_hash: key.context_hash,
|
|
})),
|
|
},
|
|
select: {
|
|
context_hash: true,
|
|
},
|
|
})
|
|
|
|
// Create a Set of existing hashes for O(1) lookup
|
|
const existingHashes = new Set(existingRecords.map(record => record.context_hash))
|
|
|
|
// Identify code contexts that are already optimized (return as tuples)
|
|
const alreadyOptimizedTuples = code_contexts
|
|
.filter(({ code_hash }) => existingHashes.has(code_hash))
|
|
.map(({ file_path, function_name }) => [file_path, function_name])
|
|
posthog?.capture({
|
|
distinctId: userId,
|
|
event: `cfapi-github-pr-optimization`,
|
|
properties: {
|
|
repo_owner: owner,
|
|
repo_name: repo,
|
|
pr_number: pr_number,
|
|
},
|
|
})
|
|
|
|
logger.info("Code context check completed", req, {
|
|
repo: `${owner}/${repo}`,
|
|
pr_number,
|
|
total_contexts: code_contexts.length,
|
|
already_optimized: alreadyOptimizedTuples.length,
|
|
new_contexts: code_contexts.length - alreadyOptimizedTuples.length,
|
|
})
|
|
|
|
res.status(200).json({
|
|
already_optimized_tuples: alreadyOptimizedTuples,
|
|
total_contexts: code_contexts.length,
|
|
new_contexts: code_contexts.length - alreadyOptimizedTuples.length,
|
|
})
|
|
} catch (error) {
|
|
if (error && typeof error === "object" && "getHttpStatus" in error) {
|
|
throw error
|
|
}
|
|
logger.errorWithSentry("Error in is_code_being_optimized_again", req, {}, error as Error)
|
|
throw internalServerError("Error checking code optimization status")
|
|
}
|
|
}
|
|
|
|
export async function add_optimized_code_context(req: Request, res: Response) {
|
|
try {
|
|
const { owner, repo, pr_number, code_hash } = req.body
|
|
const userId = (req as any).userId
|
|
|
|
if (!repo || !owner || !pr_number || !code_hash) {
|
|
throw missingRequiredFields("repo, owner, pr_number, code_hash")
|
|
}
|
|
|
|
const nickname: string | null = await userNickname(userId)
|
|
if (nickname == null) {
|
|
throw unauthorized("")
|
|
}
|
|
|
|
const octokit = await getInstallationOctokitByOwner(githubApp, owner, repo, userId)
|
|
if (octokit instanceof Error) {
|
|
throw internalServerError(octokit.message)
|
|
}
|
|
|
|
// Check collaborator status with error handling
|
|
try {
|
|
const isCollaborator = await isUserCollaborator(octokit, owner, repo, nickname)
|
|
if (!isCollaborator) {
|
|
logger.warn("User is not a collaborator on repository", req, {
|
|
nickname,
|
|
repo: `${owner}/${repo}`,
|
|
})
|
|
throw githubNotCollaborator(`${owner}/${repo}`)
|
|
}
|
|
} catch (error) {
|
|
if (error && typeof error === "object" && "getHttpStatus" in error) {
|
|
throw error
|
|
}
|
|
logger.error("Error checking collaborator status", req, {}, error as Error)
|
|
throw internalServerError("Failed to verify collaborator status")
|
|
}
|
|
|
|
// Validate pr_number is an integer above 0
|
|
const pr_number_int = parseInt(pr_number, 10)
|
|
if (isNaN(pr_number_int) || pr_number_int <= 0) {
|
|
throw validationFailure("pr_number must be a positive integer")
|
|
}
|
|
|
|
// Validate code_hash is a valid SHA-256 hash
|
|
if (typeof code_hash !== "string" || !/^[a-f0-9]{64}$/.test(code_hash)) {
|
|
throw validationFailure("code_hash must be a valid SHA-256 hash")
|
|
}
|
|
|
|
// Create the new entry
|
|
await prisma.pr_code_context_hash_cache.create({
|
|
data: {
|
|
repo,
|
|
owner,
|
|
pr_number: pr_number_int,
|
|
context_hash: code_hash,
|
|
},
|
|
})
|
|
|
|
logger.info("Code context successfully added", req, {
|
|
repo: `${owner}/${repo}`,
|
|
pr_number,
|
|
code_hash,
|
|
})
|
|
|
|
res.status(201).json({
|
|
message: "Code context successfully added",
|
|
})
|
|
} catch (error: any) {
|
|
if (error && typeof error === "object" && "getHttpStatus" in error) {
|
|
throw error
|
|
}
|
|
logger.errorWithSentry("Error in add_optimized_code_context", req, {}, error as Error)
|
|
|
|
// Handle unique constraint violations gracefully
|
|
if (error.code === "P2002") {
|
|
logger.warn("Code context already exists for this PR", req, {})
|
|
throw conflict("Code context already exists for this PR")
|
|
}
|
|
|
|
throw internalServerError("Error adding code context")
|
|
}
|
|
}
|