@dawans/promptshield
Version:
Secure your LLM stack with enterprise-grade RulePacks for AI safety scanning
227 lines (226 loc) • 9.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputFileValidatorImpl = void 0;
const Result_1 = require("../../../../shared/types/Result");
const ValidationResult_1 = require("../../core/entities/ValidationResult");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
/**
* Input file validator implementation
*/
class InputFileValidatorImpl {
async validate(target, options) {
const builder = new ValidationResult_1.ValidationResultBuilder(target, 'input-file');
try {
// Validate file exists
const existsResult = await this.validateFileExists(target);
if (existsResult.isErr()) {
builder.addError('file', existsResult.error.message, 'FILE_NOT_FOUND');
return (0, Result_1.ok)(builder.build());
}
// Validate file readability
const readableResult = await this.validateFileReadability(target);
if (readableResult.isErr()) {
builder.addError('file', readableResult.error.message, 'FILE_NOT_READABLE');
return (0, Result_1.ok)(builder.build());
}
// Validate file format
const formatResult = await this.validateFileFormat(target, options.format);
if (formatResult.isErr()) {
builder.addError('format', formatResult.error.message, 'INVALID_FORMAT');
return (0, Result_1.ok)(builder.build());
}
// Validate file content based on format
const contentResult = await this.validateFileContent(target, options, builder);
if (contentResult.isErr()) {
builder.addError('content', contentResult.error.message, 'INVALID_CONTENT');
}
return (0, Result_1.ok)(builder.build());
}
catch (error) {
return (0, Result_1.err)(new Error(`Input file validation failed: ${error}`));
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
supports(target, _options) {
const ext = path.extname(target).toLowerCase();
return ['.json', '.ndjson', '.jsonl', '.txt'].includes(ext);
}
async validateFileExists(filePath) {
try {
if (!fs.existsSync(filePath)) {
return (0, Result_1.err)(new Error(`File not found: ${filePath}`));
}
return (0, Result_1.ok)(true);
}
catch (error) {
return (0, Result_1.err)(new Error(`Error checking file existence: ${error}`));
}
}
async validateFileFormat(filePath, expectedFormat) {
try {
const ext = path.extname(filePath).toLowerCase();
const supportedFormats = ['.json', '.ndjson', '.jsonl', '.txt'];
if (!supportedFormats.includes(ext)) {
return (0, Result_1.err)(new Error(`Unsupported file format: ${ext}. Supported formats: ${supportedFormats.join(', ')}`));
}
// Check if expected format matches
if (expectedFormat) {
const expectedExt = expectedFormat.startsWith('.')
? expectedFormat
: `.${expectedFormat}`;
if (ext !== expectedExt) {
return (0, Result_1.err)(new Error(`Expected format ${expectedFormat} but got ${ext}`));
}
}
return (0, Result_1.ok)(true);
}
catch (error) {
return (0, Result_1.err)(new Error(`Error validating file format: ${error}`));
}
}
async validateJsonStructure(content) {
try {
JSON.parse(content);
return (0, Result_1.ok)(true);
}
catch (error) {
return (0, Result_1.err)(new Error(`Invalid JSON structure: ${error}`));
}
}
async validateFileReadability(filePath) {
try {
fs.accessSync(filePath, fs.constants.R_OK);
return (0, Result_1.ok)(true);
}
catch {
return (0, Result_1.err)(new Error(`File is not readable: ${filePath}`));
}
}
async validateFileContent(filePath, options, builder) {
try {
const ext = path.extname(filePath).toLowerCase();
const content = await fs.promises.readFile(filePath, 'utf-8');
// Validate empty files
if (content.trim().length === 0) {
builder.addWarning('content', 'File is empty', 'EMPTY_FILE');
return (0, Result_1.ok)(true);
}
// Validate based on file format
switch (ext) {
case '.json':
return await this.validateJsonFile(content, builder);
case '.ndjson':
case '.jsonl':
return await this.validateNdjsonFile(content, builder);
case '.txt':
return await this.validateTextFile(content, builder);
default:
return (0, Result_1.ok)(true);
}
}
catch (error) {
return (0, Result_1.err)(new Error(`Error validating file content: ${error}`));
}
}
async validateJsonFile(content, builder) {
try {
const data = JSON.parse(content);
// Validate structure for scanning
if (Array.isArray(data)) {
// Array of objects
for (let i = 0; i < data.length; i++) {
const item = data[i];
if (typeof item !== 'object' || item === null) {
builder.addWarning('content', `Item at index ${i} is not an object`, 'INVALID_ITEM_TYPE');
}
}
}
else if (typeof data === 'object' && data !== null) {
// Single object - this is fine
}
else {
builder.addWarning('content', 'JSON should contain objects suitable for scanning', 'INVALID_JSON_TYPE');
}
return (0, Result_1.ok)(true);
}
catch (error) {
return (0, Result_1.err)(new Error(`Invalid JSON: ${error}`));
}
}
async validateNdjsonFile(content, builder) {
try {
const lines = content.split('\n').filter((line) => line.trim() !== '');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
try {
const data = JSON.parse(line);
if (typeof data !== 'object' || data === null) {
builder.addWarning('content', `Line ${i + 1} does not contain a valid object`, 'INVALID_NDJSON_LINE');
}
}
catch (error) {
builder.addError('content', `Line ${i + 1} contains invalid JSON: ${error}`, 'INVALID_JSON_LINE');
}
}
return (0, Result_1.ok)(true);
}
catch (error) {
return (0, Result_1.err)(new Error(`Error validating NDJSON: ${error}`));
}
}
async validateTextFile(content, builder) {
try {
// Check for binary content
if (content.includes('\0')) {
builder.addError('content', 'File appears to contain binary data', 'BINARY_CONTENT');
return (0, Result_1.ok)(false);
}
// Check for extremely long lines (might indicate binary or non-text data)
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
if (lines[i].length > 10000) {
builder.addWarning('content', `Line ${i + 1} is extremely long (${lines[i].length} characters)`, 'LONG_LINE');
}
}
return (0, Result_1.ok)(true);
}
catch (error) {
return (0, Result_1.err)(new Error(`Error validating text file: ${error}`));
}
}
}
exports.InputFileValidatorImpl = InputFileValidatorImpl;