321 lines
10 KiB
JavaScript
321 lines
10 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Quality Assurance Validation Script
|
|
* Validates HIPAA compliance, authentication requirements, and parameter accuracy
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* Load API documentation and current endpoints
|
|
*/
|
|
function loadData() {
|
|
try {
|
|
// Load API documentation
|
|
const apiDocsPath = path.join(process.cwd(), 'complete-api-parameters.json');
|
|
const apiDocs = JSON.parse(fs.readFileSync(apiDocsPath, 'utf8'));
|
|
|
|
// Load current endpoints
|
|
const endpointsPath = path.join(process.cwd(), 'src/config/endpoints.js');
|
|
const endpointsContent = fs.readFileSync(endpointsPath, 'utf8');
|
|
|
|
return { apiDocs, endpointsContent };
|
|
} catch (error) {
|
|
console.error('❌ Error loading data:', error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate HIPAA compliance categorization
|
|
*/
|
|
function validateHIPAACompliance(endpointsContent) {
|
|
console.log('🏥 Validating HIPAA Compliance...\n');
|
|
|
|
const hipaaViolations = [];
|
|
const clinicalPatterns = [
|
|
'/emr/',
|
|
'/patient/',
|
|
'/medical',
|
|
'/appointment',
|
|
'/prescription',
|
|
'/diagnosis',
|
|
'/treatment',
|
|
'/vitals',
|
|
'/lab',
|
|
'/document'
|
|
];
|
|
|
|
// Check PUBLIC endpoints for potential HIPAA violations
|
|
const publicMatch = endpointsContent.match(/export const PUBLIC_ENDPOINTS = \[([\s\S]*?)\];/);
|
|
if (publicMatch) {
|
|
const publicContent = publicMatch[1];
|
|
|
|
clinicalPatterns.forEach(pattern => {
|
|
const regex = new RegExp(`path:\\s*["'][^"']*${pattern}[^"']*["']`, 'gi');
|
|
const matches = publicContent.match(regex);
|
|
|
|
if (matches) {
|
|
matches.forEach(match => {
|
|
const pathMatch = match.match(/path:\s*["']([^"']+)["']/);
|
|
if (pathMatch) {
|
|
const path = pathMatch[1];
|
|
// Allow certain public endpoints that are safe
|
|
if (!path.includes('/login') && !path.includes('/register') && !path.includes('/forgot-password')) {
|
|
hipaaViolations.push({
|
|
type: 'HIPAA_VIOLATION',
|
|
path,
|
|
issue: `Clinical endpoint in PUBLIC category may violate HIPAA`,
|
|
recommendation: 'Move to PROVIDER or PATIENT category'
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
console.log(`✅ HIPAA Compliance Check: ${hipaaViolations.length} potential violations found\n`);
|
|
return hipaaViolations;
|
|
}
|
|
|
|
/**
|
|
* Validate authentication requirements
|
|
*/
|
|
function validateAuthentication(endpointsContent) {
|
|
console.log('🔐 Validating Authentication Requirements...\n');
|
|
|
|
const authIssues = [];
|
|
|
|
// Check for sensitive endpoints in wrong categories
|
|
const sensitivePatterns = {
|
|
'/emr/': 'PROVIDER',
|
|
'/admin/': 'ADMIN',
|
|
'/patient/': 'PATIENT',
|
|
'/partner/': 'PARTNER',
|
|
'/affiliate/': 'AFFILIATE',
|
|
'/network/': 'NETWORK'
|
|
};
|
|
|
|
Object.entries(sensitivePatterns).forEach(([pattern, expectedAuth]) => {
|
|
const regex = new RegExp(`export const PUBLIC_ENDPOINTS = \\[([\\s\\S]*?)\\];`);
|
|
const publicMatch = endpointsContent.match(regex);
|
|
|
|
if (publicMatch) {
|
|
const publicContent = publicMatch[1];
|
|
const pathRegex = new RegExp(`path:\\s*["'][^"']*${pattern}[^"']*["']`, 'gi');
|
|
const matches = publicContent.match(pathRegex);
|
|
|
|
if (matches) {
|
|
matches.forEach(match => {
|
|
const pathMatch = match.match(/path:\s*["']([^"']+)["']/);
|
|
if (pathMatch) {
|
|
const path = pathMatch[1];
|
|
// Allow login/register endpoints
|
|
if (!path.includes('/login') && !path.includes('/register')) {
|
|
authIssues.push({
|
|
type: 'AUTH_MISMATCH',
|
|
path,
|
|
currentAuth: 'PUBLIC',
|
|
expectedAuth,
|
|
issue: `Sensitive endpoint should require ${expectedAuth} authentication`
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log(`✅ Authentication Validation: ${authIssues.length} issues found\n`);
|
|
return authIssues;
|
|
}
|
|
|
|
/**
|
|
* Validate parameter accuracy
|
|
*/
|
|
function validateParameterAccuracy(apiDocs, endpointsContent) {
|
|
console.log('📋 Validating Parameter Accuracy...\n');
|
|
|
|
const parameterIssues = [];
|
|
let checkedEndpoints = 0;
|
|
let accurateEndpoints = 0;
|
|
|
|
// Sample validation for key endpoints
|
|
const keyEndpoints = apiDocs.filter(endpoint =>
|
|
endpoint.path.includes('/emr/') ||
|
|
endpoint.path.includes('/patient/') ||
|
|
endpoint.path.includes('/appointment')
|
|
).slice(0, 10); // Check first 10 for performance
|
|
|
|
keyEndpoints.forEach(apiEndpoint => {
|
|
checkedEndpoints++;
|
|
|
|
// Find corresponding endpoint in configuration
|
|
const pathRegex = new RegExp(`path:\\s*["']${apiEndpoint.path.replace(/[{}]/g, '\\$&')}["']`, 'g');
|
|
const match = endpointsContent.match(pathRegex);
|
|
|
|
if (match) {
|
|
// Extract parameter block for this endpoint
|
|
const endpointRegex = new RegExp(
|
|
`\\{[\\s\\S]*?path:\\s*["']${apiEndpoint.path.replace(/[{}]/g, '\\$&')}["'][\\s\\S]*?parameters:\\s*\\{([\\s\\S]*?)\\}[\\s\\S]*?\\}`,
|
|
'g'
|
|
);
|
|
const endpointMatch = endpointsContent.match(endpointRegex);
|
|
|
|
if (endpointMatch) {
|
|
const parametersText = endpointMatch[0];
|
|
|
|
// Check if API parameters are represented
|
|
const apiParams = [
|
|
...apiEndpoint.parameters.path,
|
|
...apiEndpoint.parameters.query,
|
|
...apiEndpoint.parameters.body
|
|
];
|
|
|
|
let hasAllParams = true;
|
|
const missingParams = [];
|
|
|
|
apiParams.forEach(param => {
|
|
const paramRegex = new RegExp(`${param.name}:\\s*\\{`, 'g');
|
|
if (!parametersText.match(paramRegex)) {
|
|
hasAllParams = false;
|
|
missingParams.push(param.name);
|
|
}
|
|
});
|
|
|
|
if (hasAllParams && apiParams.length > 0) {
|
|
accurateEndpoints++;
|
|
} else if (missingParams.length > 0) {
|
|
parameterIssues.push({
|
|
type: 'MISSING_PARAMETERS',
|
|
path: apiEndpoint.path,
|
|
missingParams,
|
|
issue: `Missing parameters: ${missingParams.join(', ')}`
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
const accuracy = checkedEndpoints > 0 ? ((accurateEndpoints / checkedEndpoints) * 100).toFixed(1) : 0;
|
|
console.log(`✅ Parameter Accuracy: ${accuracy}% (${accurateEndpoints}/${checkedEndpoints} endpoints)\n`);
|
|
|
|
return { parameterIssues, accuracy, checkedEndpoints, accurateEndpoints };
|
|
}
|
|
|
|
/**
|
|
* Generate quality assurance report
|
|
*/
|
|
function generateQAReport(hipaaViolations, authIssues, parameterValidation) {
|
|
const report = {
|
|
timestamp: new Date().toISOString(),
|
|
summary: {
|
|
hipaaCompliance: {
|
|
status: hipaaViolations.length === 0 ? 'PASS' : 'NEEDS_ATTENTION',
|
|
violations: hipaaViolations.length
|
|
},
|
|
authentication: {
|
|
status: authIssues.length === 0 ? 'PASS' : 'NEEDS_ATTENTION',
|
|
issues: authIssues.length
|
|
},
|
|
parameterAccuracy: {
|
|
status: parameterValidation.accuracy >= 90 ? 'PASS' : 'NEEDS_IMPROVEMENT',
|
|
accuracy: parameterValidation.accuracy + '%',
|
|
checkedEndpoints: parameterValidation.checkedEndpoints,
|
|
accurateEndpoints: parameterValidation.accurateEndpoints
|
|
}
|
|
},
|
|
issues: {
|
|
hipaaViolations,
|
|
authIssues,
|
|
parameterIssues: parameterValidation.parameterIssues
|
|
},
|
|
recommendations: []
|
|
};
|
|
|
|
// Generate recommendations
|
|
if (hipaaViolations.length > 0) {
|
|
report.recommendations.push({
|
|
priority: 'HIGH',
|
|
category: 'HIPAA Compliance',
|
|
action: `Review and recategorize ${hipaaViolations.length} endpoints that may violate HIPAA requirements`
|
|
});
|
|
}
|
|
|
|
if (authIssues.length > 0) {
|
|
report.recommendations.push({
|
|
priority: 'HIGH',
|
|
category: 'Authentication',
|
|
action: `Fix authentication requirements for ${authIssues.length} sensitive endpoints`
|
|
});
|
|
}
|
|
|
|
if (parameterValidation.accuracy < 90) {
|
|
report.recommendations.push({
|
|
priority: 'MEDIUM',
|
|
category: 'Parameter Accuracy',
|
|
action: `Improve parameter mapping accuracy from ${parameterValidation.accuracy}% to 90%+`
|
|
});
|
|
}
|
|
|
|
if (report.recommendations.length === 0) {
|
|
report.recommendations.push({
|
|
priority: 'LOW',
|
|
category: 'Maintenance',
|
|
action: 'All quality checks passed. Continue monitoring for compliance.'
|
|
});
|
|
}
|
|
|
|
return report;
|
|
}
|
|
|
|
/**
|
|
* Main validation function
|
|
*/
|
|
function performQualityAssurance() {
|
|
console.log('🔍 Starting Quality Assurance Validation...\n');
|
|
|
|
// Load data
|
|
const { apiDocs, endpointsContent } = loadData();
|
|
console.log(`📊 Loaded ${apiDocs.length} API endpoints for validation\n`);
|
|
|
|
// Perform validations
|
|
const hipaaViolations = validateHIPAACompliance(endpointsContent);
|
|
const authIssues = validateAuthentication(endpointsContent);
|
|
const parameterValidation = validateParameterAccuracy(apiDocs, endpointsContent);
|
|
|
|
// Generate report
|
|
console.log('📋 Generating Quality Assurance Report...');
|
|
const report = generateQAReport(hipaaViolations, authIssues, parameterValidation);
|
|
|
|
// Save report
|
|
const reportPath = path.join(process.cwd(), 'quality-assurance-report.json');
|
|
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
console.log(`✅ Report saved to: ${reportPath}\n`);
|
|
|
|
// Display summary
|
|
console.log('📈 QUALITY ASSURANCE SUMMARY:');
|
|
console.log(`HIPAA Compliance: ${report.summary.hipaaCompliance.status} (${report.summary.hipaaCompliance.violations} violations)`);
|
|
console.log(`Authentication: ${report.summary.authentication.status} (${report.summary.authentication.issues} issues)`);
|
|
console.log(`Parameter Accuracy: ${report.summary.parameterAccuracy.status} (${report.summary.parameterAccuracy.accuracy})`);
|
|
|
|
console.log('\n🎯 RECOMMENDATIONS:');
|
|
report.recommendations.forEach(rec => {
|
|
const icon = rec.priority === 'HIGH' ? '🔴' : rec.priority === 'MEDIUM' ? '🟡' : '🟢';
|
|
console.log(`${icon} ${rec.action} (${rec.priority} priority)`);
|
|
});
|
|
|
|
console.log('\n✅ Quality Assurance validation complete!');
|
|
return report;
|
|
}
|
|
|
|
// Run if called directly
|
|
if (process.argv[1] && process.argv[1].endsWith('quality-assurance-validation.js')) {
|
|
performQualityAssurance();
|
|
}
|
|
|
|
export { performQualityAssurance };
|