feat(CF-567): Add project key validation to prevent corrupt data

- Add PROJECT_KEY_REGEX for valid format (2-5 uppercase letters)
- Add validateProjectKey() and isValidProjectKey() functions
- Update getProjectKey() to validate input and generated keys
- Reject invalid formats with clear error messages

Invalid formats now rejected:
- Single letters (A, C, U)
- Numbers (1, 20, 123)
- Full names (ClaudeFramework, Circles)
- Mixed case (Circles)
- Too long (>5 chars)

Also fixes Sentry SDK v8 API changes (httpIntegration, postgresIntegration).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-01-29 15:03:03 +02:00
parent 840767cea3
commit c83d36a2e8
4 changed files with 1771 additions and 22 deletions

View File

@@ -11,7 +11,7 @@
*/
import * as Sentry from "@sentry/node";
import { nodeProfilingIntegration } from "@sentry/profiling-node";
// Profiling integration removed due to type incompatibilities (CF-567)
export function initSentry(environment: string = "development"): void {
const dsn = process.env.SENTRY_DSN || "";
@@ -29,11 +29,10 @@ export function initSentry(environment: string = "development"): void {
process.env.SENTRY_PROFILE_SAMPLE_RATE || "0.01"
),
integrations: [
nodeProfilingIntegration(),
new Sentry.Integrations.Http({ tracing: true }),
new Sentry.Integrations.Postgres({ recordStatementAsSpans: true }),
Sentry.httpIntegration(),
Sentry.postgresIntegration(),
],
beforeSend(event, hint) {
beforeSend(event: Sentry.ErrorEvent, hint: Sentry.EventHint) {
// MCP protocol: Don't send normal error responses (isError: true)
const originalException = hint.originalException as any;
if (originalException?.isError === true) {
@@ -94,20 +93,20 @@ export async function withSentryTransaction<T>(
toolName: string,
handler: () => Promise<T>
): Promise<T> {
return Sentry.startActiveSpan(
return Sentry.startSpan(
{ name: `tool_${toolName}`, op: "mcp.tool" },
async (span) => {
async (span: Sentry.Span) => {
try {
const result = await handler();
span.setStatus("ok");
span.setStatus({ code: 1, message: "ok" }); // SpanStatusCode.OK
return result;
} catch (error: any) {
} catch (error: unknown) {
// Capture exception to Sentry (unless it's a normal MCP error)
if (!error.isError) {
const err = error as { isError?: boolean };
if (!err.isError) {
Sentry.captureException(error);
}
span.recordException(error);
span.setStatus("error");
span.setStatus({ code: 2, message: "error" }); // SpanStatusCode.ERROR
throw error;
}
}