@codebakers/cli 2.5.0 โ 2.6.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/dist/mcp/server.js +242 -0
- package/package.json +1 -1
- package/src/mcp/server.ts +245 -0
package/dist/mcp/server.js
CHANGED
|
@@ -1646,6 +1646,248 @@ Just describe what you want to build! I'll automatically:
|
|
|
1646
1646
|
catch {
|
|
1647
1647
|
response += `*(TODO scan unavailable)*\n\n`;
|
|
1648
1648
|
}
|
|
1649
|
+
// Dependency Security Scan (npm audit)
|
|
1650
|
+
response += `### ๐ Dependency Security Scan\n\n`;
|
|
1651
|
+
try {
|
|
1652
|
+
const auditOutput = (0, child_process_1.execSync)('npm audit --json 2>/dev/null', {
|
|
1653
|
+
cwd,
|
|
1654
|
+
encoding: 'utf-8',
|
|
1655
|
+
timeout: 30000,
|
|
1656
|
+
});
|
|
1657
|
+
const audit = JSON.parse(auditOutput);
|
|
1658
|
+
const vulns = audit.metadata?.vulnerabilities || {};
|
|
1659
|
+
const total = (vulns.critical || 0) + (vulns.high || 0) + (vulns.moderate || 0) + (vulns.low || 0);
|
|
1660
|
+
if (total > 0) {
|
|
1661
|
+
response += `Found **${total} vulnerabilities**:\n\n`;
|
|
1662
|
+
if (vulns.critical > 0)
|
|
1663
|
+
response += `- ๐ด **${vulns.critical} Critical**\n`;
|
|
1664
|
+
if (vulns.high > 0)
|
|
1665
|
+
response += `- ๐ **${vulns.high} High**\n`;
|
|
1666
|
+
if (vulns.moderate > 0)
|
|
1667
|
+
response += `- ๐ก **${vulns.moderate} Moderate**\n`;
|
|
1668
|
+
if (vulns.low > 0)
|
|
1669
|
+
response += `- ๐ข **${vulns.low} Low**\n`;
|
|
1670
|
+
response += `\n*Run \`npm audit fix\` to auto-fix, or \`npm audit\` for details.*\n\n`;
|
|
1671
|
+
}
|
|
1672
|
+
else {
|
|
1673
|
+
response += `โ
No known vulnerabilities in dependencies!\n\n`;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
catch (error) {
|
|
1677
|
+
// npm audit exits with non-zero if vulnerabilities found
|
|
1678
|
+
const execError = error;
|
|
1679
|
+
if (execError.stdout) {
|
|
1680
|
+
try {
|
|
1681
|
+
const audit = JSON.parse(execError.stdout);
|
|
1682
|
+
const vulns = audit.metadata?.vulnerabilities || {};
|
|
1683
|
+
const total = (vulns.critical || 0) + (vulns.high || 0) + (vulns.moderate || 0) + (vulns.low || 0);
|
|
1684
|
+
if (total > 0) {
|
|
1685
|
+
response += `Found **${total} vulnerabilities**:\n\n`;
|
|
1686
|
+
if (vulns.critical > 0)
|
|
1687
|
+
response += `- ๐ด **${vulns.critical} Critical**\n`;
|
|
1688
|
+
if (vulns.high > 0)
|
|
1689
|
+
response += `- ๐ **${vulns.high} High**\n`;
|
|
1690
|
+
if (vulns.moderate > 0)
|
|
1691
|
+
response += `- ๐ก **${vulns.moderate} Moderate**\n`;
|
|
1692
|
+
if (vulns.low > 0)
|
|
1693
|
+
response += `- ๐ข **${vulns.low} Low**\n`;
|
|
1694
|
+
response += `\n*Run \`npm audit fix\` to auto-fix, or \`npm audit\` for details.*\n\n`;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
catch {
|
|
1698
|
+
response += `*(Dependency scan unavailable)*\n\n`;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
else {
|
|
1702
|
+
response += `*(Dependency scan unavailable)*\n\n`;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
// TypeScript Strictness Check
|
|
1706
|
+
response += `### ๐ TypeScript Configuration\n\n`;
|
|
1707
|
+
try {
|
|
1708
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
1709
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
1710
|
+
const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
|
|
1711
|
+
const compilerOptions = tsconfig.compilerOptions || {};
|
|
1712
|
+
const checks = [
|
|
1713
|
+
{ name: 'strict', value: compilerOptions.strict, recommended: true },
|
|
1714
|
+
{ name: 'noImplicitAny', value: compilerOptions.noImplicitAny, recommended: true },
|
|
1715
|
+
{ name: 'strictNullChecks', value: compilerOptions.strictNullChecks, recommended: true },
|
|
1716
|
+
{ name: 'noUnusedLocals', value: compilerOptions.noUnusedLocals, recommended: true },
|
|
1717
|
+
{ name: 'noUnusedParameters', value: compilerOptions.noUnusedParameters, recommended: true },
|
|
1718
|
+
];
|
|
1719
|
+
const enabled = checks.filter(c => c.value === true);
|
|
1720
|
+
const missing = checks.filter(c => c.value !== true && c.recommended);
|
|
1721
|
+
if (compilerOptions.strict === true) {
|
|
1722
|
+
response += `โ
**Strict mode enabled** - Good!\n\n`;
|
|
1723
|
+
}
|
|
1724
|
+
else {
|
|
1725
|
+
response += `โ ๏ธ **Strict mode not enabled**\n\n`;
|
|
1726
|
+
if (missing.length > 0) {
|
|
1727
|
+
response += `Missing recommended options:\n`;
|
|
1728
|
+
missing.forEach(m => {
|
|
1729
|
+
response += `- \`${m.name}: true\`\n`;
|
|
1730
|
+
});
|
|
1731
|
+
response += `\n`;
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
// Check for any types
|
|
1735
|
+
try {
|
|
1736
|
+
const anyCount = (0, child_process_1.execSync)('grep -r ": any" --include="*.ts" --include="*.tsx" 2>/dev/null | grep -v node_modules | wc -l', { cwd, encoding: 'utf-8', timeout: 10000 }).trim();
|
|
1737
|
+
const count = parseInt(anyCount);
|
|
1738
|
+
if (count > 10) {
|
|
1739
|
+
response += `โ ๏ธ Found **${count}** uses of \`: any\` - consider typing these\n\n`;
|
|
1740
|
+
}
|
|
1741
|
+
else if (count > 0) {
|
|
1742
|
+
response += `๐ Found **${count}** uses of \`: any\`\n\n`;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
catch {
|
|
1746
|
+
// Ignore
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
else {
|
|
1750
|
+
response += `โ ๏ธ No tsconfig.json found - not a TypeScript project?\n\n`;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
catch {
|
|
1754
|
+
response += `*(TypeScript check unavailable)*\n\n`;
|
|
1755
|
+
}
|
|
1756
|
+
// Environment Variable Audit
|
|
1757
|
+
response += `### ๐ Environment Variable Audit\n\n`;
|
|
1758
|
+
try {
|
|
1759
|
+
const envExamplePath = path.join(cwd, '.env.example');
|
|
1760
|
+
const envLocalPath = path.join(cwd, '.env.local');
|
|
1761
|
+
const envPath = path.join(cwd, '.env');
|
|
1762
|
+
const hasEnvExample = fs.existsSync(envExamplePath);
|
|
1763
|
+
const hasEnvLocal = fs.existsSync(envLocalPath);
|
|
1764
|
+
const hasEnv = fs.existsSync(envPath);
|
|
1765
|
+
if (hasEnvExample) {
|
|
1766
|
+
response += `โ
\`.env.example\` exists - good for documentation\n`;
|
|
1767
|
+
}
|
|
1768
|
+
else {
|
|
1769
|
+
response += `โ ๏ธ No \`.env.example\` - add one for team onboarding\n`;
|
|
1770
|
+
}
|
|
1771
|
+
// Check for hardcoded secrets in code
|
|
1772
|
+
try {
|
|
1773
|
+
const secretPatterns = [
|
|
1774
|
+
'sk-[a-zA-Z0-9]{20,}', // OpenAI keys
|
|
1775
|
+
'sk_live_[a-zA-Z0-9]+', // Stripe live keys
|
|
1776
|
+
'pk_live_[a-zA-Z0-9]+', // Stripe public live keys
|
|
1777
|
+
'ghp_[a-zA-Z0-9]+', // GitHub tokens
|
|
1778
|
+
'AKIA[A-Z0-9]{16}', // AWS access keys
|
|
1779
|
+
];
|
|
1780
|
+
let secretsFound = 0;
|
|
1781
|
+
for (const pattern of secretPatterns) {
|
|
1782
|
+
try {
|
|
1783
|
+
const matches = (0, child_process_1.execSync)(`grep -rE "${pattern}" --include="*.ts" --include="*.tsx" --include="*.js" 2>/dev/null | grep -v node_modules | grep -v ".env" | wc -l`, { cwd, encoding: 'utf-8', timeout: 5000 }).trim();
|
|
1784
|
+
secretsFound += parseInt(matches) || 0;
|
|
1785
|
+
}
|
|
1786
|
+
catch {
|
|
1787
|
+
// Pattern not found
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
if (secretsFound > 0) {
|
|
1791
|
+
response += `\n๐ด **CRITICAL: Found ${secretsFound} potential hardcoded secrets in code!**\n`;
|
|
1792
|
+
response += `*These should be moved to environment variables immediately.*\n`;
|
|
1793
|
+
}
|
|
1794
|
+
else {
|
|
1795
|
+
response += `\nโ
No hardcoded secrets detected in code\n`;
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
catch {
|
|
1799
|
+
// Ignore
|
|
1800
|
+
}
|
|
1801
|
+
// Check if .env is gitignored
|
|
1802
|
+
try {
|
|
1803
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
1804
|
+
if (fs.existsSync(gitignorePath)) {
|
|
1805
|
+
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
1806
|
+
if (!gitignore.includes('.env')) {
|
|
1807
|
+
response += `\nโ ๏ธ \`.env\` not in .gitignore - secrets could be committed!\n`;
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
catch {
|
|
1812
|
+
// Ignore
|
|
1813
|
+
}
|
|
1814
|
+
response += `\n`;
|
|
1815
|
+
}
|
|
1816
|
+
catch {
|
|
1817
|
+
response += `*(Environment audit unavailable)*\n\n`;
|
|
1818
|
+
}
|
|
1819
|
+
// Test Coverage Analysis
|
|
1820
|
+
response += `### ๐งช Test Coverage\n\n`;
|
|
1821
|
+
try {
|
|
1822
|
+
const hasPlaywright = context.dependencies.includes('@playwright/test');
|
|
1823
|
+
const hasVitest = context.dependencies.includes('vitest');
|
|
1824
|
+
const hasJest = context.dependencies.includes('jest');
|
|
1825
|
+
if (hasPlaywright || hasVitest || hasJest) {
|
|
1826
|
+
const framework = hasPlaywright ? 'Playwright' : hasVitest ? 'Vitest' : 'Jest';
|
|
1827
|
+
response += `โ
Test framework detected: **${framework}**\n\n`;
|
|
1828
|
+
// Count test files
|
|
1829
|
+
try {
|
|
1830
|
+
const testFiles = (0, child_process_1.execSync)('find . -name "*.test.ts" -o -name "*.test.tsx" -o -name "*.spec.ts" -o -name "*.spec.tsx" 2>/dev/null | grep -v node_modules | wc -l', { cwd, encoding: 'utf-8', timeout: 10000 }).trim();
|
|
1831
|
+
const count = parseInt(testFiles);
|
|
1832
|
+
if (count > 0) {
|
|
1833
|
+
response += `Found **${count} test files**\n\n`;
|
|
1834
|
+
}
|
|
1835
|
+
else {
|
|
1836
|
+
response += `โ ๏ธ No test files found - add tests!\n\n`;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
catch {
|
|
1840
|
+
// Ignore
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
else {
|
|
1844
|
+
response += `โ ๏ธ **No test framework detected**\n\n`;
|
|
1845
|
+
response += `Recommended: Add \`vitest\` or \`@playwright/test\`\n\n`;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
catch {
|
|
1849
|
+
response += `*(Test analysis unavailable)*\n\n`;
|
|
1850
|
+
}
|
|
1851
|
+
// API Endpoint Inventory
|
|
1852
|
+
response += `### ๐ API Endpoint Inventory\n\n`;
|
|
1853
|
+
if (context.existingApiRoutes.length > 0) {
|
|
1854
|
+
response += `Found **${context.existingApiRoutes.length} API routes**:\n\n`;
|
|
1855
|
+
// Check for auth protection on routes
|
|
1856
|
+
let protectedCount = 0;
|
|
1857
|
+
let unprotectedCount = 0;
|
|
1858
|
+
for (const route of context.existingApiRoutes.slice(0, 10)) {
|
|
1859
|
+
try {
|
|
1860
|
+
const routePath = path.join(cwd, route);
|
|
1861
|
+
if (fs.existsSync(routePath)) {
|
|
1862
|
+
const content = fs.readFileSync(routePath, 'utf-8');
|
|
1863
|
+
const hasAuth = content.includes('getServerSession') ||
|
|
1864
|
+
content.includes('auth(') ||
|
|
1865
|
+
content.includes('requireAuth') ||
|
|
1866
|
+
content.includes('Authorization') ||
|
|
1867
|
+
content.includes('authenticate');
|
|
1868
|
+
if (hasAuth) {
|
|
1869
|
+
protectedCount++;
|
|
1870
|
+
}
|
|
1871
|
+
else {
|
|
1872
|
+
unprotectedCount++;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
catch {
|
|
1877
|
+
// Ignore
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
if (protectedCount > 0 || unprotectedCount > 0) {
|
|
1881
|
+
response += `- ๐ **${protectedCount}** routes with auth checks\n`;
|
|
1882
|
+
response += `- ๐ **${unprotectedCount}** routes without visible auth\n\n`;
|
|
1883
|
+
if (unprotectedCount > protectedCount) {
|
|
1884
|
+
response += `โ ๏ธ *Many routes lack visible auth - review if intentional*\n\n`;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
else {
|
|
1889
|
+
response += `No API routes detected yet.\n\n`;
|
|
1890
|
+
}
|
|
1649
1891
|
response += `---\n\n`;
|
|
1650
1892
|
// Scan for upgrade opportunities
|
|
1651
1893
|
response += `## Upgrade Opportunities\n\n`;
|
package/package.json
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -1859,6 +1859,251 @@ Just describe what you want to build! I'll automatically:
|
|
|
1859
1859
|
response += `*(TODO scan unavailable)*\n\n`;
|
|
1860
1860
|
}
|
|
1861
1861
|
|
|
1862
|
+
// Dependency Security Scan (npm audit)
|
|
1863
|
+
response += `### ๐ Dependency Security Scan\n\n`;
|
|
1864
|
+
try {
|
|
1865
|
+
const auditOutput = execSync('npm audit --json 2>/dev/null', {
|
|
1866
|
+
cwd,
|
|
1867
|
+
encoding: 'utf-8',
|
|
1868
|
+
timeout: 30000,
|
|
1869
|
+
});
|
|
1870
|
+
const audit = JSON.parse(auditOutput);
|
|
1871
|
+
const vulns = audit.metadata?.vulnerabilities || {};
|
|
1872
|
+
const total = (vulns.critical || 0) + (vulns.high || 0) + (vulns.moderate || 0) + (vulns.low || 0);
|
|
1873
|
+
|
|
1874
|
+
if (total > 0) {
|
|
1875
|
+
response += `Found **${total} vulnerabilities**:\n\n`;
|
|
1876
|
+
if (vulns.critical > 0) response += `- ๐ด **${vulns.critical} Critical**\n`;
|
|
1877
|
+
if (vulns.high > 0) response += `- ๐ **${vulns.high} High**\n`;
|
|
1878
|
+
if (vulns.moderate > 0) response += `- ๐ก **${vulns.moderate} Moderate**\n`;
|
|
1879
|
+
if (vulns.low > 0) response += `- ๐ข **${vulns.low} Low**\n`;
|
|
1880
|
+
response += `\n*Run \`npm audit fix\` to auto-fix, or \`npm audit\` for details.*\n\n`;
|
|
1881
|
+
} else {
|
|
1882
|
+
response += `โ
No known vulnerabilities in dependencies!\n\n`;
|
|
1883
|
+
}
|
|
1884
|
+
} catch (error) {
|
|
1885
|
+
// npm audit exits with non-zero if vulnerabilities found
|
|
1886
|
+
const execError = error as { stdout?: string };
|
|
1887
|
+
if (execError.stdout) {
|
|
1888
|
+
try {
|
|
1889
|
+
const audit = JSON.parse(execError.stdout);
|
|
1890
|
+
const vulns = audit.metadata?.vulnerabilities || {};
|
|
1891
|
+
const total = (vulns.critical || 0) + (vulns.high || 0) + (vulns.moderate || 0) + (vulns.low || 0);
|
|
1892
|
+
|
|
1893
|
+
if (total > 0) {
|
|
1894
|
+
response += `Found **${total} vulnerabilities**:\n\n`;
|
|
1895
|
+
if (vulns.critical > 0) response += `- ๐ด **${vulns.critical} Critical**\n`;
|
|
1896
|
+
if (vulns.high > 0) response += `- ๐ **${vulns.high} High**\n`;
|
|
1897
|
+
if (vulns.moderate > 0) response += `- ๐ก **${vulns.moderate} Moderate**\n`;
|
|
1898
|
+
if (vulns.low > 0) response += `- ๐ข **${vulns.low} Low**\n`;
|
|
1899
|
+
response += `\n*Run \`npm audit fix\` to auto-fix, or \`npm audit\` for details.*\n\n`;
|
|
1900
|
+
}
|
|
1901
|
+
} catch {
|
|
1902
|
+
response += `*(Dependency scan unavailable)*\n\n`;
|
|
1903
|
+
}
|
|
1904
|
+
} else {
|
|
1905
|
+
response += `*(Dependency scan unavailable)*\n\n`;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
// TypeScript Strictness Check
|
|
1910
|
+
response += `### ๐ TypeScript Configuration\n\n`;
|
|
1911
|
+
try {
|
|
1912
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
1913
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
1914
|
+
const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
|
|
1915
|
+
const compilerOptions = tsconfig.compilerOptions || {};
|
|
1916
|
+
|
|
1917
|
+
const checks = [
|
|
1918
|
+
{ name: 'strict', value: compilerOptions.strict, recommended: true },
|
|
1919
|
+
{ name: 'noImplicitAny', value: compilerOptions.noImplicitAny, recommended: true },
|
|
1920
|
+
{ name: 'strictNullChecks', value: compilerOptions.strictNullChecks, recommended: true },
|
|
1921
|
+
{ name: 'noUnusedLocals', value: compilerOptions.noUnusedLocals, recommended: true },
|
|
1922
|
+
{ name: 'noUnusedParameters', value: compilerOptions.noUnusedParameters, recommended: true },
|
|
1923
|
+
];
|
|
1924
|
+
|
|
1925
|
+
const enabled = checks.filter(c => c.value === true);
|
|
1926
|
+
const missing = checks.filter(c => c.value !== true && c.recommended);
|
|
1927
|
+
|
|
1928
|
+
if (compilerOptions.strict === true) {
|
|
1929
|
+
response += `โ
**Strict mode enabled** - Good!\n\n`;
|
|
1930
|
+
} else {
|
|
1931
|
+
response += `โ ๏ธ **Strict mode not enabled**\n\n`;
|
|
1932
|
+
if (missing.length > 0) {
|
|
1933
|
+
response += `Missing recommended options:\n`;
|
|
1934
|
+
missing.forEach(m => {
|
|
1935
|
+
response += `- \`${m.name}: true\`\n`;
|
|
1936
|
+
});
|
|
1937
|
+
response += `\n`;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// Check for any types
|
|
1942
|
+
try {
|
|
1943
|
+
const anyCount = execSync(
|
|
1944
|
+
'grep -r ": any" --include="*.ts" --include="*.tsx" 2>/dev/null | grep -v node_modules | wc -l',
|
|
1945
|
+
{ cwd, encoding: 'utf-8', timeout: 10000 }
|
|
1946
|
+
).trim();
|
|
1947
|
+
const count = parseInt(anyCount);
|
|
1948
|
+
if (count > 10) {
|
|
1949
|
+
response += `โ ๏ธ Found **${count}** uses of \`: any\` - consider typing these\n\n`;
|
|
1950
|
+
} else if (count > 0) {
|
|
1951
|
+
response += `๐ Found **${count}** uses of \`: any\`\n\n`;
|
|
1952
|
+
}
|
|
1953
|
+
} catch {
|
|
1954
|
+
// Ignore
|
|
1955
|
+
}
|
|
1956
|
+
} else {
|
|
1957
|
+
response += `โ ๏ธ No tsconfig.json found - not a TypeScript project?\n\n`;
|
|
1958
|
+
}
|
|
1959
|
+
} catch {
|
|
1960
|
+
response += `*(TypeScript check unavailable)*\n\n`;
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
// Environment Variable Audit
|
|
1964
|
+
response += `### ๐ Environment Variable Audit\n\n`;
|
|
1965
|
+
try {
|
|
1966
|
+
const envExamplePath = path.join(cwd, '.env.example');
|
|
1967
|
+
const envLocalPath = path.join(cwd, '.env.local');
|
|
1968
|
+
const envPath = path.join(cwd, '.env');
|
|
1969
|
+
|
|
1970
|
+
const hasEnvExample = fs.existsSync(envExamplePath);
|
|
1971
|
+
const hasEnvLocal = fs.existsSync(envLocalPath);
|
|
1972
|
+
const hasEnv = fs.existsSync(envPath);
|
|
1973
|
+
|
|
1974
|
+
if (hasEnvExample) {
|
|
1975
|
+
response += `โ
\`.env.example\` exists - good for documentation\n`;
|
|
1976
|
+
} else {
|
|
1977
|
+
response += `โ ๏ธ No \`.env.example\` - add one for team onboarding\n`;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// Check for hardcoded secrets in code
|
|
1981
|
+
try {
|
|
1982
|
+
const secretPatterns = [
|
|
1983
|
+
'sk-[a-zA-Z0-9]{20,}', // OpenAI keys
|
|
1984
|
+
'sk_live_[a-zA-Z0-9]+', // Stripe live keys
|
|
1985
|
+
'pk_live_[a-zA-Z0-9]+', // Stripe public live keys
|
|
1986
|
+
'ghp_[a-zA-Z0-9]+', // GitHub tokens
|
|
1987
|
+
'AKIA[A-Z0-9]{16}', // AWS access keys
|
|
1988
|
+
];
|
|
1989
|
+
|
|
1990
|
+
let secretsFound = 0;
|
|
1991
|
+
for (const pattern of secretPatterns) {
|
|
1992
|
+
try {
|
|
1993
|
+
const matches = execSync(
|
|
1994
|
+
`grep -rE "${pattern}" --include="*.ts" --include="*.tsx" --include="*.js" 2>/dev/null | grep -v node_modules | grep -v ".env" | wc -l`,
|
|
1995
|
+
{ cwd, encoding: 'utf-8', timeout: 5000 }
|
|
1996
|
+
).trim();
|
|
1997
|
+
secretsFound += parseInt(matches) || 0;
|
|
1998
|
+
} catch {
|
|
1999
|
+
// Pattern not found
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
if (secretsFound > 0) {
|
|
2004
|
+
response += `\n๐ด **CRITICAL: Found ${secretsFound} potential hardcoded secrets in code!**\n`;
|
|
2005
|
+
response += `*These should be moved to environment variables immediately.*\n`;
|
|
2006
|
+
} else {
|
|
2007
|
+
response += `\nโ
No hardcoded secrets detected in code\n`;
|
|
2008
|
+
}
|
|
2009
|
+
} catch {
|
|
2010
|
+
// Ignore
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
// Check if .env is gitignored
|
|
2014
|
+
try {
|
|
2015
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
2016
|
+
if (fs.existsSync(gitignorePath)) {
|
|
2017
|
+
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
2018
|
+
if (!gitignore.includes('.env')) {
|
|
2019
|
+
response += `\nโ ๏ธ \`.env\` not in .gitignore - secrets could be committed!\n`;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
} catch {
|
|
2023
|
+
// Ignore
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
response += `\n`;
|
|
2027
|
+
} catch {
|
|
2028
|
+
response += `*(Environment audit unavailable)*\n\n`;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// Test Coverage Analysis
|
|
2032
|
+
response += `### ๐งช Test Coverage\n\n`;
|
|
2033
|
+
try {
|
|
2034
|
+
const hasPlaywright = context.dependencies.includes('@playwright/test');
|
|
2035
|
+
const hasVitest = context.dependencies.includes('vitest');
|
|
2036
|
+
const hasJest = context.dependencies.includes('jest');
|
|
2037
|
+
|
|
2038
|
+
if (hasPlaywright || hasVitest || hasJest) {
|
|
2039
|
+
const framework = hasPlaywright ? 'Playwright' : hasVitest ? 'Vitest' : 'Jest';
|
|
2040
|
+
response += `โ
Test framework detected: **${framework}**\n\n`;
|
|
2041
|
+
|
|
2042
|
+
// Count test files
|
|
2043
|
+
try {
|
|
2044
|
+
const testFiles = execSync(
|
|
2045
|
+
'find . -name "*.test.ts" -o -name "*.test.tsx" -o -name "*.spec.ts" -o -name "*.spec.tsx" 2>/dev/null | grep -v node_modules | wc -l',
|
|
2046
|
+
{ cwd, encoding: 'utf-8', timeout: 10000 }
|
|
2047
|
+
).trim();
|
|
2048
|
+
const count = parseInt(testFiles);
|
|
2049
|
+
if (count > 0) {
|
|
2050
|
+
response += `Found **${count} test files**\n\n`;
|
|
2051
|
+
} else {
|
|
2052
|
+
response += `โ ๏ธ No test files found - add tests!\n\n`;
|
|
2053
|
+
}
|
|
2054
|
+
} catch {
|
|
2055
|
+
// Ignore
|
|
2056
|
+
}
|
|
2057
|
+
} else {
|
|
2058
|
+
response += `โ ๏ธ **No test framework detected**\n\n`;
|
|
2059
|
+
response += `Recommended: Add \`vitest\` or \`@playwright/test\`\n\n`;
|
|
2060
|
+
}
|
|
2061
|
+
} catch {
|
|
2062
|
+
response += `*(Test analysis unavailable)*\n\n`;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
// API Endpoint Inventory
|
|
2066
|
+
response += `### ๐ API Endpoint Inventory\n\n`;
|
|
2067
|
+
if (context.existingApiRoutes.length > 0) {
|
|
2068
|
+
response += `Found **${context.existingApiRoutes.length} API routes**:\n\n`;
|
|
2069
|
+
|
|
2070
|
+
// Check for auth protection on routes
|
|
2071
|
+
let protectedCount = 0;
|
|
2072
|
+
let unprotectedCount = 0;
|
|
2073
|
+
|
|
2074
|
+
for (const route of context.existingApiRoutes.slice(0, 10)) {
|
|
2075
|
+
try {
|
|
2076
|
+
const routePath = path.join(cwd, route);
|
|
2077
|
+
if (fs.existsSync(routePath)) {
|
|
2078
|
+
const content = fs.readFileSync(routePath, 'utf-8');
|
|
2079
|
+
const hasAuth = content.includes('getServerSession') ||
|
|
2080
|
+
content.includes('auth(') ||
|
|
2081
|
+
content.includes('requireAuth') ||
|
|
2082
|
+
content.includes('Authorization') ||
|
|
2083
|
+
content.includes('authenticate');
|
|
2084
|
+
if (hasAuth) {
|
|
2085
|
+
protectedCount++;
|
|
2086
|
+
} else {
|
|
2087
|
+
unprotectedCount++;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
} catch {
|
|
2091
|
+
// Ignore
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
if (protectedCount > 0 || unprotectedCount > 0) {
|
|
2096
|
+
response += `- ๐ **${protectedCount}** routes with auth checks\n`;
|
|
2097
|
+
response += `- ๐ **${unprotectedCount}** routes without visible auth\n\n`;
|
|
2098
|
+
|
|
2099
|
+
if (unprotectedCount > protectedCount) {
|
|
2100
|
+
response += `โ ๏ธ *Many routes lack visible auth - review if intentional*\n\n`;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
} else {
|
|
2104
|
+
response += `No API routes detected yet.\n\n`;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
1862
2107
|
response += `---\n\n`;
|
|
1863
2108
|
|
|
1864
2109
|
// Scan for upgrade opportunities
|