@soulbatical/tetra-dev-toolkit 1.1.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.
- package/README.md +312 -0
- package/bin/vca-audit.js +90 -0
- package/bin/vca-dev-token.js +39 -0
- package/bin/vca-setup.js +227 -0
- package/lib/checks/codeQuality/api-response-format.js +268 -0
- package/lib/checks/health/claude-md.js +114 -0
- package/lib/checks/health/doppler-compliance.js +174 -0
- package/lib/checks/health/git.js +61 -0
- package/lib/checks/health/gitignore.js +83 -0
- package/lib/checks/health/index.js +26 -0
- package/lib/checks/health/infrastructure-yml.js +87 -0
- package/lib/checks/health/mcps.js +57 -0
- package/lib/checks/health/naming-conventions.js +302 -0
- package/lib/checks/health/plugins.js +38 -0
- package/lib/checks/health/quality-toolkit.js +97 -0
- package/lib/checks/health/repo-visibility.js +70 -0
- package/lib/checks/health/rls-audit.js +130 -0
- package/lib/checks/health/scanner.js +68 -0
- package/lib/checks/health/secrets.js +80 -0
- package/lib/checks/health/stella-integration.js +124 -0
- package/lib/checks/health/tests.js +140 -0
- package/lib/checks/health/types.js +77 -0
- package/lib/checks/health/vincifox-widget.js +47 -0
- package/lib/checks/index.js +17 -0
- package/lib/checks/security/deprecated-supabase-admin.js +96 -0
- package/lib/checks/security/gitignore-validation.js +211 -0
- package/lib/checks/security/hardcoded-secrets.js +95 -0
- package/lib/checks/security/service-key-exposure.js +107 -0
- package/lib/checks/security/systemdb-whitelist.js +138 -0
- package/lib/checks/stability/ci-pipeline.js +143 -0
- package/lib/checks/stability/husky-hooks.js +117 -0
- package/lib/checks/stability/npm-audit.js +140 -0
- package/lib/checks/supabase/rls-policy-audit.js +261 -0
- package/lib/commands/dev-token.js +342 -0
- package/lib/config.js +213 -0
- package/lib/index.js +17 -0
- package/lib/reporters/terminal.js +134 -0
- package/lib/runner.js +179 -0
- package/package.json +72 -0
package/lib/config.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VCA Quality Toolkit - Configuration
|
|
3
|
+
*
|
|
4
|
+
* Default configuration that can be overridden per project via:
|
|
5
|
+
* - .vca-quality.json in project root
|
|
6
|
+
* - vca-quality key in package.json
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync, existsSync } from 'fs'
|
|
10
|
+
import { join } from 'path'
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_CONFIG = {
|
|
13
|
+
// Which check suites to run
|
|
14
|
+
suites: {
|
|
15
|
+
security: true,
|
|
16
|
+
stability: true,
|
|
17
|
+
codeQuality: true,
|
|
18
|
+
supabase: 'auto' // true, false, or 'auto' (detect)
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Security checks
|
|
22
|
+
security: {
|
|
23
|
+
// Supabase-specific
|
|
24
|
+
checkServiceKeyExposure: true,
|
|
25
|
+
checkRlsPolicies: true,
|
|
26
|
+
checkRpcSecurity: true,
|
|
27
|
+
|
|
28
|
+
// Secrets
|
|
29
|
+
checkHardcodedSecrets: true,
|
|
30
|
+
checkEnvInGitignore: true,
|
|
31
|
+
secretPatterns: [
|
|
32
|
+
'sk-[a-zA-Z0-9]{20,}', // OpenAI
|
|
33
|
+
'sk_live_[a-zA-Z0-9]{20,}', // Stripe
|
|
34
|
+
'sk_test_[a-zA-Z0-9]{20,}', // Stripe test
|
|
35
|
+
'AKIA[A-Z0-9]{16}', // AWS
|
|
36
|
+
'ghp_[a-zA-Z0-9]{36}', // GitHub
|
|
37
|
+
'xox[baprs]-[a-zA-Z0-9-]{10,}', // Slack
|
|
38
|
+
'eyJ[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]*' // JWT
|
|
39
|
+
],
|
|
40
|
+
|
|
41
|
+
// Code patterns
|
|
42
|
+
checkSqlInjection: true,
|
|
43
|
+
checkEvalUsage: true,
|
|
44
|
+
checkCommandInjection: true
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Stability checks
|
|
48
|
+
stability: {
|
|
49
|
+
// Testing
|
|
50
|
+
requireTestFramework: true,
|
|
51
|
+
requireTestFiles: true,
|
|
52
|
+
minTestFiles: 1,
|
|
53
|
+
requireCoverage: false,
|
|
54
|
+
minCoverage: 0,
|
|
55
|
+
|
|
56
|
+
// Linting
|
|
57
|
+
requireEslint: true,
|
|
58
|
+
requireSecurityPlugin: true,
|
|
59
|
+
|
|
60
|
+
// Pre-commit
|
|
61
|
+
requireHusky: true,
|
|
62
|
+
requirePreCommitHook: true,
|
|
63
|
+
|
|
64
|
+
// CI/CD
|
|
65
|
+
requireCiConfig: true,
|
|
66
|
+
requireTestsInCi: true,
|
|
67
|
+
requireLintInCi: true,
|
|
68
|
+
|
|
69
|
+
// Dependencies
|
|
70
|
+
requireLockFile: true,
|
|
71
|
+
allowedVulnerabilities: {
|
|
72
|
+
critical: 0,
|
|
73
|
+
high: 0,
|
|
74
|
+
moderate: 10
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Code quality checks
|
|
79
|
+
codeQuality: {
|
|
80
|
+
// Architecture
|
|
81
|
+
maxFileLines: 500,
|
|
82
|
+
checkCircularDeps: true,
|
|
83
|
+
|
|
84
|
+
// Dead code
|
|
85
|
+
runKnip: true,
|
|
86
|
+
|
|
87
|
+
// TypeScript
|
|
88
|
+
checkAnyTypes: true,
|
|
89
|
+
maxAnyCount: 10
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Supabase-specific checks
|
|
93
|
+
supabase: {
|
|
94
|
+
checkRls: true,
|
|
95
|
+
checkSecurityDefiner: true,
|
|
96
|
+
checkViewSecurity: true,
|
|
97
|
+
checkEdgeFunctions: true,
|
|
98
|
+
|
|
99
|
+
// Whitelist for intentionally public RPC functions
|
|
100
|
+
publicRpcFunctions: [],
|
|
101
|
+
|
|
102
|
+
// Tables that intentionally have no RLS (e.g., public lookup tables)
|
|
103
|
+
publicTables: []
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
// Paths
|
|
107
|
+
paths: {
|
|
108
|
+
backend: ['backend/src', 'src'],
|
|
109
|
+
frontend: ['frontend/src', 'src'],
|
|
110
|
+
migrations: ['supabase/migrations', 'migrations'],
|
|
111
|
+
tests: ['**/*.test.ts', '**/*.spec.ts', '**/*.test.tsx', '**/*.spec.tsx']
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// Ignore patterns
|
|
115
|
+
ignore: [
|
|
116
|
+
'node_modules/**',
|
|
117
|
+
'dist/**',
|
|
118
|
+
'build/**',
|
|
119
|
+
'.next/**',
|
|
120
|
+
'coverage/**',
|
|
121
|
+
'*.min.js'
|
|
122
|
+
],
|
|
123
|
+
|
|
124
|
+
// Output
|
|
125
|
+
output: {
|
|
126
|
+
format: 'terminal', // 'terminal', 'json', 'github-actions'
|
|
127
|
+
verbose: false,
|
|
128
|
+
failOn: 'error' // 'error', 'warning', 'none'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Load project-specific configuration
|
|
134
|
+
*/
|
|
135
|
+
export function loadConfig(projectRoot = process.cwd()) {
|
|
136
|
+
let projectConfig = {}
|
|
137
|
+
|
|
138
|
+
// Check for .vca-quality.json
|
|
139
|
+
const configFile = join(projectRoot, '.vca-quality.json')
|
|
140
|
+
if (existsSync(configFile)) {
|
|
141
|
+
try {
|
|
142
|
+
projectConfig = JSON.parse(readFileSync(configFile, 'utf-8'))
|
|
143
|
+
} catch (e) {
|
|
144
|
+
console.warn(`Warning: Could not parse ${configFile}`)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check for vca-quality in package.json
|
|
149
|
+
const packageFile = join(projectRoot, 'package.json')
|
|
150
|
+
if (existsSync(packageFile)) {
|
|
151
|
+
try {
|
|
152
|
+
const pkg = JSON.parse(readFileSync(packageFile, 'utf-8'))
|
|
153
|
+
if (pkg['vca-quality']) {
|
|
154
|
+
projectConfig = { ...projectConfig, ...pkg['vca-quality'] }
|
|
155
|
+
}
|
|
156
|
+
} catch (e) {
|
|
157
|
+
// Ignore
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Deep merge with defaults
|
|
162
|
+
return deepMerge(DEFAULT_CONFIG, projectConfig)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Deep merge two objects
|
|
167
|
+
*/
|
|
168
|
+
function deepMerge(target, source) {
|
|
169
|
+
const result = { ...target }
|
|
170
|
+
|
|
171
|
+
for (const key in source) {
|
|
172
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
173
|
+
result[key] = deepMerge(target[key] || {}, source[key])
|
|
174
|
+
} else {
|
|
175
|
+
result[key] = source[key]
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Detect if project uses Supabase
|
|
184
|
+
*/
|
|
185
|
+
export function detectSupabase(projectRoot = process.cwd()) {
|
|
186
|
+
const indicators = [
|
|
187
|
+
'supabase',
|
|
188
|
+
'supabase/migrations',
|
|
189
|
+
'supabase/functions'
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
for (const indicator of indicators) {
|
|
193
|
+
if (existsSync(join(projectRoot, indicator))) {
|
|
194
|
+
return true
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check package.json for supabase dependency
|
|
199
|
+
const packageFile = join(projectRoot, 'package.json')
|
|
200
|
+
if (existsSync(packageFile)) {
|
|
201
|
+
try {
|
|
202
|
+
const pkg = JSON.parse(readFileSync(packageFile, 'utf-8'))
|
|
203
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
|
|
204
|
+
if (deps['@supabase/supabase-js']) {
|
|
205
|
+
return true
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
// Ignore
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return false
|
|
213
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VCA Quality Toolkit
|
|
3
|
+
*
|
|
4
|
+
* Unified quality checks for all VCA projects.
|
|
5
|
+
* Consolidates security, stability, and code quality checks
|
|
6
|
+
* from sparkbuddy-live and vca-tools into a single npm package.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { loadConfig, detectSupabase, DEFAULT_CONFIG } from './config.js'
|
|
10
|
+
export { runAllChecks, runSecurityChecks, runStabilityChecks, runQuickCheck } from './runner.js'
|
|
11
|
+
export { formatResults, formatGitHubActions } from './reporters/terminal.js'
|
|
12
|
+
|
|
13
|
+
// Re-export individual checks for advanced usage
|
|
14
|
+
export * as checks from './checks/index.js'
|
|
15
|
+
|
|
16
|
+
// Health scanner (project ecosystem checks)
|
|
17
|
+
export { scanProjectHealth, calculateHealthStatus } from './checks/health/index.js'
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Reporter - Pretty output for CLI
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
|
|
7
|
+
const SEVERITY_COLORS = {
|
|
8
|
+
critical: chalk.bgRed.white.bold,
|
|
9
|
+
high: chalk.red.bold,
|
|
10
|
+
medium: chalk.yellow,
|
|
11
|
+
low: chalk.blue
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SEVERITY_ICONS = {
|
|
15
|
+
critical: '🚨',
|
|
16
|
+
high: '❌',
|
|
17
|
+
medium: '⚠️',
|
|
18
|
+
low: 'ℹ️'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function formatResults(results, options = {}) {
|
|
22
|
+
const lines = []
|
|
23
|
+
|
|
24
|
+
// Header
|
|
25
|
+
lines.push('')
|
|
26
|
+
lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
|
|
27
|
+
lines.push(chalk.bold.cyan(' 🔍 VCA Quality Toolkit - Audit Results'))
|
|
28
|
+
lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
|
|
29
|
+
lines.push('')
|
|
30
|
+
|
|
31
|
+
// Project info
|
|
32
|
+
lines.push(chalk.gray(` Project: ${results.projectRoot}`))
|
|
33
|
+
lines.push(chalk.gray(` Time: ${results.timestamp}`))
|
|
34
|
+
lines.push('')
|
|
35
|
+
|
|
36
|
+
// Summary box
|
|
37
|
+
const passed = results.passed
|
|
38
|
+
const statusIcon = passed ? '✅' : '❌'
|
|
39
|
+
const statusText = passed ? chalk.green.bold('PASSED') : chalk.red.bold('FAILED')
|
|
40
|
+
|
|
41
|
+
lines.push(chalk.bold(` ${statusIcon} Overall Status: ${statusText}`))
|
|
42
|
+
lines.push('')
|
|
43
|
+
|
|
44
|
+
// Finding counts
|
|
45
|
+
const { findings } = results.summary
|
|
46
|
+
if (findings.critical + findings.high + findings.medium + findings.low > 0) {
|
|
47
|
+
lines.push(chalk.bold(' Findings:'))
|
|
48
|
+
if (findings.critical > 0) lines.push(` ${SEVERITY_COLORS.critical(` ${findings.critical} CRITICAL `)}`)
|
|
49
|
+
if (findings.high > 0) lines.push(` ${SEVERITY_COLORS.high(`${findings.high} High`)}`)
|
|
50
|
+
if (findings.medium > 0) lines.push(` ${SEVERITY_COLORS.medium(`${findings.medium} Medium`)}`)
|
|
51
|
+
if (findings.low > 0) lines.push(` ${SEVERITY_COLORS.low(`${findings.low} Low`)}`)
|
|
52
|
+
lines.push('')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Suite results
|
|
56
|
+
for (const [suiteName, suite] of Object.entries(results.suites || {})) {
|
|
57
|
+
const suiteIcon = suite.passed ? '✅' : '❌'
|
|
58
|
+
lines.push(chalk.bold(` ${suiteIcon} ${suiteName.toUpperCase()}`))
|
|
59
|
+
lines.push(chalk.gray(' ' + '─'.repeat(50)))
|
|
60
|
+
|
|
61
|
+
for (const check of suite.checks || []) {
|
|
62
|
+
const checkIcon = check.skipped ? '⏭️' : (check.passed ? '✅' : '❌')
|
|
63
|
+
const checkStatus = check.skipped
|
|
64
|
+
? chalk.gray('SKIPPED')
|
|
65
|
+
: (check.passed ? chalk.green('PASS') : chalk.red('FAIL'))
|
|
66
|
+
|
|
67
|
+
lines.push(` ${checkIcon} ${check.name} ${checkStatus}`)
|
|
68
|
+
|
|
69
|
+
// Show findings for failed checks
|
|
70
|
+
if (!check.passed && !check.skipped && check.findings) {
|
|
71
|
+
for (const finding of check.findings.slice(0, options.maxFindings || 5)) {
|
|
72
|
+
const icon = SEVERITY_ICONS[finding.severity] || '•'
|
|
73
|
+
const color = SEVERITY_COLORS[finding.severity] || chalk.white
|
|
74
|
+
|
|
75
|
+
lines.push(color(` ${icon} ${finding.message}`))
|
|
76
|
+
|
|
77
|
+
if (finding.file) {
|
|
78
|
+
lines.push(chalk.gray(` ${finding.file}:${finding.line || '?'}`))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (finding.fix && options.verbose) {
|
|
82
|
+
lines.push(chalk.cyan(` Fix: ${finding.fix}`))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (check.findings.length > (options.maxFindings || 5)) {
|
|
87
|
+
lines.push(chalk.gray(` ... and ${check.findings.length - 5} more`))
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Show skip reason
|
|
92
|
+
if (check.skipped && check.skipReason) {
|
|
93
|
+
lines.push(chalk.gray(` (${check.skipReason})`))
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
lines.push('')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Footer
|
|
101
|
+
lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
|
|
102
|
+
lines.push(` Checks: ${results.summary.passed} passed, ${results.summary.failed} failed, ${results.summary.skipped} skipped`)
|
|
103
|
+
lines.push(chalk.bold('═══════════════════════════════════════════════════════════════'))
|
|
104
|
+
lines.push('')
|
|
105
|
+
|
|
106
|
+
return lines.join('\n')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Format for GitHub Actions annotations
|
|
111
|
+
*/
|
|
112
|
+
export function formatGitHubActions(results) {
|
|
113
|
+
const annotations = []
|
|
114
|
+
|
|
115
|
+
for (const [, suite] of Object.entries(results.suites || {})) {
|
|
116
|
+
for (const check of suite.checks || []) {
|
|
117
|
+
if (!check.passed && !check.skipped && check.findings) {
|
|
118
|
+
for (const finding of check.findings) {
|
|
119
|
+
const level = finding.severity === 'critical' || finding.severity === 'high'
|
|
120
|
+
? 'error'
|
|
121
|
+
: 'warning'
|
|
122
|
+
|
|
123
|
+
if (finding.file) {
|
|
124
|
+
annotations.push(`::${level} file=${finding.file},line=${finding.line || 1}::${finding.message}`)
|
|
125
|
+
} else {
|
|
126
|
+
annotations.push(`::${level}::${finding.message}`)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return annotations.join('\n')
|
|
134
|
+
}
|
package/lib/runner.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VCA Quality Toolkit - Check Runner
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates running all checks and collecting results
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { loadConfig, detectSupabase } from './config.js'
|
|
8
|
+
|
|
9
|
+
// Import all checks
|
|
10
|
+
import * as hardcodedSecrets from './checks/security/hardcoded-secrets.js'
|
|
11
|
+
import * as serviceKeyExposure from './checks/security/service-key-exposure.js'
|
|
12
|
+
import * as deprecatedSupabaseAdmin from './checks/security/deprecated-supabase-admin.js'
|
|
13
|
+
import * as systemdbWhitelist from './checks/security/systemdb-whitelist.js'
|
|
14
|
+
import * as huskyHooks from './checks/stability/husky-hooks.js'
|
|
15
|
+
import * as ciPipeline from './checks/stability/ci-pipeline.js'
|
|
16
|
+
import * as npmAudit from './checks/stability/npm-audit.js'
|
|
17
|
+
import * as apiResponseFormat from './checks/codeQuality/api-response-format.js'
|
|
18
|
+
import * as gitignoreValidation from './checks/security/gitignore-validation.js'
|
|
19
|
+
import * as rlsPolicyAudit from './checks/supabase/rls-policy-audit.js'
|
|
20
|
+
|
|
21
|
+
// Register all checks
|
|
22
|
+
const ALL_CHECKS = {
|
|
23
|
+
security: [
|
|
24
|
+
hardcodedSecrets,
|
|
25
|
+
serviceKeyExposure,
|
|
26
|
+
deprecatedSupabaseAdmin,
|
|
27
|
+
systemdbWhitelist,
|
|
28
|
+
gitignoreValidation
|
|
29
|
+
],
|
|
30
|
+
stability: [
|
|
31
|
+
huskyHooks,
|
|
32
|
+
ciPipeline,
|
|
33
|
+
npmAudit
|
|
34
|
+
],
|
|
35
|
+
codeQuality: [
|
|
36
|
+
apiResponseFormat
|
|
37
|
+
],
|
|
38
|
+
supabase: [
|
|
39
|
+
rlsPolicyAudit
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Run all checks for a project
|
|
45
|
+
*/
|
|
46
|
+
export async function runAllChecks(options = {}) {
|
|
47
|
+
const projectRoot = options.projectRoot || process.cwd()
|
|
48
|
+
const config = loadConfig(projectRoot)
|
|
49
|
+
const suites = options.suites || ['security', 'stability', 'codeQuality', 'supabase']
|
|
50
|
+
|
|
51
|
+
// Auto-detect Supabase
|
|
52
|
+
if (config.suites.supabase === 'auto') {
|
|
53
|
+
config.suites.supabase = detectSupabase(projectRoot)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const results = {
|
|
57
|
+
projectRoot,
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
passed: true,
|
|
60
|
+
suites: {},
|
|
61
|
+
summary: {
|
|
62
|
+
total: 0,
|
|
63
|
+
passed: 0,
|
|
64
|
+
failed: 0,
|
|
65
|
+
skipped: 0,
|
|
66
|
+
findings: { critical: 0, high: 0, medium: 0, low: 0 }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const suite of suites) {
|
|
71
|
+
if (!config.suites[suite]) {
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const checks = ALL_CHECKS[suite] || []
|
|
76
|
+
results.suites[suite] = {
|
|
77
|
+
passed: true,
|
|
78
|
+
checks: []
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const check of checks) {
|
|
82
|
+
const checkResult = {
|
|
83
|
+
id: check.meta.id,
|
|
84
|
+
name: check.meta.name,
|
|
85
|
+
severity: check.meta.severity,
|
|
86
|
+
...await check.run(config, projectRoot)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
results.suites[suite].checks.push(checkResult)
|
|
90
|
+
|
|
91
|
+
// Update suite status
|
|
92
|
+
if (!checkResult.passed && !checkResult.skipped) {
|
|
93
|
+
results.suites[suite].passed = false
|
|
94
|
+
results.passed = false
|
|
95
|
+
results.summary.failed++
|
|
96
|
+
} else if (checkResult.skipped) {
|
|
97
|
+
results.summary.skipped++
|
|
98
|
+
} else {
|
|
99
|
+
results.summary.passed++
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Aggregate findings
|
|
103
|
+
results.summary.total++
|
|
104
|
+
if (checkResult.summary) {
|
|
105
|
+
results.summary.findings.critical += checkResult.summary.critical || 0
|
|
106
|
+
results.summary.findings.high += checkResult.summary.high || 0
|
|
107
|
+
results.summary.findings.medium += checkResult.summary.medium || 0
|
|
108
|
+
results.summary.findings.low += checkResult.summary.low || 0
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return results
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Run only security checks
|
|
118
|
+
*/
|
|
119
|
+
export async function runSecurityChecks(options = {}) {
|
|
120
|
+
return runAllChecks({ ...options, suites: ['security'] })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Run only stability checks
|
|
125
|
+
*/
|
|
126
|
+
export async function runStabilityChecks(options = {}) {
|
|
127
|
+
return runAllChecks({ ...options, suites: ['stability'] })
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Run only code quality checks
|
|
132
|
+
*/
|
|
133
|
+
export async function runCodeQualityChecks(options = {}) {
|
|
134
|
+
return runAllChecks({ ...options, suites: ['codeQuality'] })
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Quick check - only critical checks
|
|
139
|
+
*/
|
|
140
|
+
export async function runQuickCheck(options = {}) {
|
|
141
|
+
const projectRoot = options.projectRoot || process.cwd()
|
|
142
|
+
const config = loadConfig(projectRoot)
|
|
143
|
+
|
|
144
|
+
// Run only critical security checks
|
|
145
|
+
const criticalChecks = [
|
|
146
|
+
hardcodedSecrets,
|
|
147
|
+
serviceKeyExposure
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
const results = {
|
|
151
|
+
projectRoot,
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
passed: true,
|
|
154
|
+
checks: [],
|
|
155
|
+
summary: { total: 0, passed: 0, failed: 0, findings: { critical: 0 } }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (const check of criticalChecks) {
|
|
159
|
+
const checkResult = {
|
|
160
|
+
id: check.meta.id,
|
|
161
|
+
name: check.meta.name,
|
|
162
|
+
...await check.run(config, projectRoot)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
results.checks.push(checkResult)
|
|
166
|
+
|
|
167
|
+
if (!checkResult.passed) {
|
|
168
|
+
results.passed = false
|
|
169
|
+
results.summary.failed++
|
|
170
|
+
} else {
|
|
171
|
+
results.summary.passed++
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
results.summary.total++
|
|
175
|
+
results.summary.findings.critical += checkResult.summary?.critical || 0
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return results
|
|
179
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@soulbatical/tetra-dev-toolkit",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "restricted"
|
|
6
|
+
},
|
|
7
|
+
"description": "Developer toolkit for all VCA projects - audit, dev-token, quality checks",
|
|
8
|
+
"author": "Albert Barth <albertbarth@gmail.com>",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/mralbertzwolle/vca-quality-toolkit"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"quality",
|
|
16
|
+
"security",
|
|
17
|
+
"audit",
|
|
18
|
+
"dev-token",
|
|
19
|
+
"eslint",
|
|
20
|
+
"supabase",
|
|
21
|
+
"rls",
|
|
22
|
+
"pre-commit",
|
|
23
|
+
"ci-cd"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "lib/index.js",
|
|
27
|
+
"bin": {
|
|
28
|
+
"vca-audit": "./bin/vca-audit.js",
|
|
29
|
+
"vca-security": "./bin/vca-security.js",
|
|
30
|
+
"vca-stability": "./bin/vca-stability.js",
|
|
31
|
+
"vca-setup": "./bin/vca-setup.js",
|
|
32
|
+
"vca-dev-token": "./bin/vca-dev-token.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"lib/",
|
|
37
|
+
"templates/"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "node --test src/**/*.test.js",
|
|
41
|
+
"lint": "eslint src/ lib/ bin/",
|
|
42
|
+
"build": "echo 'No build step needed'",
|
|
43
|
+
"prepublishOnly": "npm test && npm run lint"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@supabase/supabase-js": "^2.48.0",
|
|
50
|
+
"chalk": "^5.3.0",
|
|
51
|
+
"commander": "^12.0.0",
|
|
52
|
+
"dotenv": "^16.4.7",
|
|
53
|
+
"glob": "^10.3.10",
|
|
54
|
+
"ora": "^8.0.1",
|
|
55
|
+
"yaml": "^2.8.2"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"eslint": "^9.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"eslint": ">=8.0.0",
|
|
62
|
+
"typescript": ">=5.0.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"eslint": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"typescript": {
|
|
69
|
+
"optional": true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|