Implements GitHub issues #104 and #102: **#104: Sensitive File Protection** - Block writes to .env, credentials, SSH keys, cloud configs - Categories: environment, credentials, ssh_keys, api_tokens, certificates, cloud_config - Warn on reads of .env files (may contain secrets) - Block writes to id_rsa, *.pem, *.key, credentials.json, etc. **#102: Auto-Backup System** - Automatic backup before every write/edit operation - Backups stored in .codetyper-backup/ (preserves directory structure) - Max 10 backups per file - 7-day retention with auto-cleanup - listBackups, getLatestBackup, restoreFromBackup functions Closes #104 Closes #102
164 lines
4.0 KiB
TypeScript
164 lines
4.0 KiB
TypeScript
/**
|
|
* Sensitive File Guard Service
|
|
*
|
|
* Protects sensitive files (credentials, keys, etc.) from being
|
|
* modified or inadvertently exposed.
|
|
*/
|
|
|
|
import {
|
|
PROTECTED_FILE_PATTERNS,
|
|
SENSITIVE_FILE_MESSAGES,
|
|
type ProtectedFilePattern,
|
|
type SensitiveFileCategory,
|
|
} from "@constants/sensitive-files";
|
|
|
|
/**
|
|
* Type of file operation
|
|
*/
|
|
export type FileOperation = "read" | "write" | "edit" | "delete";
|
|
|
|
/**
|
|
* Result of checking a file for sensitivity
|
|
*/
|
|
export interface SensitiveFileCheckResult {
|
|
/** Whether the operation should be blocked */
|
|
blocked: boolean;
|
|
/** Whether to show a warning (for allowed reads) */
|
|
warn: boolean;
|
|
/** The matched pattern, if any */
|
|
pattern?: ProtectedFilePattern;
|
|
/** User-friendly message */
|
|
message?: string;
|
|
}
|
|
|
|
/**
|
|
* Check if a file operation on the given path should be blocked or warned
|
|
*/
|
|
export const checkSensitiveFile = (
|
|
filePath: string,
|
|
operation: FileOperation,
|
|
): SensitiveFileCheckResult => {
|
|
// Normalize path for checking
|
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
|
|
for (const pattern of PROTECTED_FILE_PATTERNS) {
|
|
if (pattern.pattern.test(normalizedPath)) {
|
|
// For read operations on files that allow reading
|
|
if (operation === "read" && pattern.allowRead) {
|
|
return {
|
|
blocked: false,
|
|
warn: true,
|
|
pattern,
|
|
message: formatWarningMessage(pattern),
|
|
};
|
|
}
|
|
|
|
// For write/edit/delete or read on files that don't allow reading
|
|
if (operation !== "read" || !pattern.allowRead) {
|
|
return {
|
|
blocked: true,
|
|
warn: false,
|
|
pattern,
|
|
message: formatBlockedMessage(pattern, operation),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
return { blocked: false, warn: false };
|
|
};
|
|
|
|
/**
|
|
* Format a warning message for sensitive file reads
|
|
*/
|
|
const formatWarningMessage = (pattern: ProtectedFilePattern): string => {
|
|
const categoryDescription =
|
|
SENSITIVE_FILE_MESSAGES.CATEGORY_DESCRIPTIONS[pattern.category];
|
|
|
|
return [
|
|
`[WARNING] ${pattern.description}`,
|
|
"",
|
|
categoryDescription,
|
|
SENSITIVE_FILE_MESSAGES.WARN_READ,
|
|
].join("\n");
|
|
};
|
|
|
|
/**
|
|
* Format a blocked message for sensitive file writes
|
|
*/
|
|
const formatBlockedMessage = (
|
|
pattern: ProtectedFilePattern,
|
|
operation: FileOperation,
|
|
): string => {
|
|
const categoryDescription =
|
|
SENSITIVE_FILE_MESSAGES.CATEGORY_DESCRIPTIONS[pattern.category];
|
|
|
|
const operationText = operation === "read" ? "reading" : "modifying";
|
|
const suggestion =
|
|
operation === "read"
|
|
? SENSITIVE_FILE_MESSAGES.READ_SUGGESTION
|
|
: SENSITIVE_FILE_MESSAGES.WRITE_SUGGESTION;
|
|
|
|
return [
|
|
`[BLOCKED] Cannot ${operation} ${pattern.description.toLowerCase()}`,
|
|
"",
|
|
`Category: ${formatCategoryName(pattern.category)}`,
|
|
"",
|
|
categoryDescription,
|
|
SENSITIVE_FILE_MESSAGES.BLOCKED_REASON,
|
|
"",
|
|
suggestion,
|
|
].join("\n");
|
|
};
|
|
|
|
/**
|
|
* Format category name for display
|
|
*/
|
|
const formatCategoryName = (category: SensitiveFileCategory): string => {
|
|
const names: Record<SensitiveFileCategory, string> = {
|
|
environment: "Environment Files",
|
|
credentials: "Credential Files",
|
|
ssh_keys: "SSH Keys",
|
|
api_tokens: "API Tokens",
|
|
certificates: "Certificates",
|
|
cloud_config: "Cloud Configuration",
|
|
};
|
|
return names[category];
|
|
};
|
|
|
|
/**
|
|
* Get all protected patterns (for configuration UI)
|
|
*/
|
|
export const getProtectedPatterns = (): readonly ProtectedFilePattern[] => {
|
|
return PROTECTED_FILE_PATTERNS;
|
|
};
|
|
|
|
/**
|
|
* Check if a file path is sensitive
|
|
*/
|
|
export const isSensitiveFile = (filePath: string): boolean => {
|
|
const result = checkSensitiveFile(filePath, "read");
|
|
return result.blocked || result.warn;
|
|
};
|
|
|
|
/**
|
|
* Check if a file can be safely written
|
|
*/
|
|
export const canWriteFile = (filePath: string): boolean => {
|
|
return !checkSensitiveFile(filePath, "write").blocked;
|
|
};
|
|
|
|
/**
|
|
* Get categories of sensitive files
|
|
*/
|
|
export const getSensitiveFileCategories = (): SensitiveFileCategory[] => {
|
|
return [
|
|
"environment",
|
|
"credentials",
|
|
"ssh_keys",
|
|
"api_tokens",
|
|
"certificates",
|
|
"cloud_config",
|
|
];
|
|
};
|