import type { SecretsDetectionConfig } from "../config";
+import type { ChatCompletionRequest } from "../services/llm-client";
export interface SecretsMatch {
type: "OPENSSH_PRIVATE_KEY" | "PEM_PRIVATE_KEY";
redactions?: SecretsRedaction[];
}
+/**
+ * Extracts all text content from an OpenAI chat completion request
+ *
+ * Concatenates content from all messages (system, user, assistant) for secrets scanning.
+ * The proxy validation ensures content is always a string, so we can safely access it directly.
+ *
+ * Returns concatenated text for secrets scanning.
+ */
+export function extractTextFromRequest(body: ChatCompletionRequest): string {
+ return body.messages
+ .map((message) => message.content)
+ .filter(
+ (content): content is string =>
+ typeof content === "string" && content.length > 0
+ )
+ .join("\n");
+}
+
/**
* Detects secret material (e.g. private keys) in text
*
*/
export function detectSecrets(
text: string,
- config: SecretsDetectionConfig,
+ config: SecretsDetectionConfig
): SecretsDetectionResult {
if (!config.enabled) {
return { detected: false, matches: [] };
}
// Apply max_scan_chars limit
- const textToScan = config.max_scan_chars > 0
- ? text.slice(0, config.max_scan_chars)
- : text;
+ const textToScan =
+ config.max_scan_chars > 0 ? text.slice(0, config.max_scan_chars) : text;
const matches: SecretsMatch[] = [];
const redactions: SecretsRedaction[] = [];
// OpenSSH private key pattern
if (entitiesToDetect.has("OPENSSH_PRIVATE_KEY")) {
- const opensshPattern = /-----BEGIN OPENSSH PRIVATE KEY-----[\s\S]*?-----END OPENSSH PRIVATE KEY-----/g;
+ const opensshPattern =
+ /-----BEGIN OPENSSH PRIVATE KEY-----[\s\S]*?-----END OPENSSH PRIVATE KEY-----/g;
const opensshMatches = textToScan.matchAll(opensshPattern);
let count = 0;
for (const match of opensshMatches) {
const matchedPositions = new Set<number>();
// RSA PRIVATE KEY
- const rsaPattern = /-----BEGIN RSA PRIVATE KEY-----[\s\S]*?-----END RSA PRIVATE KEY-----/g;
+ const rsaPattern =
+ /-----BEGIN RSA PRIVATE KEY-----[\s\S]*?-----END RSA PRIVATE KEY-----/g;
let rsaCount = 0;
for (const match of textToScan.matchAll(rsaPattern)) {
rsaCount++;
}
// PRIVATE KEY (generic) - exclude RSA matches
- const privateKeyPattern = /-----BEGIN PRIVATE KEY-----[\s\S]*?-----END PRIVATE KEY-----/g;
+ const privateKeyPattern =
+ /-----BEGIN PRIVATE KEY-----[\s\S]*?-----END PRIVATE KEY-----/g;
let privateKeyCount = 0;
for (const match of textToScan.matchAll(privateKeyPattern)) {
if (match.index !== undefined && !matchedPositions.has(match.index)) {
}
// ENCRYPTED PRIVATE KEY
- const encryptedPattern = /-----BEGIN ENCRYPTED PRIVATE KEY-----[\s\S]*?-----END ENCRYPTED PRIVATE KEY-----/g;
+ const encryptedPattern =
+ /-----BEGIN ENCRYPTED PRIVATE KEY-----[\s\S]*?-----END ENCRYPTED PRIVATE KEY-----/g;
let encryptedCount = 0;
for (const match of textToScan.matchAll(encryptedPattern)) {
if (match.index !== undefined && !matchedPositions.has(match.index)) {