@behavioral-contracts/verify-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +119 -0
  2. package/README.md +694 -0
  3. package/dist/analyze-results.js +253 -0
  4. package/dist/analyzer.d.ts +366 -0
  5. package/dist/analyzer.d.ts.map +1 -0
  6. package/dist/analyzer.js +2592 -0
  7. package/dist/analyzer.js.map +1 -0
  8. package/dist/analyzers/async-error-analyzer.d.ts +72 -0
  9. package/dist/analyzers/async-error-analyzer.d.ts.map +1 -0
  10. package/dist/analyzers/async-error-analyzer.js +243 -0
  11. package/dist/analyzers/async-error-analyzer.js.map +1 -0
  12. package/dist/analyzers/event-listener-analyzer.d.ts +102 -0
  13. package/dist/analyzers/event-listener-analyzer.d.ts.map +1 -0
  14. package/dist/analyzers/event-listener-analyzer.js +253 -0
  15. package/dist/analyzers/event-listener-analyzer.js.map +1 -0
  16. package/dist/analyzers/react-query-analyzer.d.ts +66 -0
  17. package/dist/analyzers/react-query-analyzer.d.ts.map +1 -0
  18. package/dist/analyzers/react-query-analyzer.js +341 -0
  19. package/dist/analyzers/react-query-analyzer.js.map +1 -0
  20. package/dist/analyzers/return-value-analyzer.d.ts +61 -0
  21. package/dist/analyzers/return-value-analyzer.d.ts.map +1 -0
  22. package/dist/analyzers/return-value-analyzer.js +225 -0
  23. package/dist/analyzers/return-value-analyzer.js.map +1 -0
  24. package/dist/code-snippet.d.ts +48 -0
  25. package/dist/code-snippet.d.ts.map +1 -0
  26. package/dist/code-snippet.js +84 -0
  27. package/dist/code-snippet.js.map +1 -0
  28. package/dist/corpus-loader.d.ts +33 -0
  29. package/dist/corpus-loader.d.ts.map +1 -0
  30. package/dist/corpus-loader.js +155 -0
  31. package/dist/corpus-loader.js.map +1 -0
  32. package/dist/fixture-tester.d.ts +28 -0
  33. package/dist/fixture-tester.d.ts.map +1 -0
  34. package/dist/fixture-tester.js +176 -0
  35. package/dist/fixture-tester.js.map +1 -0
  36. package/dist/index.d.ts +6 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +375 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/package-discovery.d.ts +62 -0
  41. package/dist/package-discovery.d.ts.map +1 -0
  42. package/dist/package-discovery.js +299 -0
  43. package/dist/package-discovery.js.map +1 -0
  44. package/dist/reporter.d.ts +43 -0
  45. package/dist/reporter.d.ts.map +1 -0
  46. package/dist/reporter.js +347 -0
  47. package/dist/reporter.js.map +1 -0
  48. package/dist/reporters/benchmarking.d.ts +70 -0
  49. package/dist/reporters/benchmarking.d.ts.map +1 -0
  50. package/dist/reporters/benchmarking.js +191 -0
  51. package/dist/reporters/benchmarking.js.map +1 -0
  52. package/dist/reporters/d3-visualizer.d.ts +40 -0
  53. package/dist/reporters/d3-visualizer.d.ts.map +1 -0
  54. package/dist/reporters/d3-visualizer.js +803 -0
  55. package/dist/reporters/d3-visualizer.js.map +1 -0
  56. package/dist/reporters/health-score.d.ts +33 -0
  57. package/dist/reporters/health-score.d.ts.map +1 -0
  58. package/dist/reporters/health-score.js +149 -0
  59. package/dist/reporters/health-score.js.map +1 -0
  60. package/dist/reporters/index.d.ts +11 -0
  61. package/dist/reporters/index.d.ts.map +1 -0
  62. package/dist/reporters/index.js +11 -0
  63. package/dist/reporters/index.js.map +1 -0
  64. package/dist/reporters/package-breakdown.d.ts +48 -0
  65. package/dist/reporters/package-breakdown.d.ts.map +1 -0
  66. package/dist/reporters/package-breakdown.js +185 -0
  67. package/dist/reporters/package-breakdown.js.map +1 -0
  68. package/dist/reporters/positive-evidence.d.ts +42 -0
  69. package/dist/reporters/positive-evidence.d.ts.map +1 -0
  70. package/dist/reporters/positive-evidence.js +436 -0
  71. package/dist/reporters/positive-evidence.js.map +1 -0
  72. package/dist/tsconfig-generator.d.ts +17 -0
  73. package/dist/tsconfig-generator.d.ts.map +1 -0
  74. package/dist/tsconfig-generator.js +107 -0
  75. package/dist/tsconfig-generator.js.map +1 -0
  76. package/dist/types.d.ts +298 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +5 -0
  79. package/dist/types.js.map +1 -0
  80. package/package.json +59 -0
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Phase 6.1: Analyze baseline test results
4
+ * Parses audit JSON files and generates summary report
5
+ */
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ const OUTPUT_DATE = process.argv[2] || new Date().toISOString().split('T')[0].replace(/-/g, '');
12
+ const OUTPUT_DIR = path.join(path.dirname(__dirname), 'output', OUTPUT_DATE);
13
+ function parseAuditFile(filePath) {
14
+ try {
15
+ const content = fs.readFileSync(filePath, 'utf-8');
16
+ return JSON.parse(content);
17
+ }
18
+ catch (err) {
19
+ console.error(`Failed to parse ${filePath}:`, err);
20
+ return null;
21
+ }
22
+ }
23
+ function analyzePackage(packageName) {
24
+ const safeName = packageName.replace(/\//g, '-');
25
+ const auditPath = path.join(OUTPUT_DIR, `${safeName}-audit.json`);
26
+ const result = {
27
+ name: packageName,
28
+ status: 'ERROR',
29
+ totalViolations: 0,
30
+ properViolations: 0,
31
+ missingViolations: 0,
32
+ instanceViolations: 0,
33
+ violations: [],
34
+ };
35
+ if (!fs.existsSync(auditPath)) {
36
+ result.error = 'Audit file not found';
37
+ return result;
38
+ }
39
+ const audit = parseAuditFile(auditPath);
40
+ if (!audit) {
41
+ result.error = 'Failed to parse audit file';
42
+ return result;
43
+ }
44
+ result.totalViolations = audit.violations.length;
45
+ result.violations = audit.violations;
46
+ // Categorize violations by fixture file
47
+ for (const violation of audit.violations) {
48
+ const filename = path.basename(violation.file);
49
+ if (filename === 'proper-error-handling.ts') {
50
+ result.properViolations++;
51
+ }
52
+ else if (filename === 'missing-error-handling.ts') {
53
+ result.missingViolations++;
54
+ }
55
+ else if (filename === 'instance-usage.ts') {
56
+ result.instanceViolations++;
57
+ }
58
+ }
59
+ // PASS if no violations in proper-error-handling.ts
60
+ result.status = result.properViolations === 0 ? 'PASS' : 'FAIL';
61
+ return result;
62
+ }
63
+ function generateReport(results) {
64
+ const passed = results.filter(r => r.status === 'PASS');
65
+ const failed = results.filter(r => r.status === 'FAIL');
66
+ const errors = results.filter(r => r.status === 'ERROR');
67
+ let report = `# Phase 6.1: Baseline Fixture Testing - Complete Results
68
+
69
+ **Date:** ${new Date().toISOString().split('T')[0]}
70
+ **Output Directory:** \`verify-cli/output/${OUTPUT_DATE}/\`
71
+
72
+ ---
73
+
74
+ ## Summary
75
+
76
+ - **Total Packages:** ${results.length}
77
+ - **Passed:** ${passed.length} (${((passed.length / results.length) * 100).toFixed(1)}%)
78
+ - **Failed:** ${failed.length} (${((failed.length / results.length) * 100).toFixed(1)}%)
79
+ - **Errors:** ${errors.length}
80
+
81
+ **Pass Criteria:** 0 violations in \`proper-error-handling.ts\`
82
+
83
+ ---
84
+
85
+ ## Overall Results
86
+
87
+ ### ✅ Passed Packages (${passed.length})
88
+
89
+ ${passed.length > 0 ? passed.map(r => `- **${r.name}** - ${r.totalViolations} total violations (${r.missingViolations} missing, ${r.instanceViolations} instance)`).join('\n') : '_None_'}
90
+
91
+ ### ❌ Failed Packages (${failed.length})
92
+
93
+ ${failed.length > 0 ? failed.map(r => `- **${r.name}** - ${r.properViolations} violations in proper-error-handling.ts`).join('\n') : '_None_'}
94
+
95
+ ### ⚠️ Error Packages (${errors.length})
96
+
97
+ ${errors.length > 0 ? errors.map(r => `- **${r.name}** - ${r.error}`).join('\n') : '_None_'}
98
+
99
+ ---
100
+
101
+ ## Detailed Results by Package
102
+
103
+ `;
104
+ // Sort by status then name
105
+ const sortedResults = results.sort((a, b) => {
106
+ if (a.status !== b.status) {
107
+ const order = { PASS: 0, FAIL: 1, ERROR: 2 };
108
+ return order[a.status] - order[b.status];
109
+ }
110
+ return a.name.localeCompare(b.name);
111
+ });
112
+ for (const result of sortedResults) {
113
+ const emoji = result.status === 'PASS' ? '✅' : result.status === 'FAIL' ? '❌' : '⚠️';
114
+ report += `### ${emoji} ${result.name}\n\n`;
115
+ report += `**Status:** ${result.status}\n\n`;
116
+ if (result.status === 'ERROR') {
117
+ report += `**Error:** ${result.error}\n\n`;
118
+ report += `---\n\n`;
119
+ continue;
120
+ }
121
+ report += `**Violations:**\n`;
122
+ report += `- Total: ${result.totalViolations}\n`;
123
+ report += `- In proper-error-handling.ts: ${result.properViolations}\n`;
124
+ report += `- In missing-error-handling.ts: ${result.missingViolations}\n`;
125
+ report += `- In instance-usage.ts: ${result.instanceViolations}\n\n`;
126
+ if (result.properViolations > 0) {
127
+ report += `**Issues in proper-error-handling.ts:**\n\n`;
128
+ const properViolations = result.violations.filter(v => path.basename(v.file) === 'proper-error-handling.ts');
129
+ for (const v of properViolations) {
130
+ report += `- Line ${v.line}: ${v.message}\n`;
131
+ report += ` - Package: \`${v.package}\`, Method: \`${v.method}\`\n`;
132
+ report += ` - Severity: ${v.severity}\n\n`;
133
+ }
134
+ }
135
+ report += `---\n\n`;
136
+ }
137
+ // Pattern analysis
138
+ report += `## Pattern Analysis\n\n`;
139
+ const failurePatterns = {};
140
+ for (const result of failed) {
141
+ for (const v of result.violations) {
142
+ if (path.basename(v.file) === 'proper-error-handling.ts') {
143
+ const key = `${v.package}:${v.method}`;
144
+ failurePatterns[key] = (failurePatterns[key] || 0) + 1;
145
+ }
146
+ }
147
+ }
148
+ if (Object.keys(failurePatterns).length > 0) {
149
+ report += `### Common False Positives\n\n`;
150
+ const sorted = Object.entries(failurePatterns).sort((a, b) => b[1] - a[1]);
151
+ for (const [pattern, count] of sorted) {
152
+ report += `- \`${pattern}\` - ${count} occurrence(s)\n`;
153
+ }
154
+ report += `\n`;
155
+ }
156
+ // Recommendations
157
+ report += `## Recommendations\n\n`;
158
+ if (failed.length > 0) {
159
+ report += `### High Priority Fixes\n\n`;
160
+ report += `The following packages have violations in proper-error-handling.ts and need investigation:\n\n`;
161
+ for (const result of failed.slice(0, 10)) {
162
+ report += `1. **${result.name}**\n`;
163
+ report += ` - Review proper-error-handling.ts fixture\n`;
164
+ report += ` - Verify error handling patterns are actually proper\n`;
165
+ report += ` - Update contract or analyzer if needed\n\n`;
166
+ }
167
+ if (failed.length > 10) {
168
+ report += `_...and ${failed.length - 10} more packages_\n\n`;
169
+ }
170
+ }
171
+ report += `### Next Steps\n\n`;
172
+ report += `1. **Investigate Failed Packages**: Review each package with proper-error-handling.ts violations\n`;
173
+ report += `2. **Fix Contracts**: Update contracts to match actual proper patterns\n`;
174
+ report += `3. **Fix Analyzer**: Improve analyzer detection logic if needed\n`;
175
+ report += `4. **Fix Fixtures**: Correct fixture code if patterns are actually improper\n`;
176
+ report += `5. **Re-test**: Run Phase 6.1 again after fixes\n\n`;
177
+ report += `---\n\n`;
178
+ report += `**Generated:** ${new Date().toISOString()}\n`;
179
+ return report;
180
+ }
181
+ async function main() {
182
+ console.log(`Analyzing results in: ${OUTPUT_DIR}`);
183
+ const packages = [
184
+ "axios",
185
+ "cloudinary",
186
+ "discord.js",
187
+ "express",
188
+ "firebase-admin",
189
+ "ioredis",
190
+ "mongodb",
191
+ "mongoose",
192
+ "openai",
193
+ "pg",
194
+ "react-hook-form",
195
+ "redis",
196
+ "square",
197
+ "stripe",
198
+ "twilio",
199
+ "typescript",
200
+ "zod",
201
+ "@anthropic-ai/sdk",
202
+ "@aws-sdk/client-s3",
203
+ "@clerk/nextjs",
204
+ "@octokit/rest",
205
+ "@prisma/client",
206
+ "@sendgrid/mail",
207
+ "@slack/web-api",
208
+ "@supabase/supabase-js",
209
+ "@tanstack/react-query",
210
+ "bullmq",
211
+ "@vercel/postgres",
212
+ "drizzle-orm",
213
+ "socket.io",
214
+ "joi",
215
+ "ethers",
216
+ "fastify",
217
+ "next",
218
+ "dotenv",
219
+ "jsonwebtoken",
220
+ "bcrypt",
221
+ "multer",
222
+ "helmet",
223
+ "cors",
224
+ "winston",
225
+ "passport",
226
+ "knex",
227
+ "typeorm",
228
+ "graphql",
229
+ "uuid",
230
+ "date-fns",
231
+ "@nestjs/common",
232
+ "@hapi/hapi",
233
+ ];
234
+ const results = [];
235
+ for (const pkg of packages) {
236
+ const result = analyzePackage(pkg);
237
+ results.push(result);
238
+ const emoji = result.status === 'PASS' ? '✅' : result.status === 'FAIL' ? '❌' : '⚠️';
239
+ console.log(`${emoji} ${pkg}: ${result.status} (${result.properViolations} proper violations)`);
240
+ }
241
+ console.log(`\nGenerating report...`);
242
+ const report = generateReport(results);
243
+ const reportPath = path.join(__dirname, '../dev-notes/findings/phase6-baseline-complete-results.md');
244
+ // Ensure directory exists
245
+ fs.mkdirSync(path.dirname(reportPath), { recursive: true });
246
+ fs.writeFileSync(reportPath, report, 'utf-8');
247
+ console.log(`\nReport saved to: ${reportPath}`);
248
+ console.log(`\nSummary:`);
249
+ console.log(` Passed: ${results.filter(r => r.status === 'PASS').length}`);
250
+ console.log(` Failed: ${results.filter(r => r.status === 'FAIL').length}`);
251
+ console.log(` Errors: ${results.filter(r => r.status === 'ERROR').length}`);
252
+ }
253
+ main().catch(console.error);
@@ -0,0 +1,366 @@
1
+ /**
2
+ * AST Analyzer - uses TypeScript Compiler API to detect behavioral contract violations
3
+ */
4
+ import type { PackageContract, Violation, AnalyzerConfig } from './types.js';
5
+ /**
6
+ * Main analyzer that coordinates the verification process
7
+ */
8
+ export declare class Analyzer {
9
+ private program;
10
+ private typeChecker;
11
+ private contracts;
12
+ private violations;
13
+ private projectRoot;
14
+ private includeTests;
15
+ private typeToPackage;
16
+ private classToPackage;
17
+ private factoryToPackage;
18
+ private awaitPatternToPackage;
19
+ constructor(config: AnalyzerConfig, contracts: Map<string, PackageContract>);
20
+ /**
21
+ * Builds detection maps from contract definitions
22
+ * This replaces hardcoded mappings with data-driven approach
23
+ */
24
+ private buildDetectionMaps;
25
+ /**
26
+ * Analyzes all files in the program and returns violations
27
+ */
28
+ analyze(): Violation[];
29
+ /**
30
+ * Extracts all package imports from a source file
31
+ */
32
+ private extractImports;
33
+ /**
34
+ * Determines if a file is a test file based on common patterns
35
+ * Test files are excluded by default because:
36
+ * - Tests intentionally expect errors to be thrown
37
+ * - Test frameworks (Jest, Vitest) handle errors automatically
38
+ * - 90%+ of test violations are false positives
39
+ */
40
+ private isTestFile;
41
+ /**
42
+ * Analyzes a single source file
43
+ */
44
+ private analyzeFile;
45
+ /**
46
+ * Detects async functions with unprotected await expressions
47
+ */
48
+ private detectAsyncErrors;
49
+ /**
50
+ * Detects functions with unprotected return value error checks
51
+ */
52
+ private detectReturnValueErrors;
53
+ /**
54
+ * Creates a violation for unprotected return value error checks
55
+ */
56
+ private createReturnValueViolation;
57
+ /**
58
+ * Detects instances missing required event listeners
59
+ */
60
+ private detectEventListenerErrors;
61
+ /**
62
+ * Creates a violation for missing required event listeners
63
+ */
64
+ private createEventListenerViolation;
65
+ /**
66
+ * Creates a violation for unprotected async calls
67
+ */
68
+ private createAsyncErrorViolation;
69
+ /**
70
+ * Detects which package is being called from the await expression text
71
+ * Uses dynamic pattern matching from contract detection rules
72
+ */
73
+ private detectPackageFromAwaitText;
74
+ /**
75
+ * Detects package using TypeScript's type system (MOST ACCURATE METHOD)
76
+ *
77
+ * Uses TypeScript's type checker to determine which package a variable belongs to
78
+ * based on its type, eliminating false positives from pattern matching.
79
+ *
80
+ * Steps:
81
+ * 1. Get the type of the object the method is called on
82
+ * 2. Extract the type name (e.g., "TextChannel", "Model")
83
+ * 3. Look up which package defines this type
84
+ *
85
+ * Example:
86
+ * const channel: TextChannel = getChannel();
87
+ * await channel.createInvite();
88
+ *
89
+ * → channel has type TextChannel
90
+ * → TextChannel is from discord.js (per contract detection.type_names)
91
+ * → Return "discord.js"
92
+ *
93
+ * This eliminates false positives from pattern overlap:
94
+ * - mongoose ".create" won't match discord.js ".createInvite()"
95
+ * - Each type uniquely identifies its package
96
+ *
97
+ * @param node The call expression node from the AST
98
+ * @returns Package name if type is recognized, null otherwise
99
+ */
100
+ private detectPackageFromType;
101
+ /**
102
+ * Extracts package name from a TypeScript type
103
+ * Handles edge cases: generics, aliases, unions, intersections
104
+ *
105
+ * Edge cases:
106
+ * 1. Type aliases: import { Client as Bot } from 'discord.js' → resolve "Bot" to "Client"
107
+ * 2. Generic types: Model<User> → extract base type "Model"
108
+ * 3. Union types: TextChannel | VoiceChannel → try all types in union
109
+ * 4. Intersection types: A & B → try all types in intersection
110
+ *
111
+ * @param type The TypeScript type to analyze
112
+ * @returns Package name if recognized, null otherwise
113
+ */
114
+ private extractPackageFromType;
115
+ /**
116
+ * Detects package from tracked instance (most accurate method)
117
+ * Extracts instance name from await expression and checks if it's tracked
118
+ *
119
+ * Examples:
120
+ * "await this.catModel.find()" → extracts "catModel" → checks if tracked
121
+ * "await user.save()" → extracts "user" → checks if tracked
122
+ * "await Model.create()" → extracts "Model" → checks if tracked
123
+ */
124
+ private detectPackageFromTrackedInstance;
125
+ /**
126
+ * Creates a violation for a detected package
127
+ * Extracted from createAsyncErrorViolation to reduce duplication
128
+ */
129
+ private createViolationForPackage;
130
+ /**
131
+ * Creates a violation for empty or ineffective catch blocks
132
+ */
133
+ private createEmptyCatchViolation;
134
+ /**
135
+ * Analyzes a call expression to see if it violates any contracts
136
+ */
137
+ private analyzeCallExpression;
138
+ /**
139
+ * Analyzes React Query hooks for error handling
140
+ */
141
+ private analyzeReactQueryHook;
142
+ /**
143
+ * Checks if a mutation variable is used with mutateAsync in a try-catch block
144
+ */
145
+ private checkMutateAsyncInTryCatch;
146
+ /**
147
+ * Checks a React Query postcondition and returns a violation if not met
148
+ */
149
+ private checkReactQueryPostcondition;
150
+ /**
151
+ * Walks up a property access chain and returns components
152
+ * Example: prisma.user.create → { root: 'prisma', chain: ['user'], method: 'create' }
153
+ * Example: axios.get → { root: 'axios', chain: [], method: 'get' }
154
+ * Example: openai.chat.completions.create → { root: 'openai', chain: ['chat', 'completions'], method: 'create' }
155
+ */
156
+ private walkPropertyAccessChain;
157
+ /**
158
+ * Extracts call site information from a call expression
159
+ */
160
+ private extractCallSite;
161
+ /**
162
+ * Extracts the instance variable name from a call expression
163
+ * e.g., for "axiosInstance.get(...)" returns "axiosInstance"
164
+ */
165
+ private extractInstanceVariable;
166
+ /**
167
+ * Resolves which package a function comes from by looking at imports
168
+ */
169
+ private resolvePackageFromImports;
170
+ /**
171
+ * Extracts package name from new expressions
172
+ * Examples: new PrismaClient() → "@prisma/client"
173
+ * new Stripe(key) → "stripe"
174
+ * new OpenAI(config) → "openai"
175
+ */
176
+ private extractPackageFromNewExpression;
177
+ /**
178
+ * Extracts package name from axios.create() call
179
+ * Returns the package name if this is an axios.create() or similar factory call
180
+ */
181
+ private extractPackageFromAxiosCreate;
182
+ /**
183
+ * Extracts package name from generic factory methods defined in detection rules
184
+ * Examples:
185
+ * mongoose.model('User', schema) → "mongoose" (if factory_methods includes "model")
186
+ * prisma.client() → "prisma" (if factory_methods includes "client")
187
+ */
188
+ private extractPackageFromGenericFactory;
189
+ /**
190
+ * Extracts package name from schema factory methods (z.object(), z.string(), etc.)
191
+ * Returns the package name if this is a schema creation call
192
+ */
193
+ private extractPackageFromSchemaFactory;
194
+ /**
195
+ * Analyzes what error handling exists around a call site
196
+ */
197
+ /**
198
+ * Checks if a call is within an Express route that has error middleware
199
+ * Pattern: app.post('/route', middleware, errorHandler)
200
+ * where errorHandler has 4 parameters (err, req, res, next)
201
+ */
202
+ private isWithinExpressErrorMiddleware;
203
+ /**
204
+ * Checks if a call is within a NestJS controller method
205
+ * NestJS controllers use exception filters to handle errors globally
206
+ */
207
+ private isWithinNestJSController;
208
+ /**
209
+ * Checks if a node has a specific decorator
210
+ */
211
+ private hasDecorator;
212
+ /**
213
+ * Finds a function definition by name in the source file
214
+ */
215
+ private findFunctionDefinition;
216
+ private analyzeErrorHandling;
217
+ /**
218
+ * Checks if a node is inside a try-catch block
219
+ */
220
+ private isInTryCatch;
221
+ /**
222
+ * Checks if a node is inside a callback/arrow function that contains a try-catch block
223
+ * Handles fastify routes, socket.io event handlers, etc.
224
+ */
225
+ private isInsideCallbackWithTryCatch;
226
+ /**
227
+ * Checks if a node is inside a try block within a function
228
+ */
229
+ private isNodeInsideTryBlock;
230
+ /**
231
+ * Checks if a function contains a try-catch block in its body
232
+ */
233
+ private functionContainsTryCatch;
234
+ /**
235
+ * Checks if this is a route/event handler registration with try-catch in the handler
236
+ * Handles: fastify routes (app.get, app.post), socket.io events (io.on, socket.on)
237
+ * Pattern: app.get('/route', async (req, res) => { try { ... } catch { ... } })
238
+ */
239
+ private isRouteHandlerWithTryCatch;
240
+ /**
241
+ * Checks if a function contains await expressions
242
+ */
243
+ private functionContainsAwait;
244
+ /**
245
+ * Checks if there's a finally block that cleans up resources
246
+ * Pattern: const client = await pool.connect(); ... finally { client.release(); }
247
+ */
248
+ private hasFinallyCleanup;
249
+ /**
250
+ * Checks if a finally block calls a cleanup method on a variable
251
+ */
252
+ private finallyBlockCallsCleanup;
253
+ /**
254
+ * Checks if a call uses callback-based error handling
255
+ * Pattern: callback((error, result) => { if (error) return reject(error); })
256
+ */
257
+ private hasCallbackErrorHandling;
258
+ /**
259
+ * Checks if a callback function body checks the error parameter
260
+ * Looks for patterns like: if (error) or if (err)
261
+ */
262
+ private callbackChecksErrorParam;
263
+ /**
264
+ * Finds the enclosing catch clause for a node
265
+ */
266
+ private findEnclosingCatchClause;
267
+ /**
268
+ * Checks if a catch block checks error.response exists
269
+ */
270
+ private catchChecksResponseExists;
271
+ /**
272
+ * Checks if an expression checks for response property
273
+ */
274
+ private expressionChecksResponse;
275
+ /**
276
+ * Checks if a catch block checks status codes
277
+ */
278
+ private catchChecksStatusCode;
279
+ /**
280
+ * Extracts which status codes are explicitly handled
281
+ */
282
+ private extractHandledStatusCodes;
283
+ /**
284
+ * Checks if catch block has retry logic
285
+ */
286
+ private catchHasRetryLogic;
287
+ /**
288
+ * Checks if a postcondition is violated at a call site
289
+ */
290
+ private checkPostcondition;
291
+ /**
292
+ * Checks if a function call result has proper null handling
293
+ * Used for Clerk functions that return null when not authenticated
294
+ */
295
+ private checkNullHandling;
296
+ /**
297
+ * Checks if a condition is a null check for the given variables
298
+ */
299
+ private isNullCheckCondition;
300
+ /**
301
+ * Finds the containing function/method for a node
302
+ */
303
+ private findContainingFunction;
304
+ /**
305
+ * Checks if a function call has hardcoded credentials (string literals)
306
+ * vs environment variables (process.env.*)
307
+ *
308
+ * Returns true if hardcoded credentials are detected (violation)
309
+ * Returns false if credentials come from environment variables (valid)
310
+ */
311
+ private checkHardcodedCredentials;
312
+ /**
313
+ * Finds a variable declaration in the scope of the given node
314
+ */
315
+ private findVariableDeclaration;
316
+ /**
317
+ * Checks if a specific file exists in the project
318
+ * Tries multiple possible locations (root, src/, etc.)
319
+ *
320
+ * @param fileName - The file name to search for (e.g., 'middleware.ts')
321
+ * @param variations - Optional variations of the file name (e.g., ['middleware.ts', 'middleware.js'])
322
+ * @returns The full path if found, null otherwise
323
+ */
324
+ private checkFileExists;
325
+ /**
326
+ * Checks if a file imports and exports specific patterns
327
+ *
328
+ * @param filePath - Absolute path to the file to check
329
+ * @param importPattern - Object specifying what to look for in imports
330
+ * @param exportPattern - Object specifying what to look for in exports
331
+ * @returns Object with hasImport and hasExport booleans
332
+ */
333
+ private checkFileImportsAndExports;
334
+ /**
335
+ * Checks if middleware.ts exists and properly exports clerkMiddleware
336
+ * This is specific to @clerk/nextjs middleware setup
337
+ *
338
+ * @returns true if middleware is properly configured, false otherwise
339
+ */
340
+ private checkClerkMiddlewareExists;
341
+ /**
342
+ * Creates a violation object
343
+ */
344
+ private createViolation;
345
+ /**
346
+ * Gets statistics about the analysis run
347
+ */
348
+ getStats(): {
349
+ filesAnalyzed: number;
350
+ contractsApplied: number;
351
+ };
352
+ /**
353
+ * Analyzes S3 send() calls to detect command type
354
+ * Pattern: s3Client.send(new GetObjectCommand(...))
355
+ */
356
+ private analyzeS3SendCall;
357
+ /**
358
+ * Analyzes an S3 command call and creates violations if needed
359
+ */
360
+ private analyzeS3Command;
361
+ /**
362
+ * Maps S3 command types to their corresponding postcondition IDs
363
+ */
364
+ private mapS3CommandToPostcondition;
365
+ }
366
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}