@vibgrate/cli 1.0.66 → 1.0.67
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.
|
@@ -319,20 +319,6 @@ function formatText(artifact) {
|
|
|
319
319
|
if (artifact.extended?.architecture) {
|
|
320
320
|
lines.push(...formatArchitectureDiagram(artifact.extended.architecture));
|
|
321
321
|
}
|
|
322
|
-
if (artifact.relationshipDiagram?.mermaid) {
|
|
323
|
-
lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
324
|
-
lines.push(chalk.bold.cyan("\u2551 Project Relationship Diagram \u2551"));
|
|
325
|
-
lines.push(chalk.bold.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
326
|
-
lines.push("");
|
|
327
|
-
lines.push(chalk.bold(" Mermaid") + chalk.dim(" (copy into https://mermaid.live or a ```mermaid code block)"));
|
|
328
|
-
lines.push("");
|
|
329
|
-
lines.push(chalk.dim(" ```mermaid"));
|
|
330
|
-
for (const mLine of artifact.relationshipDiagram.mermaid.split("\n")) {
|
|
331
|
-
lines.push(chalk.dim(` ${mLine}`));
|
|
332
|
-
}
|
|
333
|
-
lines.push(chalk.dim(" ```"));
|
|
334
|
-
lines.push("");
|
|
335
|
-
}
|
|
336
322
|
if (artifact.solutions && artifact.solutions.length > 0) {
|
|
337
323
|
lines.push(chalk.bold.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
338
324
|
lines.push(chalk.bold.cyan("\u2551 Solution Drift Summary \u2551"));
|
|
@@ -1308,7 +1294,9 @@ function compactApiSurface(result) {
|
|
|
1308
1294
|
parameters: [],
|
|
1309
1295
|
// Don't include params
|
|
1310
1296
|
configOptions: [],
|
|
1311
|
-
authHints: []
|
|
1297
|
+
authHints: [],
|
|
1298
|
+
files: []
|
|
1299
|
+
// Don't include file paths
|
|
1312
1300
|
}));
|
|
1313
1301
|
return {
|
|
1314
1302
|
integrations: uniqueIntegrations,
|
|
@@ -7726,6 +7714,17 @@ function extract(content, pattern, sourceFile) {
|
|
|
7726
7714
|
}
|
|
7727
7715
|
return out;
|
|
7728
7716
|
}
|
|
7717
|
+
var LOCKFILE_NAMES = /* @__PURE__ */ new Set([
|
|
7718
|
+
"package-lock.json",
|
|
7719
|
+
"pnpm-lock.yaml",
|
|
7720
|
+
"yarn.lock",
|
|
7721
|
+
"composer.lock",
|
|
7722
|
+
"Gemfile.lock",
|
|
7723
|
+
"Cargo.lock",
|
|
7724
|
+
"poetry.lock",
|
|
7725
|
+
"Pipfile.lock",
|
|
7726
|
+
"packages.lock.json"
|
|
7727
|
+
]);
|
|
7729
7728
|
function detectDbBrand(raw) {
|
|
7730
7729
|
const value = raw.toLowerCase();
|
|
7731
7730
|
if (value.includes("postgres")) return { kind: "sql", brand: "PostgreSQL", version: null, evidence: raw };
|
|
@@ -7736,6 +7735,7 @@ function detectDbBrand(raw) {
|
|
|
7736
7735
|
if (value.includes("mongodb")) return { kind: "nosql", brand: "MongoDB", version: null, evidence: raw };
|
|
7737
7736
|
if (value.includes("redis")) return { kind: "nosql", brand: "Redis", version: null, evidence: raw };
|
|
7738
7737
|
if (value.includes("cassandra")) return { kind: "nosql", brand: "Cassandra", version: null, evidence: raw };
|
|
7738
|
+
if (value.includes("cosmosdb") || value.includes("@azure/cosmos") || value.includes("cosmos")) return { kind: "nosql", brand: "CosmosDB", version: null, evidence: raw };
|
|
7739
7739
|
if (value.includes("dynamodb")) return { kind: "nosql", brand: "DynamoDB", version: null, evidence: raw };
|
|
7740
7740
|
if (value.includes("neo4j")) return { kind: "nosql", brand: "Neo4j", version: null, evidence: raw };
|
|
7741
7741
|
return null;
|
|
@@ -7817,13 +7817,16 @@ async function scanDataStores(rootDir, fileCache) {
|
|
|
7817
7817
|
for (const file of files) {
|
|
7818
7818
|
const connStrings = extract(file.content, /\b(?:postgres(?:ql)?:\/\/[^\s'"`]+|mysql:\/\/[^\s'"`]+|mongodb(?:\+srv)?:\/\/[^\s'"`]+|redis:\/\/[^\s'"`]+)\b/gi, file.relPath);
|
|
7819
7819
|
result.connectionStrings.push(...connStrings);
|
|
7820
|
-
const
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7820
|
+
const isLockFile = LOCKFILE_NAMES.has(path23.basename(file.relPath));
|
|
7821
|
+
if (!isLockFile) {
|
|
7822
|
+
const dbEvidence = [
|
|
7823
|
+
...extract(file.content, /\b(?:postgres|postgresql|mysql|mariadb|mssql|sqlserver|oracle|sqlite|mongodb|redis|cassandra|cosmosdb|cosmos|dynamodb|neo4j)\b/gi, file.relPath),
|
|
7824
|
+
...connStrings
|
|
7825
|
+
];
|
|
7826
|
+
for (const evidence of dbEvidence) {
|
|
7827
|
+
const detected = detectDbBrand(evidence);
|
|
7828
|
+
if (detected) dbTechnologies.push(detected);
|
|
7829
|
+
}
|
|
7827
7830
|
}
|
|
7828
7831
|
result.connectionPoolSettings.push(...extract(file.content, /\b(?:pool(?:Size|_size)?|maxPoolSize|minPoolSize|connectionLimit)\b[^\n]*/gi, file.relPath));
|
|
7829
7832
|
result.replicationSettings.push(...extract(file.content, /\b(?:replication|cluster|replicaSet)\b[^\n]*/gi, file.relPath));
|
|
@@ -7840,7 +7843,10 @@ async function scanDataStores(rootDir, fileCache) {
|
|
|
7840
7843
|
result.otherServices.push(...extract(file.content, /\b(?:redis:\/\/[^\s'"`]+|amqp:\/\/[^\s'"`]+|kafka:\/\/[^\s'"`]+|elasticsearch:\/\/[^\s'"`]+)\b/gi, file.relPath));
|
|
7841
7844
|
}
|
|
7842
7845
|
const dedupDb = /* @__PURE__ */ new Map();
|
|
7843
|
-
for (const db of dbTechnologies)
|
|
7846
|
+
for (const db of dbTechnologies) {
|
|
7847
|
+
const key = `${db.kind}:${db.brand}`;
|
|
7848
|
+
if (!dedupDb.has(key)) dedupDb.set(key, db);
|
|
7849
|
+
}
|
|
7844
7850
|
result.databaseTechnologies = [...dedupDb.values()].sort((a, b) => a.brand.localeCompare(b.brand));
|
|
7845
7851
|
result.connectionStrings = uniq(result.connectionStrings);
|
|
7846
7852
|
result.connectionPoolSettings = uniq(result.connectionPoolSettings);
|
|
@@ -7870,6 +7876,17 @@ function detectOpenApiSpecification(relPath, content) {
|
|
|
7870
7876
|
const endpointCount = content.match(/(^|\n)\s*\/[^\n:]+:\s*(get|post|put|patch|delete|options|head)/gim)?.length ?? content.match(/"\/[^"]+"\s*:\s*\{/g)?.length ?? null;
|
|
7871
7877
|
return { path: relPath, format, version, title, endpointCount };
|
|
7872
7878
|
}
|
|
7879
|
+
function isInternalHost(hostname) {
|
|
7880
|
+
const h = hostname.toLowerCase();
|
|
7881
|
+
if (h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0") return true;
|
|
7882
|
+
if (/^192\.168\./.test(h) || /^10\./.test(h) || /^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
|
|
7883
|
+
if (!h.includes(".")) return true;
|
|
7884
|
+
if (/^(example|test|localhost|local|internal)\b/.test(h)) return true;
|
|
7885
|
+
return false;
|
|
7886
|
+
}
|
|
7887
|
+
function normalizeUrl(raw) {
|
|
7888
|
+
return raw.replace(/[\u0000-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2066-\u2069\uFEFF]/g, "").trim();
|
|
7889
|
+
}
|
|
7873
7890
|
async function scanApiSurface(rootDir, fileCache) {
|
|
7874
7891
|
const files = await scanTextFiles(rootDir, fileCache);
|
|
7875
7892
|
const integrations = [];
|
|
@@ -7889,18 +7906,29 @@ async function scanApiSurface(rootDir, fileCache) {
|
|
|
7889
7906
|
for (const file of files) {
|
|
7890
7907
|
const openApiSpec = detectOpenApiSpecification(file.relPath, file.content);
|
|
7891
7908
|
if (openApiSpec) result.openApiSpecifications.push(openApiSpec);
|
|
7892
|
-
const
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
const
|
|
7896
|
-
const
|
|
7909
|
+
const urlRegex = /\bhttps?:\/\/[^\s'"`]+/gi;
|
|
7910
|
+
let match;
|
|
7911
|
+
while ((match = urlRegex.exec(file.content)) !== null) {
|
|
7912
|
+
const rawUrl = match[0].replace(/[,;)\]}"'`]+$/, "");
|
|
7913
|
+
const cleanUrl = normalizeUrl(rawUrl);
|
|
7914
|
+
if (!cleanUrl.startsWith("http")) continue;
|
|
7915
|
+
let hostname;
|
|
7916
|
+
try {
|
|
7917
|
+
hostname = new URL(cleanUrl).hostname;
|
|
7918
|
+
} catch {
|
|
7919
|
+
continue;
|
|
7920
|
+
}
|
|
7921
|
+
if (isInternalHost(hostname)) continue;
|
|
7922
|
+
const versionMatch = cleanUrl.match(/\/(v\d+(?:\.\d+)?)\b/i);
|
|
7923
|
+
const params = cleanUrl.match(/[?&]([a-zA-Z0-9_.-]+)=/g)?.map((p) => p.replace(/[?&=]/g, "")) ?? [];
|
|
7897
7924
|
integrations.push({
|
|
7898
|
-
provider,
|
|
7899
|
-
endpoint,
|
|
7925
|
+
provider: hostname,
|
|
7926
|
+
endpoint: cleanUrl,
|
|
7900
7927
|
version: versionMatch ? versionMatch[1] : null,
|
|
7901
7928
|
parameters: params,
|
|
7902
7929
|
configOptions: extract(file.content, /\b(?:baseUrl|apiUrl|endpoint|timeout|retries|apiVersion)\b[^\n]*/gi, file.relPath),
|
|
7903
|
-
authHints: extract(file.content, /\b(?:api[_-]?key|bearer\s+[a-z0-9_.-]+|client[_-]?secret|oauth)\b[^\n]*/gi, file.relPath)
|
|
7930
|
+
authHints: extract(file.content, /\b(?:api[_-]?key|bearer\s+[a-z0-9_.-]+|client[_-]?secret|oauth)\b[^\n]*/gi, file.relPath),
|
|
7931
|
+
files: [file.relPath]
|
|
7904
7932
|
});
|
|
7905
7933
|
}
|
|
7906
7934
|
result.webhookUrls.push(...extract(file.content, /\bhttps?:\/\/[^\s'"`]*(?:webhook|hooks?)[^\s'"`]*/gi, file.relPath));
|
|
@@ -7910,18 +7938,27 @@ async function scanApiSurface(rootDir, fileCache) {
|
|
|
7910
7938
|
result.rateLimitOverrides.push(...extract(file.content, /\b(?:rate[_-]?limit|max[_-]?requests|throttle)\b[^\n]*/gi, file.relPath));
|
|
7911
7939
|
result.customHeaders.push(...extract(file.content, /\b(?:x-[a-z0-9-]+|authorization|user-agent)\b[^\n]*/gi, file.relPath));
|
|
7912
7940
|
result.corsPolicies.push(...extract(file.content, /\b(?:cors|access-control-allow-origin|allowedOrigins)\b[^\n]*/gi, file.relPath));
|
|
7913
|
-
result.oauthScopes.push(...extract(file.content, /\
|
|
7941
|
+
result.oauthScopes.push(...extract(file.content, /\bscopes?\s*[=:]\s*['"][^'"]*(?:openid|profile|email|offline_access|read(?::\w+)?|write(?::\w+)?)[^'"]*['"]/gi, file.relPath));
|
|
7914
7942
|
result.apiTokens.push(...extract(file.content, /\b(?:api[_-]?token|access[_-]?token|bearer[_-]?token)\b[^\n]*/gi, file.relPath));
|
|
7915
7943
|
}
|
|
7916
7944
|
const integrationMap = /* @__PURE__ */ new Map();
|
|
7917
7945
|
for (const integration of integrations) {
|
|
7918
|
-
const key =
|
|
7919
|
-
integrationMap.
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7946
|
+
const key = integration.endpoint;
|
|
7947
|
+
const existing = integrationMap.get(key);
|
|
7948
|
+
if (existing) {
|
|
7949
|
+
existing.files = uniq([...existing.files, ...integration.files]);
|
|
7950
|
+
existing.parameters = uniq([...existing.parameters, ...integration.parameters]);
|
|
7951
|
+
existing.configOptions = uniq([...existing.configOptions, ...integration.configOptions]);
|
|
7952
|
+
existing.authHints = uniq([...existing.authHints, ...integration.authHints]);
|
|
7953
|
+
} else {
|
|
7954
|
+
integrationMap.set(key, {
|
|
7955
|
+
...integration,
|
|
7956
|
+
parameters: uniq(integration.parameters),
|
|
7957
|
+
configOptions: uniq(integration.configOptions),
|
|
7958
|
+
authHints: uniq(integration.authHints),
|
|
7959
|
+
files: [...integration.files]
|
|
7960
|
+
});
|
|
7961
|
+
}
|
|
7925
7962
|
}
|
|
7926
7963
|
result.integrations = [...integrationMap.values()].sort((a, b) => a.provider.localeCompare(b.provider));
|
|
7927
7964
|
result.openApiSpecifications = [...new Map(result.openApiSpecifications.map((spec) => [spec.path, spec])).values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -7936,8 +7973,27 @@ async function scanApiSurface(rootDir, fileCache) {
|
|
|
7936
7973
|
result.apiTokens = uniq(result.apiTokens);
|
|
7937
7974
|
return result;
|
|
7938
7975
|
}
|
|
7976
|
+
var NON_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
7977
|
+
".md",
|
|
7978
|
+
".mdx",
|
|
7979
|
+
".markdown",
|
|
7980
|
+
".txt",
|
|
7981
|
+
".rst",
|
|
7982
|
+
".adoc",
|
|
7983
|
+
".asciidoc",
|
|
7984
|
+
".html",
|
|
7985
|
+
".htm",
|
|
7986
|
+
".pdf",
|
|
7987
|
+
".docx",
|
|
7988
|
+
".doc",
|
|
7989
|
+
".rtf"
|
|
7990
|
+
]);
|
|
7939
7991
|
async function scanOperationalResilience(rootDir, fileCache) {
|
|
7940
|
-
const
|
|
7992
|
+
const allFiles = await scanTextFiles(rootDir, fileCache);
|
|
7993
|
+
const files = allFiles.filter((f) => {
|
|
7994
|
+
const ext = path23.extname(f.relPath).toLowerCase();
|
|
7995
|
+
return !NON_CODE_EXTENSIONS.has(ext);
|
|
7996
|
+
});
|
|
7941
7997
|
const result = {
|
|
7942
7998
|
implicitTimeouts: [],
|
|
7943
7999
|
defaultPaginationSize: [],
|
|
@@ -8912,12 +8968,10 @@ async function runScan(rootDir, opts) {
|
|
|
8912
8968
|
steps: progress.getStepTimings()
|
|
8913
8969
|
});
|
|
8914
8970
|
if (!opts.noLocalArtifacts && !maxPrivacyMode) {
|
|
8971
|
+
const projectScores = {};
|
|
8915
8972
|
for (const project of allProjects) {
|
|
8916
8973
|
if (project.drift && project.path) {
|
|
8917
|
-
|
|
8918
|
-
const projectVibgrateDir = path24.join(projectDir, ".vibgrate");
|
|
8919
|
-
await ensureDir(projectVibgrateDir);
|
|
8920
|
-
await writeJsonFile(path24.join(projectVibgrateDir, "project_score.json"), {
|
|
8974
|
+
projectScores[project.path] = {
|
|
8921
8975
|
projectId: project.projectId,
|
|
8922
8976
|
name: project.name,
|
|
8923
8977
|
type: project.type,
|
|
@@ -8930,9 +8984,14 @@ async function runScan(rootDir, opts) {
|
|
|
8930
8984
|
vibgrateVersion: VERSION,
|
|
8931
8985
|
solutionId: project.solutionId,
|
|
8932
8986
|
solutionName: project.solutionName
|
|
8933
|
-
}
|
|
8987
|
+
};
|
|
8934
8988
|
}
|
|
8935
8989
|
}
|
|
8990
|
+
if (Object.keys(projectScores).length > 0) {
|
|
8991
|
+
const vibgrateDir = path24.join(rootDir, ".vibgrate");
|
|
8992
|
+
await ensureDir(vibgrateDir);
|
|
8993
|
+
await writeJsonFile(path24.join(vibgrateDir, "project_scores.json"), projectScores);
|
|
8994
|
+
}
|
|
8936
8995
|
}
|
|
8937
8996
|
if (opts.format === "json") {
|
|
8938
8997
|
const jsonStr = JSON.stringify(artifact, null, 2);
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
baselineCommand
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NASOHLRL.js";
|
|
5
5
|
import {
|
|
6
6
|
VERSION,
|
|
7
7
|
dsnCommand,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
pushCommand,
|
|
11
11
|
scanCommand,
|
|
12
12
|
writeDefaultConfig
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-56UAFVE4.js";
|
|
14
14
|
import {
|
|
15
15
|
ensureDir,
|
|
16
16
|
pathExists,
|
|
@@ -39,7 +39,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
39
39
|
console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
|
|
40
40
|
}
|
|
41
41
|
if (opts.baseline) {
|
|
42
|
-
const { runBaseline } = await import("./baseline-
|
|
42
|
+
const { runBaseline } = await import("./baseline-YGEO4GK7.js");
|
|
43
43
|
await runBaseline(rootDir);
|
|
44
44
|
}
|
|
45
45
|
console.log("");
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED