create-qa-architect 5.0.0 → 5.0.6
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/nightly-gitleaks-verification.yml +1 -1
- package/.github/workflows/quality.yml +3 -0
- package/.github/workflows/release.yml +12 -10
- package/.github/workflows/weekly-audit.yml +173 -0
- package/LICENSE +66 -0
- package/README.md +44 -30
- package/config/defaults.js +22 -1
- package/config/quality-config.schema.json +1 -1
- package/create-saas-monetization.js +75 -27
- package/docs/ARCHITECTURE.md +53 -0
- package/docs/DEPLOYMENT.md +62 -0
- package/docs/PREFLIGHT_REPORT.md +108 -0
- package/docs/SLA_GATES.md +28 -0
- package/docs/TESTING.md +61 -0
- package/docs/security/SOC2_STARTER.md +29 -0
- package/lib/config-validator.js +8 -2
- package/lib/dependency-monitoring-basic.js +73 -26
- 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 +11 -10
- package/lib/package-utils.js +224 -8
- package/lib/project-maturity.js +1 -1
- package/lib/setup-enhancements.js +33 -0
- 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 +5 -4
- package/lib/validation/validation-factory.js +1 -1
- package/lib/yaml-utils.js +15 -10
- package/package.json +18 -13
- 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 +161 -44
- package/templates/QUALITY_TROUBLESHOOTING.md +403 -0
- package/templates/ci/circleci-config.yml +35 -0
- package/templates/ci/gitlab-ci.yml +47 -0
- package/templates/integration-tests/api-service.test.js +244 -0
- package/templates/integration-tests/frontend-app.test.js +267 -0
- package/templates/scripts/smart-test-strategy.sh +109 -0
- package/templates/test-stubs/e2e.smoke.test.js +12 -0
- package/templates/test-stubs/unit.test.js +7 -0
- package/legal/README.md +0 -106
- package/legal/copyright.md +0 -76
- package/legal/disclaimer.md +0 -146
- package/legal/privacy-policy.html +0 -324
- package/legal/privacy-policy.md +0 -196
- package/legal/terms-of-service.md +0 -224
- package/marketing/beta-user-email-campaign.md +0 -372
- package/marketing/landing-page.html +0 -721
|
@@ -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 = {}) {
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub API Integration for QA Architect
|
|
3
|
+
* Enables Dependabot alerts and security features via GitHub API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const https = require('https')
|
|
7
|
+
const { execSync } = require('child_process')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get GitHub token from environment or gh CLI
|
|
11
|
+
*/
|
|
12
|
+
function getGitHubToken() {
|
|
13
|
+
// Check environment variable first
|
|
14
|
+
if (process.env.GITHUB_TOKEN) {
|
|
15
|
+
return process.env.GITHUB_TOKEN
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Try to get from gh CLI
|
|
19
|
+
try {
|
|
20
|
+
const token = execSync('gh auth token', { encoding: 'utf8' }).trim()
|
|
21
|
+
if (token) return token
|
|
22
|
+
} catch {
|
|
23
|
+
// gh CLI not available or not authenticated
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get repository info from git remote
|
|
31
|
+
*/
|
|
32
|
+
function getRepoInfo(projectPath = '.') {
|
|
33
|
+
try {
|
|
34
|
+
const remoteUrl = execSync('git remote get-url origin', {
|
|
35
|
+
cwd: projectPath,
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
}).trim()
|
|
38
|
+
|
|
39
|
+
// Parse GitHub URL (https or ssh format)
|
|
40
|
+
const httpsMatch = remoteUrl.match(
|
|
41
|
+
/github\.com[/:]([^/]+)\/([^/.]+)(\.git)?$/
|
|
42
|
+
)
|
|
43
|
+
if (httpsMatch) {
|
|
44
|
+
return { owner: httpsMatch[1], repo: httpsMatch[2] }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return null
|
|
48
|
+
} catch {
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Make GitHub API request
|
|
55
|
+
*/
|
|
56
|
+
function githubRequest(method, path, token, data = null) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const options = {
|
|
59
|
+
hostname: 'api.github.com',
|
|
60
|
+
port: 443,
|
|
61
|
+
path: path,
|
|
62
|
+
method: method,
|
|
63
|
+
headers: {
|
|
64
|
+
Authorization: `Bearer ${token}`,
|
|
65
|
+
Accept: 'application/vnd.github+json',
|
|
66
|
+
'User-Agent': 'qa-architect',
|
|
67
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (data) {
|
|
72
|
+
options.headers['Content-Type'] = 'application/json'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const req = https.request(options, res => {
|
|
76
|
+
let body = ''
|
|
77
|
+
res.on('data', chunk => (body += chunk))
|
|
78
|
+
res.on('end', () => {
|
|
79
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
80
|
+
resolve({
|
|
81
|
+
status: res.statusCode,
|
|
82
|
+
data: body ? JSON.parse(body) : null,
|
|
83
|
+
})
|
|
84
|
+
} else if (res.statusCode === 204) {
|
|
85
|
+
resolve({ status: 204, data: null })
|
|
86
|
+
} else {
|
|
87
|
+
reject(
|
|
88
|
+
new Error(
|
|
89
|
+
`GitHub API error: ${res.statusCode} - ${body || res.statusMessage}`
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
req.on('error', reject)
|
|
97
|
+
|
|
98
|
+
if (data) {
|
|
99
|
+
req.write(JSON.stringify(data))
|
|
100
|
+
}
|
|
101
|
+
req.end()
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check if Dependabot alerts are enabled
|
|
107
|
+
*/
|
|
108
|
+
async function checkDependabotStatus(owner, repo, token) {
|
|
109
|
+
try {
|
|
110
|
+
await githubRequest(
|
|
111
|
+
'GET',
|
|
112
|
+
`/repos/${owner}/${repo}/vulnerability-alerts`,
|
|
113
|
+
token
|
|
114
|
+
)
|
|
115
|
+
return true // 204 means enabled
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error.message.includes('404')) {
|
|
118
|
+
return false // Not enabled
|
|
119
|
+
}
|
|
120
|
+
throw error
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Enable Dependabot alerts for a repository
|
|
126
|
+
*/
|
|
127
|
+
async function enableDependabotAlerts(owner, repo, token) {
|
|
128
|
+
try {
|
|
129
|
+
await githubRequest(
|
|
130
|
+
'PUT',
|
|
131
|
+
`/repos/${owner}/${repo}/vulnerability-alerts`,
|
|
132
|
+
token
|
|
133
|
+
)
|
|
134
|
+
return { success: true, message: 'Dependabot alerts enabled' }
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return { success: false, message: error.message }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Enable Dependabot security updates
|
|
142
|
+
*/
|
|
143
|
+
async function enableDependabotSecurityUpdates(owner, repo, token) {
|
|
144
|
+
try {
|
|
145
|
+
await githubRequest(
|
|
146
|
+
'PUT',
|
|
147
|
+
`/repos/${owner}/${repo}/automated-security-fixes`,
|
|
148
|
+
token
|
|
149
|
+
)
|
|
150
|
+
return { success: true, message: 'Dependabot security updates enabled' }
|
|
151
|
+
} catch (error) {
|
|
152
|
+
return { success: false, message: error.message }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Full setup: Enable all Dependabot features
|
|
158
|
+
*/
|
|
159
|
+
async function setupDependabot(projectPath = '.', options = {}) {
|
|
160
|
+
const { verbose = false } = options
|
|
161
|
+
const results = {
|
|
162
|
+
success: false,
|
|
163
|
+
repoInfo: null,
|
|
164
|
+
alerts: null,
|
|
165
|
+
securityUpdates: null,
|
|
166
|
+
errors: [],
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Get token
|
|
170
|
+
const token = getGitHubToken()
|
|
171
|
+
if (!token) {
|
|
172
|
+
results.errors.push(
|
|
173
|
+
'No GitHub token found. Set GITHUB_TOKEN env var or run `gh auth login`'
|
|
174
|
+
)
|
|
175
|
+
return results
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Get repo info
|
|
179
|
+
const repoInfo = getRepoInfo(projectPath)
|
|
180
|
+
if (!repoInfo) {
|
|
181
|
+
results.errors.push('Could not determine GitHub repository from git remote')
|
|
182
|
+
return results
|
|
183
|
+
}
|
|
184
|
+
results.repoInfo = repoInfo
|
|
185
|
+
|
|
186
|
+
if (verbose) {
|
|
187
|
+
console.log(`📦 Repository: ${repoInfo.owner}/${repoInfo.repo}`)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check current status
|
|
191
|
+
try {
|
|
192
|
+
const isEnabled = await checkDependabotStatus(
|
|
193
|
+
repoInfo.owner,
|
|
194
|
+
repoInfo.repo,
|
|
195
|
+
token
|
|
196
|
+
)
|
|
197
|
+
if (isEnabled) {
|
|
198
|
+
if (verbose) console.log('✅ Dependabot alerts already enabled')
|
|
199
|
+
results.alerts = { success: true, message: 'Already enabled' }
|
|
200
|
+
} else {
|
|
201
|
+
// Enable alerts
|
|
202
|
+
results.alerts = await enableDependabotAlerts(
|
|
203
|
+
repoInfo.owner,
|
|
204
|
+
repoInfo.repo,
|
|
205
|
+
token
|
|
206
|
+
)
|
|
207
|
+
if (verbose) {
|
|
208
|
+
console.log(
|
|
209
|
+
results.alerts.success
|
|
210
|
+
? '✅ Dependabot alerts enabled'
|
|
211
|
+
: `❌ Failed to enable alerts: ${results.alerts.message}`
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
results.errors.push(`Alerts check failed: ${error.message}`)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Enable security updates
|
|
220
|
+
try {
|
|
221
|
+
results.securityUpdates = await enableDependabotSecurityUpdates(
|
|
222
|
+
repoInfo.owner,
|
|
223
|
+
repoInfo.repo,
|
|
224
|
+
token
|
|
225
|
+
)
|
|
226
|
+
if (verbose) {
|
|
227
|
+
console.log(
|
|
228
|
+
results.securityUpdates.success
|
|
229
|
+
? '✅ Dependabot security updates enabled'
|
|
230
|
+
: `⚠️ Security updates: ${results.securityUpdates.message}`
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
results.errors.push(`Security updates failed: ${error.message}`)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
results.success = results.alerts?.success && results.errors.length === 0
|
|
238
|
+
|
|
239
|
+
return results
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = {
|
|
243
|
+
getGitHubToken,
|
|
244
|
+
getRepoInfo,
|
|
245
|
+
checkDependabotStatus,
|
|
246
|
+
enableDependabotAlerts,
|
|
247
|
+
enableDependabotSecurityUpdates,
|
|
248
|
+
setupDependabot,
|
|
249
|
+
}
|
package/lib/license-validator.js
CHANGED
|
@@ -26,7 +26,7 @@ class LicenseValidator {
|
|
|
26
26
|
// Allow enterprises to host their own registry
|
|
27
27
|
this.licenseDbUrl =
|
|
28
28
|
process.env.QAA_LICENSE_DB_URL ||
|
|
29
|
-
'https://
|
|
29
|
+
'https://vibebuildlab.com/api/licenses/qa-architect.json'
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
ensureLicenseDir() {
|
package/lib/licensing.js
CHANGED
|
@@ -216,7 +216,7 @@ function isDeveloperMode() {
|
|
|
216
216
|
if (fs.existsSync(developerMarkerFile)) {
|
|
217
217
|
return true
|
|
218
218
|
}
|
|
219
|
-
} catch
|
|
219
|
+
} catch {
|
|
220
220
|
// Ignore errors checking for marker file
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -338,7 +338,7 @@ function verifyLicenseSignature(payload, signature) {
|
|
|
338
338
|
Buffer.from(signature),
|
|
339
339
|
Buffer.from(expectedSignature)
|
|
340
340
|
)
|
|
341
|
-
} catch
|
|
341
|
+
} catch {
|
|
342
342
|
// If signature verification fails, treat as invalid
|
|
343
343
|
return false
|
|
344
344
|
}
|
|
@@ -396,7 +396,7 @@ function showUpgradeMessage(feature) {
|
|
|
396
396
|
console.log('')
|
|
397
397
|
console.log(' 🎁 Start 14-day free trial - no credit card required')
|
|
398
398
|
console.log('')
|
|
399
|
-
console.log('🚀 Upgrade: https://vibebuildlab.com/
|
|
399
|
+
console.log('🚀 Upgrade: https://vibebuildlab.com/tools/qa-architect')
|
|
400
400
|
console.log(
|
|
401
401
|
'🔑 Activate: npx create-qa-architect@latest --activate-license'
|
|
402
402
|
)
|
|
@@ -414,7 +414,7 @@ function showUpgradeMessage(feature) {
|
|
|
414
414
|
console.log(' ✅ Slack/email alerts for failures')
|
|
415
415
|
console.log(' ✅ Priority support (business hours)')
|
|
416
416
|
console.log('')
|
|
417
|
-
console.log('👥 Upgrade: https://vibebuildlab.com/
|
|
417
|
+
console.log('👥 Upgrade: https://vibebuildlab.com/tools/qa-architect')
|
|
418
418
|
} else if (license.tier === LICENSE_TIERS.TEAM) {
|
|
419
419
|
console.log('\n🏢 Upgrade to ENTERPRISE - $249/month (annual) + onboarding')
|
|
420
420
|
console.log('')
|
|
@@ -553,7 +553,7 @@ async function addLegitimateKey(
|
|
|
553
553
|
if (fs.existsSync(legitimateDBFile)) {
|
|
554
554
|
try {
|
|
555
555
|
database = JSON.parse(fs.readFileSync(legitimateDBFile, 'utf8'))
|
|
556
|
-
} catch
|
|
556
|
+
} catch {
|
|
557
557
|
console.error(
|
|
558
558
|
'Warning: Could not parse existing database, creating new one'
|
|
559
559
|
)
|
|
@@ -654,7 +654,7 @@ async function promptLicenseActivation() {
|
|
|
654
654
|
console.log(
|
|
655
655
|
' If you purchased this license, please contact support at:'
|
|
656
656
|
)
|
|
657
|
-
console.log(' Email: support@
|
|
657
|
+
console.log(' Email: support@vibebuildlab.com')
|
|
658
658
|
console.log(
|
|
659
659
|
' Include your license key and purchase email for verification.'
|
|
660
660
|
)
|
|
@@ -745,7 +745,7 @@ function loadUsage() {
|
|
|
745
745
|
|
|
746
746
|
return data
|
|
747
747
|
}
|
|
748
|
-
} catch
|
|
748
|
+
} catch {
|
|
749
749
|
// Ignore errors reading usage file
|
|
750
750
|
}
|
|
751
751
|
|
|
@@ -770,7 +770,7 @@ function saveUsage(usage) {
|
|
|
770
770
|
}
|
|
771
771
|
fs.writeFileSync(getUsageFile(), JSON.stringify(usage, null, 2))
|
|
772
772
|
return true
|
|
773
|
-
} catch
|
|
773
|
+
} catch {
|
|
774
774
|
return false
|
|
775
775
|
}
|
|
776
776
|
}
|
|
@@ -795,7 +795,8 @@ function checkUsageCaps(operation = 'general') {
|
|
|
795
795
|
usage: {
|
|
796
796
|
prePushRuns: usage.prePushRuns,
|
|
797
797
|
dependencyPRs: usage.dependencyPRs,
|
|
798
|
-
repos: usage.repos
|
|
798
|
+
repos: usage.repos || [],
|
|
799
|
+
repoCount: (usage.repos || []).length,
|
|
799
800
|
},
|
|
800
801
|
caps: {
|
|
801
802
|
maxPrePushRunsPerMonth: caps.maxPrePushRunsPerMonth,
|
|
@@ -957,7 +958,7 @@ function showLicenseStatus() {
|
|
|
957
958
|
// Show upgrade path
|
|
958
959
|
if (license.tier === LICENSE_TIERS.FREE) {
|
|
959
960
|
console.log('\n💡 Upgrade to PRO for unlimited access + security scanning')
|
|
960
|
-
console.log(' → https://vibebuildlab.com/
|
|
961
|
+
console.log(' → https://vibebuildlab.com/tools/qa-architect')
|
|
961
962
|
}
|
|
962
963
|
}
|
|
963
964
|
|