create-qa-architect 5.0.1 → 5.0.7
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/.github/RELEASE_CHECKLIST.md +2 -4
- package/.github/workflows/daily-deploy-check.yml +136 -0
- package/.github/workflows/dependabot-auto-merge.yml +32 -0
- package/.github/workflows/nightly-gitleaks-verification.yml +1 -1
- package/.github/workflows/release.yml +12 -10
- package/.github/workflows/weekly-audit.yml +173 -0
- package/LICENSE +3 -3
- package/README.md +11 -17
- package/config/defaults.js +22 -1
- package/config/quality-config.schema.json +1 -1
- package/create-saas-monetization.js +65 -27
- package/docs/ARCHITECTURE.md +16 -13
- package/docs/DEPLOYMENT.md +1 -2
- package/docs/PREFLIGHT_REPORT.md +100 -0
- package/docs/TESTING.md +4 -6
- package/lib/billing-dashboard.html +6 -12
- package/lib/config-validator.js +8 -2
- package/lib/dependency-monitoring-premium.js +21 -19
- package/lib/github-api.js +249 -0
- package/lib/interactive/questions.js +4 -0
- package/lib/license-validator.js +1 -1
- package/lib/licensing.js +16 -18
- package/lib/package-utils.js +9 -8
- package/lib/project-maturity.js +1 -1
- package/lib/template-loader.js +2 -0
- package/lib/ui-helpers.js +2 -1
- package/lib/validation/base-validator.js +5 -1
- package/lib/validation/cache-manager.js +1 -0
- package/lib/validation/config-security.js +9 -4
- package/lib/validation/validation-factory.js +1 -1
- package/lib/validation/workflow-validation.js +27 -22
- package/lib/yaml-utils.js +15 -10
- package/package.json +17 -14
- package/scripts/check-docs.sh +63 -0
- package/scripts/smart-test-strategy.sh +98 -0
- package/scripts/test-e2e-package.sh +283 -0
- package/scripts/validate-command-patterns.js +112 -0
- package/setup.js +38 -9
- package/templates/QUALITY_TROUBLESHOOTING.md +32 -33
- package/templates/scripts/smart-test-strategy.sh +1 -1
|
@@ -39,7 +39,7 @@ const FOUNDER_ENTERPRISE_PRICE = '74.50'
|
|
|
39
39
|
|
|
40
40
|
class SaaSMonetizationBootstrap {
|
|
41
41
|
constructor() {
|
|
42
|
-
this.projectRoot = process.cwd()
|
|
42
|
+
this.projectRoot = path.resolve(process.cwd())
|
|
43
43
|
this.config = {}
|
|
44
44
|
this.templates = {
|
|
45
45
|
stripe: this.getStripeTemplate(),
|
|
@@ -50,6 +50,56 @@ class SaaSMonetizationBootstrap {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
resolveProjectPath(relativePath) {
|
|
54
|
+
const normalizedRoot = this.projectRoot.endsWith(path.sep)
|
|
55
|
+
? this.projectRoot
|
|
56
|
+
: `${this.projectRoot}${path.sep}`
|
|
57
|
+
const resolvedPath = path.resolve(this.projectRoot, relativePath)
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
resolvedPath !== this.projectRoot &&
|
|
61
|
+
!resolvedPath.startsWith(normalizedRoot)
|
|
62
|
+
) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Refusing to access path outside project root: ${relativePath}`
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return resolvedPath
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ensureDir(relativePath) {
|
|
72
|
+
const target = this.resolveProjectPath(relativePath)
|
|
73
|
+
// Path is constrained to the project root before touching the filesystem
|
|
74
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
75
|
+
if (!fs.existsSync(target)) {
|
|
76
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
77
|
+
fs.mkdirSync(target, { recursive: true })
|
|
78
|
+
}
|
|
79
|
+
return target
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
writeProjectFile(relativePath, content) {
|
|
83
|
+
const target = this.resolveProjectPath(relativePath)
|
|
84
|
+
// Path is constrained to the project root before touching the filesystem
|
|
85
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
86
|
+
fs.writeFileSync(target, content)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
readProjectFile(relativePath) {
|
|
90
|
+
const target = this.resolveProjectPath(relativePath)
|
|
91
|
+
// Path is constrained to the project root before touching the filesystem
|
|
92
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
93
|
+
return fs.readFileSync(target, 'utf8')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
projectFileExists(relativePath) {
|
|
97
|
+
const target = this.resolveProjectPath(relativePath)
|
|
98
|
+
// Path is constrained to the project root before touching the filesystem
|
|
99
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
100
|
+
return fs.existsSync(target)
|
|
101
|
+
}
|
|
102
|
+
|
|
53
103
|
async run() {
|
|
54
104
|
console.log('🚀 Create SaaS Monetization')
|
|
55
105
|
console.log('═══════════════════════════════════')
|
|
@@ -159,10 +209,7 @@ class SaaSMonetizationBootstrap {
|
|
|
159
209
|
const dirs = ['lib/monetization', 'legal', 'marketing', 'billing']
|
|
160
210
|
|
|
161
211
|
dirs.forEach(dir => {
|
|
162
|
-
|
|
163
|
-
if (!fs.existsSync(fullPath)) {
|
|
164
|
-
fs.mkdirSync(fullPath, { recursive: true })
|
|
165
|
-
}
|
|
212
|
+
this.ensureDir(dir)
|
|
166
213
|
})
|
|
167
214
|
}
|
|
168
215
|
|
|
@@ -172,8 +219,8 @@ class SaaSMonetizationBootstrap {
|
|
|
172
219
|
.replace(/{{PRO_PRICE}}/g, this.config.proPrice)
|
|
173
220
|
.replace(/{{ENTERPRISE_PRICE}}/g, this.config.enterprisePrice)
|
|
174
221
|
|
|
175
|
-
|
|
176
|
-
path.join(
|
|
222
|
+
this.writeProjectFile(
|
|
223
|
+
path.join('lib/monetization', 'stripe-integration.js'),
|
|
177
224
|
stripeCode
|
|
178
225
|
)
|
|
179
226
|
}
|
|
@@ -188,8 +235,8 @@ class SaaSMonetizationBootstrap {
|
|
|
188
235
|
.replace(/{{FOUNDER_PRO_PRICE}}/g, this.config.founderProPrice)
|
|
189
236
|
.replace(/{{DOMAIN}}/g, this.config.domain)
|
|
190
237
|
|
|
191
|
-
|
|
192
|
-
path.join(
|
|
238
|
+
this.writeProjectFile(
|
|
239
|
+
path.join('lib/monetization', 'licensing.js'),
|
|
193
240
|
licensingCode
|
|
194
241
|
)
|
|
195
242
|
}
|
|
@@ -204,7 +251,7 @@ class SaaSMonetizationBootstrap {
|
|
|
204
251
|
.replace(/{{DESCRIPTION}}/g, this.config.description)
|
|
205
252
|
.replace(/{{DATE}}/g, new Date().toISOString().split('T')[0])
|
|
206
253
|
|
|
207
|
-
|
|
254
|
+
this.writeProjectFile(path.join('legal', filename), content)
|
|
208
255
|
}
|
|
209
256
|
}
|
|
210
257
|
|
|
@@ -233,10 +280,7 @@ class SaaSMonetizationBootstrap {
|
|
|
233
280
|
)
|
|
234
281
|
.replace(/{{SUPPORT_EMAIL}}/g, this.config.supportEmail)
|
|
235
282
|
|
|
236
|
-
|
|
237
|
-
path.join(this.projectRoot, 'marketing', filename),
|
|
238
|
-
content
|
|
239
|
-
)
|
|
283
|
+
this.writeProjectFile(path.join('marketing', filename), content)
|
|
240
284
|
}
|
|
241
285
|
}
|
|
242
286
|
|
|
@@ -252,17 +296,14 @@ class SaaSMonetizationBootstrap {
|
|
|
252
296
|
)
|
|
253
297
|
.replace(/{{PREMIUM_FEATURES}}/g, this.config.premiumFeatures)
|
|
254
298
|
|
|
255
|
-
|
|
256
|
-
path.join(this.projectRoot, 'billing/dashboard.html'),
|
|
257
|
-
billingCode
|
|
258
|
-
)
|
|
299
|
+
this.writeProjectFile(path.join('billing', 'dashboard.html'), billingCode)
|
|
259
300
|
}
|
|
260
301
|
|
|
261
302
|
async updatePackageJson() {
|
|
262
|
-
const packagePath =
|
|
303
|
+
const packagePath = 'package.json'
|
|
263
304
|
|
|
264
|
-
if (
|
|
265
|
-
const pkg = JSON.parse(
|
|
305
|
+
if (this.projectFileExists(packagePath)) {
|
|
306
|
+
const pkg = JSON.parse(this.readProjectFile(packagePath))
|
|
266
307
|
|
|
267
308
|
// Add monetization scripts
|
|
268
309
|
pkg.scripts = pkg.scripts || {}
|
|
@@ -276,7 +317,7 @@ class SaaSMonetizationBootstrap {
|
|
|
276
317
|
pkg.dependencies.stripe = '^14.15.0'
|
|
277
318
|
pkg.dependencies.crypto = '^1.0.1'
|
|
278
319
|
|
|
279
|
-
|
|
320
|
+
this.writeProjectFile(packagePath, JSON.stringify(pkg, null, 2))
|
|
280
321
|
}
|
|
281
322
|
}
|
|
282
323
|
|
|
@@ -307,7 +348,7 @@ COMPANY_NAME=${this.config.companyName}
|
|
|
307
348
|
# STRIPE_PUBLISHABLE_KEY=pk_live_your_live_key_here
|
|
308
349
|
`
|
|
309
350
|
|
|
310
|
-
|
|
351
|
+
this.writeProjectFile('.env.template', envTemplate)
|
|
311
352
|
}
|
|
312
353
|
|
|
313
354
|
async generateDeploymentGuide() {
|
|
@@ -483,10 +524,7 @@ For implementation questions:
|
|
|
483
524
|
**Revenue Potential**: $1,500-5,000/month recurring
|
|
484
525
|
`
|
|
485
526
|
|
|
486
|
-
|
|
487
|
-
path.join(this.projectRoot, 'MONETIZATION_GUIDE.md'),
|
|
488
|
-
guide
|
|
489
|
-
)
|
|
527
|
+
this.writeProjectFile('MONETIZATION_GUIDE.md', guide)
|
|
490
528
|
}
|
|
491
529
|
|
|
492
530
|
// Template methods (condensed versions of our implementations)
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -9,18 +9,18 @@ QA Architect is a CLI tool that bootstraps quality automation in JavaScript/Type
|
|
|
9
9
|
```
|
|
10
10
|
create-qa-architect/
|
|
11
11
|
├── setup.js # Main CLI entry point
|
|
12
|
-
├── lib/
|
|
13
|
-
│ ├── smart-strategy-generator.js # Smart test strategy (Pro)
|
|
14
|
-
│ ├── dependency-monitoring-*.js # Dependency monitoring
|
|
15
|
-
│ └── validation/ # Validation utilities
|
|
12
|
+
├── lib/ # Core logic (validation, licensing, maturity, telemetry, dependency monitoring)
|
|
16
13
|
├── templates/ # Project templates
|
|
17
|
-
│ ├──
|
|
18
|
-
│ ├── .
|
|
19
|
-
│ ├──
|
|
20
|
-
│
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
│ ├── ci/ # GitHub Actions + CircleCI/GitLab samples
|
|
15
|
+
│ ├── scripts/ # Helper scripts (smart test strategy, etc.)
|
|
16
|
+
│ ├── integration-tests/# Starter integration tests
|
|
17
|
+
│ ├── test-stubs/ # Unit/E2E placeholders
|
|
18
|
+
│ ├── python/ # Python quality config
|
|
19
|
+
│ └── QUALITY_TROUBLESHOOTING.md
|
|
20
|
+
├── config/ # Defaults and language-specific configs
|
|
21
|
+
│ ├── pyproject.toml
|
|
22
|
+
│ └── quality-python.yml
|
|
23
|
+
└── docs/ # Architecture/testing/SLA/security docs
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Data Flow
|
|
@@ -50,5 +50,8 @@ Risk-based pre-push validation that adapts to change context:
|
|
|
50
50
|
- `--deps` - Dependency monitoring only
|
|
51
51
|
- `--security-config` - Security validation
|
|
52
52
|
- `--check-maturity` - Project maturity report
|
|
53
|
-
- `--comprehensive` - Full validation suite
|
|
54
|
-
|
|
53
|
+
- `--validate` / `--comprehensive` - Full validation suite
|
|
54
|
+
- `--validate-docs` - Documentation validation only
|
|
55
|
+
- `--validate-config` - Validate `.qualityrc.json`
|
|
56
|
+
- `--alerts-slack` / `--pr-comments` - Collaboration hooks
|
|
57
|
+
- `--license-status` - Show current tier/features
|
package/docs/DEPLOYMENT.md
CHANGED
|
@@ -59,5 +59,4 @@ npm deprecate create-qa-architect@VERSION "Critical bug, use VERSION instead"
|
|
|
59
59
|
## npm Registry
|
|
60
60
|
|
|
61
61
|
- Package: https://www.npmjs.com/package/create-qa-architect
|
|
62
|
-
- Documentation: https://github.com/vibebuildlab/
|
|
63
|
-
|
|
62
|
+
- Documentation: https://github.com/vibebuildlab/qa-architect
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Preflight Review: QA Architect (create-qa-architect)
|
|
2
|
+
|
|
3
|
+
**Depth**: standard
|
|
4
|
+
**Date**: 2025-12-13
|
|
5
|
+
**Version**: 5.0.7
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overall Status: ✅ PASS (prerelease suite)
|
|
10
|
+
|
|
11
|
+
Prerelease (`npm run prerelease`) executed for 5.0.7, including docs check, command patterns, full test suite, command tests, and e2e package validation.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Critical Issues (P0) - Must Fix
|
|
16
|
+
|
|
17
|
+
| Issue | Category | Location | Fix |
|
|
18
|
+
| ----- | -------- | -------- | --- |
|
|
19
|
+
| None | - | - | - |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Important Issues (P1) - Should Fix
|
|
24
|
+
|
|
25
|
+
| Issue | Category | Location | Recommendation |
|
|
26
|
+
| ------------------------ | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
27
|
+
| Gitleaks false positives | Security | tests/\*.test.js | Test fixtures use fake API key patterns (QAA-XXXX format); consider a scoped `.gitleaksignore` for fixtures. |
|
|
28
|
+
| Publish verification | Release | package.json | Confirm npm shows 5.0.7 after publishing; update if propagation is pending. |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## P0 Functional Checks
|
|
33
|
+
|
|
34
|
+
| Check | Status | Notes |
|
|
35
|
+
| ----------------- | ------ | ------------------------------------------------------------------ |
|
|
36
|
+
| All tests passing | ✅ | `npm run prerelease` (includes full test suite) |
|
|
37
|
+
| npm audit | ⚠️ | Not run in prerelease; run `npm run security:audit` before publish |
|
|
38
|
+
| ESLint | ⚠️ | Not run in prerelease; run `npm run lint` if desired |
|
|
39
|
+
| Build/validation | ✅ | Covered via prerelease command + e2e package test |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## P0 Security Checks
|
|
44
|
+
|
|
45
|
+
| Check | Status | Notes |
|
|
46
|
+
| ------------------------- | ------ | ------------------------------------------------------------------------------------- |
|
|
47
|
+
| npm audit (high/critical) | ⚠️ | Not run in prerelease; run `npm run security:audit` |
|
|
48
|
+
| Hardcoded secrets scan | ⚠️ | Re-run gitleaks/`npm run security:secrets`; expect fixture false positives (QAA-XXXX) |
|
|
49
|
+
| No production secrets | ✅ | No `.env` files, no real API keys committed |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Product Packaging
|
|
54
|
+
|
|
55
|
+
| Item | Status | Notes |
|
|
56
|
+
| ------------ | ------ | ------------------------------ |
|
|
57
|
+
| CHANGELOG.md | ✅ | Present |
|
|
58
|
+
| LICENSE | ✅ | Present |
|
|
59
|
+
| README.md | ✅ | Present |
|
|
60
|
+
| .env.example | N/A | Not needed for CLI tool |
|
|
61
|
+
| Version tags | ⚠️ | Confirm v5.0.7 tag pushed |
|
|
62
|
+
| Git status | ⚠️ | Verify clean before publishing |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Quality Violations
|
|
67
|
+
|
|
68
|
+
| Type | Count | Assessment |
|
|
69
|
+
| ----------------------- | ----- | ------------------------------------------------------------ |
|
|
70
|
+
| eslint-disable comments | 24 | All have security justification comments explaining why safe |
|
|
71
|
+
| any types | 0 | JavaScript project, N/A |
|
|
72
|
+
|
|
73
|
+
**Note**: The `eslint-disable` comments are all for security-related ESLint rules (detect-unsafe-regex, detect-non-literal-fs-filename, detect-object-injection) and each includes a detailed safety justification explaining why the pattern is safe in context.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Silent Killer Check (N/A for npm package)
|
|
78
|
+
|
|
79
|
+
| Integration | Status | Notes |
|
|
80
|
+
| --------------- | ------ | --------------------- |
|
|
81
|
+
| Stripe webhooks | N/A | CLI tool, no webhooks |
|
|
82
|
+
| OAuth redirects | N/A | CLI tool |
|
|
83
|
+
| API keys | N/A | CLI tool |
|
|
84
|
+
| CORS config | N/A | CLI tool |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Next Steps
|
|
89
|
+
|
|
90
|
+
1. Run `npm run security:audit` (and optional gitleaks scan) before publish
|
|
91
|
+
2. Confirm npm publish and tag for 5.0.7 are visible on npm/GitHub
|
|
92
|
+
3. Add `.gitleaksignore` scoped to test fixtures if false positives remain
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Recommendation
|
|
97
|
+
|
|
98
|
+
**✅ Cleared for launch (5.0.7)**
|
|
99
|
+
|
|
100
|
+
Prerelease suite passed for 5.0.7. Run `npm run security:audit`, confirm publish/tag visibility, and handle fixture gitleaks ignores if needed; then proceed with release comms. This remains an npm CLI package (no web surface), so focus stays on docs/CI/security validation.
|
package/docs/TESTING.md
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
QA Architect uses
|
|
5
|
+
QA Architect uses plain Node-based test runners (no Jest) with a heavy focus on integration tests that validate real CLI workflows end to end.
|
|
6
6
|
|
|
7
7
|
## Running Tests
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm test # Run all tests
|
|
11
|
-
npm run test:coverage # Run with coverage report
|
|
12
|
-
npm run test:watch # Watch mode for development
|
|
10
|
+
npm test # Run all tests (sequential Node scripts)
|
|
11
|
+
npm run test:coverage # Run with coverage report via c8
|
|
13
12
|
```
|
|
14
13
|
|
|
15
14
|
## Test Structure
|
|
@@ -48,7 +47,7 @@ Use real packages from the ecosystem, not toy examples:
|
|
|
48
47
|
const TOP_PYTHON_PACKAGES = [
|
|
49
48
|
'django-cors-headers',
|
|
50
49
|
'scikit-learn',
|
|
51
|
-
'pytest-cov'
|
|
50
|
+
'pytest-cov',
|
|
52
51
|
]
|
|
53
52
|
```
|
|
54
53
|
|
|
@@ -59,4 +58,3 @@ Always run before release:
|
|
|
59
58
|
```bash
|
|
60
59
|
npm run prerelease # Runs docs:check + all tests
|
|
61
60
|
```
|
|
62
|
-
|
|
@@ -311,9 +311,9 @@
|
|
|
311
311
|
onclick="selectTier('pro')"
|
|
312
312
|
>
|
|
313
313
|
<div class="tier-name">Pro</div>
|
|
314
|
-
<div class="tier-price">$
|
|
314
|
+
<div class="tier-price">$19<span class="period">/month</span></div>
|
|
315
315
|
<div style="color: #22c55e; font-size: 0.9rem">
|
|
316
|
-
or $
|
|
316
|
+
or $190/year (save $38)
|
|
317
317
|
</div>
|
|
318
318
|
|
|
319
319
|
<ul class="tier-features">
|
|
@@ -329,12 +329,8 @@
|
|
|
329
329
|
<!-- Team Tier -->
|
|
330
330
|
<div class="tier-card" data-tier="team" onclick="selectTier('team')">
|
|
331
331
|
<div class="tier-name">Team</div>
|
|
332
|
-
<div class="tier-price">
|
|
333
|
-
|
|
334
|
-
</div>
|
|
335
|
-
<div style="color: #666; font-size: 0.9rem">
|
|
336
|
-
5-seat minimum ($75/mo)
|
|
337
|
-
</div>
|
|
332
|
+
<div class="tier-price">Contact us</div>
|
|
333
|
+
<div style="color: #666; font-size: 0.9rem">Coming soon</div>
|
|
338
334
|
|
|
339
335
|
<ul class="tier-features">
|
|
340
336
|
<li>All PRO features included</li>
|
|
@@ -353,10 +349,8 @@
|
|
|
353
349
|
onclick="selectTier('enterprise')"
|
|
354
350
|
>
|
|
355
351
|
<div class="tier-name">Enterprise</div>
|
|
356
|
-
<div class="tier-price"
|
|
357
|
-
<div style="color: #666; font-size: 0.9rem">
|
|
358
|
-
annual + $499 onboarding
|
|
359
|
-
</div>
|
|
352
|
+
<div class="tier-price">Contact us</div>
|
|
353
|
+
<div style="color: #666; font-size: 0.9rem">Coming soon</div>
|
|
360
354
|
|
|
361
355
|
<ul class="tier-features">
|
|
362
356
|
<li>All TEAM features included</li>
|
package/lib/config-validator.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const AjvImport = require('ajv')
|
|
4
|
+
const addFormatsImport = require('ajv-formats')
|
|
5
5
|
const fs = require('fs')
|
|
6
6
|
const path = require('path')
|
|
7
7
|
|
|
8
|
+
// Handle CJS/ESM interop for Ajv and ajv-formats in JS type-checking
|
|
9
|
+
const Ajv = /** @type {any} */ (AjvImport.default || AjvImport)
|
|
10
|
+
const addFormats = /** @type {(ajv: any) => void} */ (
|
|
11
|
+
addFormatsImport.default || addFormatsImport
|
|
12
|
+
)
|
|
13
|
+
|
|
8
14
|
function validateQualityConfig(configPath) {
|
|
9
15
|
const result = {
|
|
10
16
|
valid: false,
|
|
@@ -289,7 +289,7 @@ function matchesPattern(depName, pattern) {
|
|
|
289
289
|
if (!regex) {
|
|
290
290
|
// Cache miss - compile and store
|
|
291
291
|
const regexPattern = pattern.replace(/\*/g, '.*')
|
|
292
|
-
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
292
|
+
// eslint-disable-next-line security/detect-non-literal-regexp -- Safe: pattern from internal config, only allows * wildcards replaced with .*, anchored with ^$
|
|
293
293
|
regex = new RegExp(`^${regexPattern}$`)
|
|
294
294
|
|
|
295
295
|
// Implement size-limited cache with FIFO eviction
|
|
@@ -348,7 +348,7 @@ function generateReactGroups(_frameworkInfo) {
|
|
|
348
348
|
/**
|
|
349
349
|
* Generate dependency groups for Vue ecosystem
|
|
350
350
|
*
|
|
351
|
-
* @param {Object}
|
|
351
|
+
* @param {Object} _frameworkInfo - Vue detection results
|
|
352
352
|
* @returns {Object} Dependabot groups configuration
|
|
353
353
|
*/
|
|
354
354
|
function generateVueGroups(_frameworkInfo) {
|
|
@@ -376,7 +376,7 @@ function generateVueGroups(_frameworkInfo) {
|
|
|
376
376
|
/**
|
|
377
377
|
* Generate dependency groups for Angular ecosystem
|
|
378
378
|
*
|
|
379
|
-
* @param {Object}
|
|
379
|
+
* @param {Object} _frameworkInfo - Angular detection results
|
|
380
380
|
* @returns {Object} Dependabot groups configuration
|
|
381
381
|
*/
|
|
382
382
|
function generateAngularGroups(_frameworkInfo) {
|
|
@@ -404,7 +404,7 @@ function generateAngularGroups(_frameworkInfo) {
|
|
|
404
404
|
/**
|
|
405
405
|
* Generate dependency groups for testing frameworks
|
|
406
406
|
*
|
|
407
|
-
* @param {Object}
|
|
407
|
+
* @param {Object} _frameworkInfo - Testing framework detection results
|
|
408
408
|
* @returns {Object} Dependabot groups configuration
|
|
409
409
|
*/
|
|
410
410
|
function generateTestingGroups(_frameworkInfo) {
|
|
@@ -428,7 +428,7 @@ function generateTestingGroups(_frameworkInfo) {
|
|
|
428
428
|
/**
|
|
429
429
|
* Generate dependency groups for build tools
|
|
430
430
|
*
|
|
431
|
-
* @param {Object}
|
|
431
|
+
* @param {Object} _frameworkInfo - Build tool detection results
|
|
432
432
|
* @returns {Object} Dependabot groups configuration
|
|
433
433
|
*/
|
|
434
434
|
function generateBuildToolGroups(_frameworkInfo) {
|
|
@@ -446,7 +446,7 @@ function generateBuildToolGroups(_frameworkInfo) {
|
|
|
446
446
|
/**
|
|
447
447
|
* Generate Storybook dependency groups
|
|
448
448
|
*
|
|
449
|
-
* @param {Object}
|
|
449
|
+
* @param {Object} _frameworkInfo - Storybook detection results
|
|
450
450
|
* @returns {Object} Dependabot groups configuration
|
|
451
451
|
*/
|
|
452
452
|
function generateStorybookGroups(_frameworkInfo) {
|
|
@@ -490,7 +490,7 @@ function hasPythonProject(projectPath) {
|
|
|
490
490
|
* attacks with maliciously large requirements files.
|
|
491
491
|
*
|
|
492
492
|
* @param {string} requirementsPath - Path to requirements.txt file
|
|
493
|
-
* @returns {
|
|
493
|
+
* @returns {Record<string, string>} Map of package names to version specifiers
|
|
494
494
|
* @throws {Error} If file exceeds MAX_REQUIREMENTS_FILE_SIZE
|
|
495
495
|
*
|
|
496
496
|
* @example
|
|
@@ -512,6 +512,7 @@ function parsePipRequirements(requirementsPath) {
|
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
const content = fs.readFileSync(requirementsPath, 'utf8')
|
|
515
|
+
/** @type {Record<string, string>} */
|
|
515
516
|
const dependencies = {}
|
|
516
517
|
|
|
517
518
|
content.split('\n').forEach(line => {
|
|
@@ -529,7 +530,7 @@ function parsePipRequirements(requirementsPath) {
|
|
|
529
530
|
// Support dotted names (zope.interface), hyphens (pytest-cov), underscores (google_cloud)
|
|
530
531
|
// Also handle extras like fastapi[all] by capturing everything before the bracket
|
|
531
532
|
// Fixed: Replaced (.*) with ([^\s]*) to prevent catastrophic backtracking
|
|
532
|
-
// eslint-disable-next-line security/detect-unsafe-regex
|
|
533
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded character classes [\w.-], [\w,\s-], [^\s], anchored ^$, no nested quantifiers
|
|
533
534
|
const match = line.match(/^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/)
|
|
534
535
|
if (match) {
|
|
535
536
|
const [, name, _extras, operator, version] = match
|
|
@@ -552,12 +553,13 @@ function parsePipRequirements(requirementsPath) {
|
|
|
552
553
|
*/
|
|
553
554
|
function parsePyprojectToml(pyprojectPath) {
|
|
554
555
|
const content = fs.readFileSync(pyprojectPath, 'utf8')
|
|
556
|
+
/** @type {Record<string, string>} */
|
|
555
557
|
const dependencies = {}
|
|
556
558
|
|
|
557
559
|
// Parse PEP 621 list-style dependencies: dependencies = ["package>=1.0.0", ...]
|
|
558
560
|
// Match main dependencies array: dependencies = [...]
|
|
559
561
|
// Allow optional whitespace/comments after ] to handle: ] # end of deps
|
|
560
|
-
// eslint-disable-next-line security/detect-unsafe-regex
|
|
562
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Safe: lazy quantifier *? prevents backtracking, anchored ^$, bounded alternation
|
|
561
563
|
const mainDepPattern = /^dependencies\s*=\s*\[([\s\S]*?)\]\s*(?:#.*)?$/m
|
|
562
564
|
const mainMatch = mainDepPattern.exec(content)
|
|
563
565
|
|
|
@@ -574,7 +576,7 @@ function parsePyprojectToml(pyprojectPath) {
|
|
|
574
576
|
// Support dotted names, hyphens, underscores, and extras
|
|
575
577
|
// Fixed: Replaced ($|.*) with ([^\s]*) to prevent catastrophic backtracking
|
|
576
578
|
const match = depString.match(
|
|
577
|
-
/^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/ // eslint-disable-line security/detect-unsafe-regex
|
|
579
|
+
/^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/ // eslint-disable-line security/detect-unsafe-regex -- Safe: bounded character classes, anchored ^$, no nested quantifiers
|
|
578
580
|
)
|
|
579
581
|
if (match) {
|
|
580
582
|
const [, name, _extras, operator, version] = match
|
|
@@ -606,7 +608,7 @@ function parsePyprojectToml(pyprojectPath) {
|
|
|
606
608
|
const depString = pkgMatch[1].trim()
|
|
607
609
|
|
|
608
610
|
const match = depString.match(
|
|
609
|
-
/^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?(
|
|
611
|
+
/^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/ // eslint-disable-line security/detect-unsafe-regex -- Safe: bounded character classes, anchored ^$, no nested quantifiers
|
|
610
612
|
)
|
|
611
613
|
if (match) {
|
|
612
614
|
const [, name, _extras, operator, version] = match
|
|
@@ -829,7 +831,7 @@ function parseCargoToml(cargoPath) {
|
|
|
829
831
|
if (!trimmed || trimmed.startsWith('#')) continue
|
|
830
832
|
|
|
831
833
|
// Match simple pattern: name = "version"
|
|
832
|
-
// eslint-disable-next-line security/detect-unsafe-regex
|
|
834
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded groups \w+, [^"']+, anchored ^, no nested quantifiers
|
|
833
835
|
const simpleMatch = trimmed.match(/^(\w+(?:-\w+)*)\s*=\s*["']([^"']+)["']/)
|
|
834
836
|
if (simpleMatch) {
|
|
835
837
|
const [, name, version] = simpleMatch
|
|
@@ -840,7 +842,7 @@ function parseCargoToml(cargoPath) {
|
|
|
840
842
|
|
|
841
843
|
// Match complex pattern: name = { version = "...", ... }
|
|
842
844
|
const complexMatch = trimmed.match(
|
|
843
|
-
// eslint-disable-next-line security/detect-unsafe-regex
|
|
845
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded negated class [^}]*, anchored ^, no nested quantifiers
|
|
844
846
|
/^(\w+(?:-\w+)*)\s*=\s*\{[^}]*version\s*=\s*["']([^"']+)["']/
|
|
845
847
|
)
|
|
846
848
|
if (complexMatch) {
|
|
@@ -970,7 +972,7 @@ function parseGemfile(gemfilePath) {
|
|
|
970
972
|
|
|
971
973
|
// Match: gem 'rails', '~> 7.0' or gem 'rails'
|
|
972
974
|
const gemMatch = trimmed.match(
|
|
973
|
-
// eslint-disable-next-line security/detect-unsafe-regex
|
|
975
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded negated class [^'"]+, no nested quantifiers, processed line-by-line
|
|
974
976
|
/gem\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/
|
|
975
977
|
)
|
|
976
978
|
if (gemMatch) {
|
|
@@ -1270,11 +1272,11 @@ function generateBundlerGroups(bundlerFrameworks) {
|
|
|
1270
1272
|
/**
|
|
1271
1273
|
* Generate premium Dependabot configuration with multi-language framework-aware grouping
|
|
1272
1274
|
*
|
|
1273
|
-
* @param {Object} options - Configuration options
|
|
1274
|
-
* @param {string} options.projectPath - Path to project directory
|
|
1275
|
-
* @param {string} options.schedule - Update schedule (daily, weekly, monthly)
|
|
1276
|
-
* @param {string} options.day - Day of week for updates
|
|
1277
|
-
* @param {string} options.time - Time for updates
|
|
1275
|
+
* @param {Object} [options] - Configuration options
|
|
1276
|
+
* @param {string} [options.projectPath='.'] - Path to project directory
|
|
1277
|
+
* @param {string} [options.schedule='weekly'] - Update schedule (daily, weekly, monthly)
|
|
1278
|
+
* @param {string} [options.day='monday'] - Day of week for updates
|
|
1279
|
+
* @param {string} [options.time='09:00'] - Time for updates
|
|
1278
1280
|
* @returns {Object|null} Dependabot configuration object or null if not licensed
|
|
1279
1281
|
*/
|
|
1280
1282
|
function generatePremiumDependabotConfig(options = {}) {
|