codeflash-internal/js/cf-api/endpoints/code-context-hash.ts
HeshamHM28 a805f4cfbf revert: rollback PR #2601 and dependent fixes to ec39cd51
Reverts the following commits from main:
- d7a8b8f2 perf: fix CI build + lazy-load heavy libs + parallelize DB queries (#2601)
- 48b5e2b4 fix: make tree-sitter WASM build failure non-fatal when cache exists (#2602)
- c372b6bc Merge pull request #2603 from codeflash-ai/fix/deploy-build-common
- b656bb1d fix: cf-api deploy broken by pnpm workspace migration
- c1b0076c fix: align TypeScript versions to deduplicate @prisma/client in pnpm
- 09ed4d4b fix: use redirect instead of throw for auth failures during prerender
- 71127055 fix: 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>
2026-04-13 18:59:11 +00:00

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")
}
}