@vibgrate/cli 1.0.64 → 1.0.65

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 CHANGED
@@ -179,6 +179,7 @@ Vibgrate now supports explicit privacy controls:
179
179
 
180
180
  - `--no-local-artifacts` prevents writing `.vibgrate/*.json` files to disk.
181
181
  - `--max-privacy` enables a hardened profile (suppresses local artifact writes and disables high-context scanners such as UI-purpose evidence and architecture/code-quality enrichment).
182
+ - Additional commercial scanner families are available and independently toggleable in `vibgrate.config.*`: `runtimeConfiguration`, `dataStores`, `apiSurface`, `operationalResilience`, `assetBranding`, and `ossGovernance` (see `docs/commercial-grade-scanners.md`).
182
183
  - `--offline` disables live registry/network lookups and never uploads scan results.
183
184
  - `--package-manifest <file>` accepts a local JSON or ZIP package-version manifest so drift can still be calculated offline. Download the latest bundle at `https://github.com/vibgrate/manifests/latest-packages.zip`.
184
185
 
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  baselineCommand,
3
3
  runBaseline
4
- } from "./chunk-GCMUJKM7.js";
5
- import "./chunk-IQ4T2HLG.js";
4
+ } from "./chunk-VGZRUFB4.js";
5
+ import "./chunk-XDZGC6KJ.js";
6
6
  import "./chunk-TBE6NQ5Z.js";
7
7
  export {
8
8
  baselineCommand,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runScan
3
- } from "./chunk-IQ4T2HLG.js";
3
+ } from "./chunk-XDZGC6KJ.js";
4
4
  import {
5
5
  writeJsonFile
6
6
  } from "./chunk-TBE6NQ5Z.js";
@@ -1251,7 +1251,7 @@ var pushCommand = new Command2("push").description("Push scan results to Vibgrat
1251
1251
  });
1252
1252
 
1253
1253
  // src/commands/scan.ts
1254
- import * as path23 from "path";
1254
+ import * as path24 from "path";
1255
1255
  import { Command as Command3 } from "commander";
1256
1256
  import chalk6 from "chalk";
1257
1257
 
@@ -7446,6 +7446,355 @@ function isUsefulString(s) {
7446
7446
  return true;
7447
7447
  }
7448
7448
 
7449
+ // src/scanners/requirements-scanners.ts
7450
+ import * as path23 from "path";
7451
+ var MAX_SCAN_BYTES = 75e4;
7452
+ function uniq(values) {
7453
+ return [...new Set(values)].sort((a, b) => a.localeCompare(b));
7454
+ }
7455
+ function pushMatch(matches, value, sourceFile) {
7456
+ if (!value) return;
7457
+ const normalized = value.trim();
7458
+ if (!normalized) return;
7459
+ matches.push(`${normalized} (${sourceFile})`);
7460
+ }
7461
+ function extract(content, pattern, sourceFile) {
7462
+ const out = [];
7463
+ let match;
7464
+ while ((match = pattern.exec(content)) !== null) {
7465
+ const captured = match.slice(1).find((v) => typeof v === "string" && v.length > 0);
7466
+ pushMatch(out, captured ?? match[0], sourceFile);
7467
+ }
7468
+ return out;
7469
+ }
7470
+ function detectDbBrand(raw) {
7471
+ const value = raw.toLowerCase();
7472
+ if (value.includes("postgres")) return { kind: "sql", brand: "PostgreSQL", version: null, evidence: raw };
7473
+ if (value.includes("mysql") || value.includes("mariadb")) return { kind: "sql", brand: "MySQL/MariaDB", version: null, evidence: raw };
7474
+ if (value.includes("mssql") || value.includes("sqlserver")) return { kind: "sql", brand: "SQL Server", version: null, evidence: raw };
7475
+ if (value.includes("oracle")) return { kind: "sql", brand: "Oracle", version: null, evidence: raw };
7476
+ if (value.includes("sqlite")) return { kind: "sql", brand: "SQLite", version: null, evidence: raw };
7477
+ if (value.includes("mongodb")) return { kind: "nosql", brand: "MongoDB", version: null, evidence: raw };
7478
+ if (value.includes("redis")) return { kind: "nosql", brand: "Redis", version: null, evidence: raw };
7479
+ if (value.includes("cassandra")) return { kind: "nosql", brand: "Cassandra", version: null, evidence: raw };
7480
+ if (value.includes("dynamodb")) return { kind: "nosql", brand: "DynamoDB", version: null, evidence: raw };
7481
+ if (value.includes("neo4j")) return { kind: "nosql", brand: "Neo4j", version: null, evidence: raw };
7482
+ return null;
7483
+ }
7484
+ async function scanTextFiles(rootDir, fileCache) {
7485
+ const entries = await fileCache.walkDir(rootDir);
7486
+ const files = entries.filter((entry) => entry.isFile).map((entry) => entry.absPath);
7487
+ const out = [];
7488
+ for (const file of files) {
7489
+ try {
7490
+ const relPath = path23.relative(rootDir, file).replace(/\\/g, "/");
7491
+ const content = await fileCache.readTextFile(file);
7492
+ if (!content || content.length > MAX_SCAN_BYTES) continue;
7493
+ out.push({ relPath, content });
7494
+ } catch {
7495
+ }
7496
+ }
7497
+ return out;
7498
+ }
7499
+ async function scanRuntimeConfiguration(rootDir, fileCache) {
7500
+ const files = await scanTextFiles(rootDir, fileCache);
7501
+ const result = {
7502
+ environmentVariables: [],
7503
+ featureFlags: [],
7504
+ hiddenConfigFiles: [],
7505
+ dotEnvFiles: [],
7506
+ secretsInjectionPaths: [],
7507
+ containerEntrypoints: [],
7508
+ startupArguments: [],
7509
+ jvmFlags: [],
7510
+ threadPoolSettings: []
7511
+ };
7512
+ for (const file of files) {
7513
+ if (file.relPath.split("/").some((segment) => segment.startsWith("."))) {
7514
+ result.hiddenConfigFiles.push(file.relPath);
7515
+ }
7516
+ if (/\.env(\.|$)/i.test(file.relPath)) {
7517
+ result.dotEnvFiles.push(file.relPath);
7518
+ }
7519
+ result.environmentVariables.push(...extract(file.content, /(?:process\.env\.([A-Z0-9_]{3,})|process\.env\[['"]([A-Z0-9_]{3,})['"]\]|System\.getenv\(['"]([A-Z0-9_]{3,})['"]\)|os\.environ\[['"]([A-Z0-9_]{3,})['"]\]|getenv\(['"]([A-Z0-9_]{3,})['"]\))/g, file.relPath));
7520
+ result.featureFlags.push(...extract(file.content, /\b(?:feature[_-]?flag|ff_|is[A-Z][A-Za-z0-9]+Enabled|launchdarkly|unleash)\b/gi, file.relPath));
7521
+ result.secretsInjectionPaths.push(...extract(file.content, /\b(?:vault|secrets?\/(?:mount|inject|path)|aws\/secretsmanager|gcp\/secretmanager)\b/gi, file.relPath));
7522
+ result.containerEntrypoints.push(...extract(file.content, /\b(?:ENTRYPOINT|CMD)\s+([^\n]+)/g, file.relPath));
7523
+ result.startupArguments.push(...extract(file.content, /\b(?:--[a-z0-9-]+=?[\w:/.-]*)/gi, file.relPath));
7524
+ result.jvmFlags.push(...extract(file.content, /\b(-Xmx\S+|-Xms\S+|-XX:[^\s'"\]]+)/g, file.relPath));
7525
+ result.threadPoolSettings.push(...extract(file.content, /\b(?:thread[_-]?pool|max[_-]?threads|min[_-]?threads|worker[_-]?threads)\b[^\n]*/gi, file.relPath));
7526
+ }
7527
+ result.environmentVariables = uniq(result.environmentVariables);
7528
+ result.featureFlags = uniq(result.featureFlags);
7529
+ result.hiddenConfigFiles = uniq(result.hiddenConfigFiles);
7530
+ result.dotEnvFiles = uniq(result.dotEnvFiles);
7531
+ result.secretsInjectionPaths = uniq(result.secretsInjectionPaths);
7532
+ result.containerEntrypoints = uniq(result.containerEntrypoints);
7533
+ result.startupArguments = uniq(result.startupArguments).slice(0, 250);
7534
+ result.jvmFlags = uniq(result.jvmFlags);
7535
+ result.threadPoolSettings = uniq(result.threadPoolSettings);
7536
+ return result;
7537
+ }
7538
+ async function scanDataStores(rootDir, fileCache) {
7539
+ const files = await scanTextFiles(rootDir, fileCache);
7540
+ const dbTechnologies = [];
7541
+ const result = {
7542
+ databaseTechnologies: [],
7543
+ connectionStrings: [],
7544
+ connectionPoolSettings: [],
7545
+ replicationSettings: [],
7546
+ readReplicaSettings: [],
7547
+ failoverSettings: [],
7548
+ collationAndEncoding: [],
7549
+ queryTimeoutDefaults: [],
7550
+ manualIndexes: [],
7551
+ tables: [],
7552
+ views: [],
7553
+ storedProcedures: [],
7554
+ triggers: [],
7555
+ rowLevelSecurityPolicies: [],
7556
+ otherServices: []
7557
+ };
7558
+ for (const file of files) {
7559
+ const connStrings = extract(file.content, /\b(?:postgres(?:ql)?:\/\/[^\s'"`]+|mysql:\/\/[^\s'"`]+|mongodb(?:\+srv)?:\/\/[^\s'"`]+|redis:\/\/[^\s'"`]+)\b/gi, file.relPath);
7560
+ result.connectionStrings.push(...connStrings);
7561
+ const dbEvidence = [
7562
+ ...extract(file.content, /\b(?:postgres|postgresql|mysql|mariadb|mssql|sqlserver|oracle|sqlite|mongodb|redis|cassandra|dynamodb|neo4j)\b/gi, file.relPath),
7563
+ ...connStrings
7564
+ ];
7565
+ for (const evidence of dbEvidence) {
7566
+ const detected = detectDbBrand(evidence);
7567
+ if (detected) dbTechnologies.push(detected);
7568
+ }
7569
+ result.connectionPoolSettings.push(...extract(file.content, /\b(?:pool(?:Size|_size)?|maxPoolSize|minPoolSize|connectionLimit)\b[^\n]*/gi, file.relPath));
7570
+ result.replicationSettings.push(...extract(file.content, /\b(?:replication|cluster|replicaSet)\b[^\n]*/gi, file.relPath));
7571
+ result.readReplicaSettings.push(...extract(file.content, /\b(?:read[_-]?replica|reader[_-]?endpoint)\b[^\n]*/gi, file.relPath));
7572
+ result.failoverSettings.push(...extract(file.content, /\b(?:failover|multi[_-]?az|secondary[_-]?host)\b[^\n]*/gi, file.relPath));
7573
+ result.collationAndEncoding.push(...extract(file.content, /\b(?:collation|charset|encoding|utf-?8)\b[^\n]*/gi, file.relPath));
7574
+ result.queryTimeoutDefaults.push(...extract(file.content, /\b(?:statement_timeout|query[_-]?timeout|socketTimeout|commandTimeout)\b[^\n]*/gi, file.relPath));
7575
+ result.manualIndexes.push(...extract(file.content, /\bcreate\s+(?:unique\s+)?index\s+[^;\n]+/gi, file.relPath));
7576
+ result.tables.push(...extract(file.content, /\bcreate\s+table\s+([a-zA-Z0-9_."]+)/gi, file.relPath));
7577
+ result.views.push(...extract(file.content, /\bcreate\s+view\s+([a-zA-Z0-9_."]+)/gi, file.relPath));
7578
+ result.storedProcedures.push(...extract(file.content, /\bcreate\s+(?:or\s+replace\s+)?procedure\s+([a-zA-Z0-9_."]+)/gi, file.relPath));
7579
+ result.triggers.push(...extract(file.content, /\bcreate\s+trigger\s+([a-zA-Z0-9_."]+)/gi, file.relPath));
7580
+ result.rowLevelSecurityPolicies.push(...extract(file.content, /\b(?:row\s+level\s+security|create\s+policy|alter\s+table\s+.*\s+enable\s+row\s+level\s+security)\b[^;\n]*/gi, file.relPath));
7581
+ result.otherServices.push(...extract(file.content, /\b(?:redis:\/\/[^\s'"`]+|amqp:\/\/[^\s'"`]+|kafka:\/\/[^\s'"`]+|elasticsearch:\/\/[^\s'"`]+)\b/gi, file.relPath));
7582
+ }
7583
+ const dedupDb = /* @__PURE__ */ new Map();
7584
+ for (const db of dbTechnologies) dedupDb.set(`${db.kind}:${db.brand}:${db.evidence}`, db);
7585
+ result.databaseTechnologies = [...dedupDb.values()].sort((a, b) => a.brand.localeCompare(b.brand));
7586
+ result.connectionStrings = uniq(result.connectionStrings);
7587
+ result.connectionPoolSettings = uniq(result.connectionPoolSettings);
7588
+ result.replicationSettings = uniq(result.replicationSettings);
7589
+ result.readReplicaSettings = uniq(result.readReplicaSettings);
7590
+ result.failoverSettings = uniq(result.failoverSettings);
7591
+ result.collationAndEncoding = uniq(result.collationAndEncoding);
7592
+ result.queryTimeoutDefaults = uniq(result.queryTimeoutDefaults);
7593
+ result.manualIndexes = uniq(result.manualIndexes);
7594
+ result.tables = uniq(result.tables);
7595
+ result.views = uniq(result.views);
7596
+ result.storedProcedures = uniq(result.storedProcedures);
7597
+ result.triggers = uniq(result.triggers);
7598
+ result.rowLevelSecurityPolicies = uniq(result.rowLevelSecurityPolicies);
7599
+ result.otherServices = uniq(result.otherServices);
7600
+ return result;
7601
+ }
7602
+ function detectOpenApiSpecification(relPath, content) {
7603
+ const lowerPath = relPath.toLowerCase();
7604
+ const format = lowerPath.endsWith(".json") ? "json" : lowerPath.endsWith(".yaml") ? "yaml" : lowerPath.endsWith(".yml") ? "yml" : null;
7605
+ if (!format) return null;
7606
+ const hasOpenApi = /\bopenapi\s*[:=]\s*['"]?3\./i.test(content) || /\bswagger\s*[:=]\s*['"]?2\.0/i.test(content) || /"openapi"\s*:\s*"3\./i.test(content);
7607
+ const likelyName = /(openapi|swagger).*(\.ya?ml|\.json)$/i.test(lowerPath) || /\/api-docs\b/i.test(lowerPath);
7608
+ if (!hasOpenApi && !likelyName) return null;
7609
+ const version = content.match(/\bopenapi\s*[:=]\s*['"]?([^'"\s,]+)/i)?.[1] ?? content.match(/\bswagger\s*[:=]\s*['"]?([^'"\s,]+)/i)?.[1] ?? null;
7610
+ const title = content.match(/\btitle\s*[:=]\s*['"]([^'"]+)['"]/i)?.[1] ?? content.match(/\btitle\s*[:=]\s*([^\n#]+)/i)?.[1]?.trim() ?? content.match(/"title"\s*:\s*"([^"]+)"/i)?.[1] ?? null;
7611
+ const endpointCount = content.match(/(^|\n)\s*\/[^\n:]+:\s*(get|post|put|patch|delete|options|head)/gim)?.length ?? content.match(/"\/[^"]+"\s*:\s*\{/g)?.length ?? null;
7612
+ return { path: relPath, format, version, title, endpointCount };
7613
+ }
7614
+ async function scanApiSurface(rootDir, fileCache) {
7615
+ const files = await scanTextFiles(rootDir, fileCache);
7616
+ const integrations = [];
7617
+ const result = {
7618
+ integrations: [],
7619
+ openApiSpecifications: [],
7620
+ webhookUrls: [],
7621
+ callbackEndpoints: [],
7622
+ apiVersionPins: [],
7623
+ tokenExpirationPolicies: [],
7624
+ rateLimitOverrides: [],
7625
+ customHeaders: [],
7626
+ corsPolicies: [],
7627
+ oauthScopes: [],
7628
+ apiTokens: []
7629
+ };
7630
+ for (const file of files) {
7631
+ const openApiSpec = detectOpenApiSpecification(file.relPath, file.content);
7632
+ if (openApiSpec) result.openApiSpecifications.push(openApiSpec);
7633
+ const endpoints = extract(file.content, /\bhttps?:\/\/[^\s'"`]+/gi, file.relPath);
7634
+ for (const endpoint of endpoints) {
7635
+ const provider = endpoint.replace(/^https?:\/\//, "").split("/")[0] ?? "unknown";
7636
+ const versionMatch = endpoint.match(/\/(v\d+(?:\.\d+)?)\b/i);
7637
+ const params = endpoint.match(/[?&]([a-zA-Z0-9_.-]+)=/g)?.map((p) => p.replace(/[?&=]/g, "")) ?? [];
7638
+ integrations.push({
7639
+ provider,
7640
+ endpoint,
7641
+ version: versionMatch ? versionMatch[1] : null,
7642
+ parameters: params,
7643
+ configOptions: extract(file.content, /\b(?:baseUrl|apiUrl|endpoint|timeout|retries|apiVersion)\b[^\n]*/gi, file.relPath),
7644
+ authHints: extract(file.content, /\b(?:api[_-]?key|bearer\s+[a-z0-9_.-]+|client[_-]?secret|oauth)\b[^\n]*/gi, file.relPath)
7645
+ });
7646
+ }
7647
+ result.webhookUrls.push(...extract(file.content, /\bhttps?:\/\/[^\s'"`]*(?:webhook|hooks?)[^\s'"`]*/gi, file.relPath));
7648
+ result.callbackEndpoints.push(...extract(file.content, /\bhttps?:\/\/[^\s'"`]*(?:callback|redirect_uri)[^\s'"`]*/gi, file.relPath));
7649
+ result.apiVersionPins.push(...extract(file.content, /\bapi[_-]?version\b[^\n]*/gi, file.relPath));
7650
+ result.tokenExpirationPolicies.push(...extract(file.content, /\b(?:token[_-]?expiry|expires[_-]?in|refresh[_-]?token[_-]?ttl)\b[^\n]*/gi, file.relPath));
7651
+ result.rateLimitOverrides.push(...extract(file.content, /\b(?:rate[_-]?limit|max[_-]?requests|throttle)\b[^\n]*/gi, file.relPath));
7652
+ result.customHeaders.push(...extract(file.content, /\b(?:x-[a-z0-9-]+|authorization|user-agent)\b[^\n]*/gi, file.relPath));
7653
+ result.corsPolicies.push(...extract(file.content, /\b(?:cors|access-control-allow-origin|allowedOrigins)\b[^\n]*/gi, file.relPath));
7654
+ result.oauthScopes.push(...extract(file.content, /\b(?:scope|scopes)\b[^\n]*(?:openid|profile|email|read|write)/gi, file.relPath));
7655
+ result.apiTokens.push(...extract(file.content, /\b(?:api[_-]?token|access[_-]?token|bearer[_-]?token)\b[^\n]*/gi, file.relPath));
7656
+ }
7657
+ const integrationMap = /* @__PURE__ */ new Map();
7658
+ for (const integration of integrations) {
7659
+ const key = `${integration.provider}:${integration.endpoint}`;
7660
+ integrationMap.set(key, {
7661
+ ...integration,
7662
+ parameters: uniq(integration.parameters),
7663
+ configOptions: uniq(integration.configOptions),
7664
+ authHints: uniq(integration.authHints)
7665
+ });
7666
+ }
7667
+ result.integrations = [...integrationMap.values()].sort((a, b) => a.provider.localeCompare(b.provider));
7668
+ result.openApiSpecifications = [...new Map(result.openApiSpecifications.map((spec) => [spec.path, spec])).values()].sort((a, b) => a.path.localeCompare(b.path));
7669
+ result.webhookUrls = uniq(result.webhookUrls);
7670
+ result.callbackEndpoints = uniq(result.callbackEndpoints);
7671
+ result.apiVersionPins = uniq(result.apiVersionPins);
7672
+ result.tokenExpirationPolicies = uniq(result.tokenExpirationPolicies);
7673
+ result.rateLimitOverrides = uniq(result.rateLimitOverrides);
7674
+ result.customHeaders = uniq(result.customHeaders);
7675
+ result.corsPolicies = uniq(result.corsPolicies);
7676
+ result.oauthScopes = uniq(result.oauthScopes);
7677
+ result.apiTokens = uniq(result.apiTokens);
7678
+ return result;
7679
+ }
7680
+ async function scanOperationalResilience(rootDir, fileCache) {
7681
+ const files = await scanTextFiles(rootDir, fileCache);
7682
+ const result = {
7683
+ implicitTimeouts: [],
7684
+ defaultPaginationSize: [],
7685
+ implicitRetryLogic: [],
7686
+ defaultLocale: [],
7687
+ defaultCurrency: [],
7688
+ implicitTimezone: [],
7689
+ defaultCharacterEncoding: [],
7690
+ sessionStores: [],
7691
+ distributedLocks: [],
7692
+ jobSchedulers: [],
7693
+ idempotencyKeys: [],
7694
+ rateLimitingCounters: [],
7695
+ circuitBreakerState: [],
7696
+ abTestToggles: [],
7697
+ regionalEnablementRules: [],
7698
+ betaAccessGroups: [],
7699
+ licensingEnforcementLogic: [],
7700
+ killSwitches: [],
7701
+ connectorRetryLogic: [],
7702
+ apiPollingIntervals: [],
7703
+ fieldMappings: [],
7704
+ schemaRegistryRules: [],
7705
+ deadLetterQueueBehavior: [],
7706
+ dataMaskingRules: [],
7707
+ transformationLogic: [],
7708
+ timezoneHandling: [],
7709
+ encryptionSettings: [],
7710
+ hardcodedSecretSignals: []
7711
+ };
7712
+ for (const file of files) {
7713
+ result.implicitTimeouts.push(...extract(file.content, /\b(?:timeout|requestTimeout|connectTimeout|readTimeout)\b[^\n]*/gi, file.relPath));
7714
+ result.defaultPaginationSize.push(...extract(file.content, /\b(?:pageSize|limit|perPage|defaultPageSize)\b[^\n]*/gi, file.relPath));
7715
+ result.implicitRetryLogic.push(...extract(file.content, /\b(?:retry|backoff|maxRetries)\b[^\n]*/gi, file.relPath));
7716
+ result.defaultLocale.push(...extract(file.content, /\b(?:defaultLocale|locale\s*[:=]|en-US|en_GB)\b[^\n]*/gi, file.relPath));
7717
+ result.defaultCurrency.push(...extract(file.content, /\b(?:defaultCurrency|currency\s*[:=]|USD|EUR|GBP)\b[^\n]*/gi, file.relPath));
7718
+ result.implicitTimezone.push(...extract(file.content, /\b(?:timezone|timeZone|UTC|GMT)\b[^\n]*/gi, file.relPath));
7719
+ result.defaultCharacterEncoding.push(...extract(file.content, /\b(?:charset|encoding|UTF-?8|ISO-8859-1)\b[^\n]*/gi, file.relPath));
7720
+ result.sessionStores.push(...extract(file.content, /\b(?:sessionStore|redisStore|memoryStore)\b[^\n]*/gi, file.relPath));
7721
+ result.distributedLocks.push(...extract(file.content, /\b(?:distributed[_-]?lock|redlock|mutex)\b[^\n]*/gi, file.relPath));
7722
+ result.jobSchedulers.push(...extract(file.content, /\b(?:cron|schedule|bullmq|agenda|job[_-]?scheduler)\b[^\n]*/gi, file.relPath));
7723
+ result.idempotencyKeys.push(...extract(file.content, /\b(?:idempotency[_-]?key|Idempotency-Key)\b[^\n]*/gi, file.relPath));
7724
+ result.rateLimitingCounters.push(...extract(file.content, /\b(?:rate[_-]?limit|throttle|quota)\b[^\n]*/gi, file.relPath));
7725
+ result.circuitBreakerState.push(...extract(file.content, /\b(?:circuit[_-]?breaker|half[_-]?open|open[_-]?state)\b[^\n]*/gi, file.relPath));
7726
+ result.abTestToggles.push(...extract(file.content, /\b(?:a\/b|ab[_-]?test|experiment[_-]?flag)\b[^\n]*/gi, file.relPath));
7727
+ result.regionalEnablementRules.push(...extract(file.content, /\b(?:region[_-]?enabled|geo[_-]?fence|country[_-]?allow)\b[^\n]*/gi, file.relPath));
7728
+ result.betaAccessGroups.push(...extract(file.content, /\b(?:beta[_-]?users?|early[_-]?access|allowlist)\b[^\n]*/gi, file.relPath));
7729
+ result.licensingEnforcementLogic.push(...extract(file.content, /\b(?:license[_-]?key|entitlement|plan[_-]?check)\b[^\n]*/gi, file.relPath));
7730
+ result.killSwitches.push(...extract(file.content, /\b(?:kill[_-]?switch|disable[_-]?all|emergency[_-]?stop)\b[^\n]*/gi, file.relPath));
7731
+ result.connectorRetryLogic.push(...extract(file.content, /\b(?:connector|integration)\b[^\n]*(?:retry|backoff)/gi, file.relPath));
7732
+ result.apiPollingIntervals.push(...extract(file.content, /\b(?:poll(?:ing)?[_-]?interval|sync[_-]?interval)\b[^\n]*/gi, file.relPath));
7733
+ result.fieldMappings.push(...extract(file.content, /\b(?:field[_-]?mapping|mapFields?|column[_-]?map)\b[^\n]*/gi, file.relPath));
7734
+ result.schemaRegistryRules.push(...extract(file.content, /\b(?:schema[_-]?registry|avro|protobuf)\b[^\n]*/gi, file.relPath));
7735
+ result.deadLetterQueueBehavior.push(...extract(file.content, /\b(?:dead[_-]?letter|dlq)\b[^\n]*/gi, file.relPath));
7736
+ result.dataMaskingRules.push(...extract(file.content, /\b(?:data[_-]?mask|redact|pii[_-]?mask)\b[^\n]*/gi, file.relPath));
7737
+ result.transformationLogic.push(...extract(file.content, /\b(?:transform|mapper|normaliz(?:e|ation))\b[^\n]*/gi, file.relPath));
7738
+ result.timezoneHandling.push(...extract(file.content, /\b(?:convertTimezone|tz\(|moment\.tz|DateTimeZone)\b[^\n]*/gi, file.relPath));
7739
+ result.encryptionSettings.push(...extract(file.content, /\b(?:kms|encrypt(?:ion)?|cipher|tls|minTlsVersion)\b[^\n]*/gi, file.relPath));
7740
+ result.hardcodedSecretSignals.push(...extract(file.content, /\b(?:password|passwd|connectionString|api[_-]?key|secret)\b\s*[:=]\s*['"][^'"]{4,}['"]/gi, file.relPath));
7741
+ }
7742
+ Object.keys(result).forEach((key) => {
7743
+ result[key] = uniq(result[key]);
7744
+ });
7745
+ return result;
7746
+ }
7747
+ async function scanAssetBranding(rootDir, fileCache) {
7748
+ const entries = await fileCache.walkDir(rootDir);
7749
+ const faviconCandidates = entries.filter((entry) => entry.isFile && /favicon/i.test(entry.name));
7750
+ const logoCandidates = entries.filter((entry) => entry.isFile && /logo/i.test(entry.name));
7751
+ const faviconFiles = [];
7752
+ for (const entry of faviconCandidates.slice(0, 10)) {
7753
+ try {
7754
+ const content = await fileCache.readTextFile(entry.absPath);
7755
+ if (!content) continue;
7756
+ const encoded = Buffer.from(content).toString("base64");
7757
+ faviconFiles.push({
7758
+ path: entry.relPath,
7759
+ base64: encoded.slice(0, 1e4)
7760
+ });
7761
+ } catch {
7762
+ }
7763
+ }
7764
+ return {
7765
+ faviconFiles,
7766
+ productLogos: uniq(logoCandidates.map((entry) => entry.relPath))
7767
+ };
7768
+ }
7769
+ async function scanOssGovernance(rootDir, fileCache) {
7770
+ const files = await scanTextFiles(rootDir, fileCache);
7771
+ const directDeps = /* @__PURE__ */ new Set();
7772
+ const transitiveDeps = /* @__PURE__ */ new Set();
7773
+ const vulns = [];
7774
+ const licenseRisks = [];
7775
+ for (const file of files) {
7776
+ if (/package(-lock)?\.json$|pnpm-lock\.yaml$|poetry\.lock$|pom\.xml$/i.test(file.relPath)) {
7777
+ const depMatches = file.content.match(/"([@a-zA-Z0-9_.\/-]+)"\s*:/g) ?? [];
7778
+ depMatches.forEach((match) => transitiveDeps.add(match.replace(/["\s:]/g, "")));
7779
+ }
7780
+ if (/package\.json$/i.test(file.relPath)) {
7781
+ const depMatches = file.content.match(/"([@a-zA-Z0-9_.\/-]+)"\s*:\s*"[~^0-9*><=.-]+"/g) ?? [];
7782
+ depMatches.forEach((match) => {
7783
+ const pkg2 = match.split(":")[0]?.replace(/"/g, "").trim();
7784
+ if (pkg2) directDeps.add(pkg2);
7785
+ });
7786
+ }
7787
+ vulns.push(...extract(file.content, /\b(?:CVE-\d{4}-\d{4,}|critical vulnerability|high severity)\b[^\n]*/gi, file.relPath));
7788
+ licenseRisks.push(...extract(file.content, /\b(?:GPL-3\.0|AGPL|SSPL|BUSL|source[- ]available)\b[^\n]*/gi, file.relPath));
7789
+ }
7790
+ return {
7791
+ directDependencies: directDeps.size,
7792
+ transitiveDependencies: Math.max(transitiveDeps.size, directDeps.size),
7793
+ knownVulnerabilities: uniq(vulns),
7794
+ licenseRisks: uniq(licenseRisks)
7795
+ };
7796
+ }
7797
+
7449
7798
  // src/utils/compact-evidence.ts
7450
7799
  var CATEGORY_PATTERNS = [
7451
7800
  { category: "pricing", pattern: /price|pricing|billing|subscri|trial|credit|plan|tier|upgrade|premium|pro|enterprise/i },
@@ -7639,21 +7988,27 @@ function escapeLabel(input) {
7639
7988
  }
7640
7989
  function scoreClass(score) {
7641
7990
  if (score === void 0 || Number.isNaN(score)) return "scoreUnknown";
7642
- if (score >= 70) return "scoreHigh";
7643
- if (score >= 40) return "scoreModerate";
7991
+ if (score >= 80) return "scoreHigh";
7992
+ if (score >= 50) return "scoreModerate";
7644
7993
  return "scoreLow";
7645
7994
  }
7646
7995
  function nodeLabel(project) {
7647
7996
  const score = project.drift?.score;
7648
- const scoreText = typeof score === "number" ? ` (${score})` : " (n/a)";
7649
- return `${project.name}${scoreText}`;
7997
+ if (typeof score === "number") {
7998
+ return `${project.name} (${score})`;
7999
+ }
8000
+ return project.name;
7650
8001
  }
7651
8002
  function buildDefs() {
7652
8003
  return [
7653
- "classDef scoreHigh fill:#064e3b,stroke:#10b981,color:#d1fae5,stroke-width:2px",
7654
- "classDef scoreModerate fill:#78350f,stroke:#f59e0b,color:#fef3c7,stroke-width:2px",
7655
- "classDef scoreLow fill:#7f1d1d,stroke:#ef4444,color:#fee2e2,stroke-width:2px",
7656
- "classDef scoreUnknown fill:#334155,stroke:#94a3b8,color:#e2e8f0,stroke-width:2px"
8004
+ // Emerald border for high scores (>= 80) - border-emerald-500
8005
+ "classDef scoreHigh fill:#1e293b,stroke:#10b981,color:#f1f5f9,stroke-width:2px",
8006
+ // Amber border for moderate scores (50-79) - border-amber-500
8007
+ "classDef scoreModerate fill:#1e293b,stroke:#f59e0b,color:#f1f5f9,stroke-width:2px",
8008
+ // Red border for low scores (< 50) - border-red-500
8009
+ "classDef scoreLow fill:#1e293b,stroke:#ef4444,color:#f1f5f9,stroke-width:2px",
8010
+ // Slate border for unknown scores
8011
+ "classDef scoreUnknown fill:#1e293b,stroke:#64748b,color:#94a3b8,stroke-width:2px"
7657
8012
  ];
7658
8013
  }
7659
8014
  function generateWorkspaceRelationshipMermaid(projects) {
@@ -7739,17 +8094,17 @@ async function discoverSolutions(rootDir, fileCache) {
7739
8094
  for (const solutionFile of solutionFiles) {
7740
8095
  try {
7741
8096
  const content = await fileCache.readTextFile(solutionFile);
7742
- const dir = path23.dirname(solutionFile);
7743
- const relSolutionPath = path23.relative(rootDir, solutionFile).replace(/\\/g, "/");
8097
+ const dir = path24.dirname(solutionFile);
8098
+ const relSolutionPath = path24.relative(rootDir, solutionFile).replace(/\\/g, "/");
7744
8099
  const projectPaths = /* @__PURE__ */ new Set();
7745
8100
  const projectRegex = /Project\("[^"]*"\)\s*=\s*"([^"]*)",\s*"([^"]+\.csproj)"/g;
7746
8101
  let match;
7747
8102
  while ((match = projectRegex.exec(content)) !== null) {
7748
8103
  const projectRelative = match[2];
7749
- const absProjectPath = path23.resolve(dir, projectRelative.replace(/\\/g, "/"));
7750
- projectPaths.add(path23.relative(rootDir, absProjectPath).replace(/\\/g, "/"));
8104
+ const absProjectPath = path24.resolve(dir, projectRelative.replace(/\\/g, "/"));
8105
+ projectPaths.add(path24.relative(rootDir, absProjectPath).replace(/\\/g, "/"));
7751
8106
  }
7752
- const solutionName = path23.basename(solutionFile, path23.extname(solutionFile));
8107
+ const solutionName = path24.basename(solutionFile, path24.extname(solutionFile));
7753
8108
  parsed.push({
7754
8109
  path: relSolutionPath,
7755
8110
  name: solutionName,
@@ -7792,7 +8147,13 @@ async function runScan(rootDir, opts) {
7792
8147
  architecture: !maxPrivacyMode,
7793
8148
  codeQuality: !maxPrivacyMode,
7794
8149
  owaspCategoryMapping: !maxPrivacyMode,
7795
- uiPurpose: !maxPrivacyMode
8150
+ uiPurpose: !maxPrivacyMode,
8151
+ runtimeConfiguration: !maxPrivacyMode,
8152
+ dataStores: true,
8153
+ apiSurface: !maxPrivacyMode,
8154
+ operationalResilience: !maxPrivacyMode,
8155
+ assetBranding: true,
8156
+ ossGovernance: true
7796
8157
  };
7797
8158
  let filesScanned = 0;
7798
8159
  const progress = new ScanProgress(rootDir);
@@ -7821,7 +8182,13 @@ async function runScan(rootDir, opts) {
7821
8182
  ...scannerPolicy.architecture && scanners?.architecture?.enabled !== false ? [{ id: "architecture", label: "Architecture layers" }] : [],
7822
8183
  ...scannerPolicy.codeQuality && scanners?.codeQuality?.enabled !== false ? [{ id: "codequality", label: "Code quality metrics" }] : [],
7823
8184
  ...scannerPolicy.owaspCategoryMapping && scanners?.owaspCategoryMapping?.enabled !== false ? [{ id: "owasp", label: "OWASP category mapping" }] : [],
7824
- ...!maxPrivacyMode && (opts.uiPurpose || scanners?.uiPurpose?.enabled === true) ? [{ id: "uipurpose", label: "UI purpose evidence" }] : []
8185
+ ...!maxPrivacyMode && (opts.uiPurpose || scanners?.uiPurpose?.enabled === true) ? [{ id: "uipurpose", label: "UI purpose evidence" }] : [],
8186
+ ...scannerPolicy.runtimeConfiguration && scanners?.runtimeConfiguration?.enabled !== false ? [{ id: "runtimecfg", label: "Runtime configuration" }] : [],
8187
+ ...scannerPolicy.dataStores && scanners?.dataStores?.enabled !== false ? [{ id: "datastores", label: "Data stores & schema" }] : [],
8188
+ ...scannerPolicy.apiSurface && scanners?.apiSurface?.enabled !== false ? [{ id: "apisurface", label: "API integrations" }] : [],
8189
+ ...scannerPolicy.operationalResilience && scanners?.operationalResilience?.enabled !== false ? [{ id: "resilience", label: "Operational resilience" }] : [],
8190
+ ...scannerPolicy.assetBranding && scanners?.assetBranding?.enabled !== false ? [{ id: "branding", label: "Branding assets" }] : [],
8191
+ ...scannerPolicy.ossGovernance && scanners?.ossGovernance?.enabled !== false ? [{ id: "ossgov", label: "OSS governance" }] : []
7825
8192
  ] : [],
7826
8193
  { id: "drift", label: "Computing drift score" },
7827
8194
  { id: "findings", label: "Generating findings" }
@@ -7947,7 +8314,7 @@ async function runScan(rootDir, opts) {
7947
8314
  project.drift = computeDriftScore([project]);
7948
8315
  project.projectId = computeProjectId(project.path, project.name, workspaceId);
7949
8316
  }
7950
- const solutionsManifestPath = path23.join(rootDir, ".vibgrate", "solutions.json");
8317
+ const solutionsManifestPath = path24.join(rootDir, ".vibgrate", "solutions.json");
7951
8318
  const persistedSolutionIds = /* @__PURE__ */ new Map();
7952
8319
  if (await pathExists(solutionsManifestPath)) {
7953
8320
  try {
@@ -8124,6 +8491,66 @@ async function runScan(rootDir, opts) {
8124
8491
  })
8125
8492
  );
8126
8493
  }
8494
+ if (scannerPolicy.runtimeConfiguration && scanners?.runtimeConfiguration?.enabled !== false) {
8495
+ progress.startStep("runtimecfg");
8496
+ scannerTasks.push(
8497
+ scanRuntimeConfiguration(rootDir, fileCache).then((result) => {
8498
+ extended.runtimeConfiguration = result;
8499
+ const count = result.environmentVariables.length + result.featureFlags.length + result.dotEnvFiles.length;
8500
+ progress.completeStep("runtimecfg", `${count} runtime signals`, count);
8501
+ })
8502
+ );
8503
+ }
8504
+ if (scannerPolicy.dataStores && scanners?.dataStores?.enabled !== false) {
8505
+ progress.startStep("datastores");
8506
+ scannerTasks.push(
8507
+ scanDataStores(rootDir, fileCache).then((result) => {
8508
+ extended.dataStores = result;
8509
+ const count = result.databaseTechnologies.length + result.tables.length + result.views.length;
8510
+ progress.completeStep("datastores", `${result.databaseTechnologies.length} engines \xB7 ${result.tables.length} tables`, count);
8511
+ })
8512
+ );
8513
+ }
8514
+ if (scannerPolicy.apiSurface && scanners?.apiSurface?.enabled !== false) {
8515
+ progress.startStep("apisurface");
8516
+ scannerTasks.push(
8517
+ scanApiSurface(rootDir, fileCache).then((result) => {
8518
+ extended.apiSurface = result;
8519
+ const count = result.integrations.length + result.webhookUrls.length;
8520
+ progress.completeStep("apisurface", `${result.integrations.length} integrations`, count);
8521
+ })
8522
+ );
8523
+ }
8524
+ if (scannerPolicy.operationalResilience && scanners?.operationalResilience?.enabled !== false) {
8525
+ progress.startStep("resilience");
8526
+ scannerTasks.push(
8527
+ scanOperationalResilience(rootDir, fileCache).then((result) => {
8528
+ extended.operationalResilience = result;
8529
+ const count = result.implicitTimeouts.length + result.implicitRetryLogic.length + result.killSwitches.length;
8530
+ progress.completeStep("resilience", `${count} resilience signals`, count);
8531
+ })
8532
+ );
8533
+ }
8534
+ if (scannerPolicy.assetBranding && scanners?.assetBranding?.enabled !== false) {
8535
+ progress.startStep("branding");
8536
+ scannerTasks.push(
8537
+ scanAssetBranding(rootDir, fileCache).then((result) => {
8538
+ extended.assetBranding = result;
8539
+ const count = result.faviconFiles.length + result.productLogos.length;
8540
+ progress.completeStep("branding", `${result.faviconFiles.length} favicons \xB7 ${result.productLogos.length} logos`, count);
8541
+ })
8542
+ );
8543
+ }
8544
+ if (scannerPolicy.ossGovernance && scanners?.ossGovernance?.enabled !== false) {
8545
+ progress.startStep("ossgov");
8546
+ scannerTasks.push(
8547
+ scanOssGovernance(rootDir, fileCache).then((result) => {
8548
+ extended.ossGovernance = result;
8549
+ const count = result.directDependencies + result.knownVulnerabilities.length + result.licenseRisks.length;
8550
+ progress.completeStep("ossgov", `${result.directDependencies} direct deps`, count);
8551
+ })
8552
+ );
8553
+ }
8127
8554
  if (scannerPolicy.dependencyRisk && scanners?.dependencyRisk?.enabled !== false) {
8128
8555
  progress.startStep("deprisk");
8129
8556
  scannerTasks.push(
@@ -8164,7 +8591,7 @@ async function runScan(rootDir, opts) {
8164
8591
  const summary = [`${up.topEvidence.length} evidence`, ...up.capped ? ["capped"] : []].join(" \xB7 ");
8165
8592
  progress.completeStep("uipurpose", summary, up.topEvidence.length);
8166
8593
  await Promise.all(allProjects.map(async (project) => {
8167
- const projectDir = path23.join(rootDir, project.path);
8594
+ const projectDir = path24.join(rootDir, project.path);
8168
8595
  const projectResult = await scanUiPurpose(projectDir, fileCache, 150);
8169
8596
  if (projectResult.topEvidence.length > 0) {
8170
8597
  project.uiPurpose = compactUiPurpose(projectResult);
@@ -8258,13 +8685,19 @@ async function runScan(rootDir, opts) {
8258
8685
  if (extended.codeQuality) filesScanned += extended.codeQuality.filesAnalyzed;
8259
8686
  if (extended.owaspCategoryMapping) filesScanned += extended.owaspCategoryMapping.scannedFiles;
8260
8687
  if (extended.uiPurpose) filesScanned += extended.uiPurpose.topEvidence.length;
8688
+ if (extended.runtimeConfiguration) filesScanned += extended.runtimeConfiguration.hiddenConfigFiles.length;
8689
+ if (extended.dataStores) filesScanned += extended.dataStores.databaseTechnologies.length + extended.dataStores.tables.length;
8690
+ if (extended.apiSurface) filesScanned += extended.apiSurface.integrations.length;
8691
+ if (extended.operationalResilience) filesScanned += extended.operationalResilience.implicitTimeouts.length;
8692
+ if (extended.assetBranding) filesScanned += extended.assetBranding.faviconFiles.length + extended.assetBranding.productLogos.length;
8693
+ if (extended.ossGovernance) filesScanned += extended.ossGovernance.directDependencies;
8261
8694
  const durationMs = Date.now() - scanStart;
8262
8695
  const repository = await buildRepositoryInfo(rootDir, vcs.remoteUrl, extended.buildDeploy?.ci);
8263
8696
  const artifact = {
8264
8697
  schemaVersion: "1.0",
8265
8698
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8266
8699
  vibgrateVersion: VERSION,
8267
- rootPath: path23.basename(rootDir),
8700
+ rootPath: path24.basename(rootDir),
8268
8701
  ...vcs.type !== "unknown" ? { vcs } : {},
8269
8702
  repository,
8270
8703
  projects: allProjects,
@@ -8278,7 +8711,7 @@ async function runScan(rootDir, opts) {
8278
8711
  relationshipDiagram
8279
8712
  };
8280
8713
  if (opts.baseline) {
8281
- const baselinePath = path23.resolve(opts.baseline);
8714
+ const baselinePath = path24.resolve(opts.baseline);
8282
8715
  if (await pathExists(baselinePath)) {
8283
8716
  try {
8284
8717
  const baseline = await readJsonFile(baselinePath);
@@ -8290,10 +8723,10 @@ async function runScan(rootDir, opts) {
8290
8723
  }
8291
8724
  }
8292
8725
  if (!opts.noLocalArtifacts && !maxPrivacyMode) {
8293
- const vibgrateDir = path23.join(rootDir, ".vibgrate");
8726
+ const vibgrateDir = path24.join(rootDir, ".vibgrate");
8294
8727
  await ensureDir(vibgrateDir);
8295
- await writeJsonFile(path23.join(vibgrateDir, "scan_result.json"), artifact);
8296
- await writeJsonFile(path23.join(vibgrateDir, "solutions.json"), {
8728
+ await writeJsonFile(path24.join(vibgrateDir, "scan_result.json"), artifact);
8729
+ await writeJsonFile(path24.join(vibgrateDir, "solutions.json"), {
8297
8730
  scannedAt: artifact.timestamp,
8298
8731
  solutions: solutions.map((solution) => ({
8299
8732
  solutionId: solution.solutionId,
@@ -8314,10 +8747,10 @@ async function runScan(rootDir, opts) {
8314
8747
  if (!opts.noLocalArtifacts && !maxPrivacyMode) {
8315
8748
  for (const project of allProjects) {
8316
8749
  if (project.drift && project.path) {
8317
- const projectDir = path23.resolve(rootDir, project.path);
8318
- const projectVibgrateDir = path23.join(projectDir, ".vibgrate");
8750
+ const projectDir = path24.resolve(rootDir, project.path);
8751
+ const projectVibgrateDir = path24.join(projectDir, ".vibgrate");
8319
8752
  await ensureDir(projectVibgrateDir);
8320
- await writeJsonFile(path23.join(projectVibgrateDir, "project_score.json"), {
8753
+ await writeJsonFile(path24.join(projectVibgrateDir, "project_score.json"), {
8321
8754
  projectId: project.projectId,
8322
8755
  name: project.name,
8323
8756
  type: project.type,
@@ -8337,7 +8770,7 @@ async function runScan(rootDir, opts) {
8337
8770
  if (opts.format === "json") {
8338
8771
  const jsonStr = JSON.stringify(artifact, null, 2);
8339
8772
  if (opts.out) {
8340
- await writeTextFile(path23.resolve(opts.out), jsonStr);
8773
+ await writeTextFile(path24.resolve(opts.out), jsonStr);
8341
8774
  console.log(chalk6.green("\u2714") + ` JSON written to ${opts.out}`);
8342
8775
  } else {
8343
8776
  console.log(jsonStr);
@@ -8346,7 +8779,7 @@ async function runScan(rootDir, opts) {
8346
8779
  const sarif = formatSarif(artifact);
8347
8780
  const sarifStr = JSON.stringify(sarif, null, 2);
8348
8781
  if (opts.out) {
8349
- await writeTextFile(path23.resolve(opts.out), sarifStr);
8782
+ await writeTextFile(path24.resolve(opts.out), sarifStr);
8350
8783
  console.log(chalk6.green("\u2714") + ` SARIF written to ${opts.out}`);
8351
8784
  } else {
8352
8785
  console.log(sarifStr);
@@ -8355,20 +8788,20 @@ async function runScan(rootDir, opts) {
8355
8788
  const markdown = formatMarkdown(artifact);
8356
8789
  console.log(markdown);
8357
8790
  if (opts.out) {
8358
- await writeTextFile(path23.resolve(opts.out), markdown);
8791
+ await writeTextFile(path24.resolve(opts.out), markdown);
8359
8792
  }
8360
8793
  } else {
8361
8794
  const text = formatText(artifact);
8362
8795
  console.log(text);
8363
8796
  if (opts.out) {
8364
- await writeTextFile(path23.resolve(opts.out), text);
8797
+ await writeTextFile(path24.resolve(opts.out), text);
8365
8798
  }
8366
8799
  }
8367
8800
  return artifact;
8368
8801
  }
8369
8802
  async function buildRepositoryInfo(rootDir, remoteUrl, ciSystems) {
8370
- const packageJsonPath = path23.join(rootDir, "package.json");
8371
- let name = path23.basename(rootDir);
8803
+ const packageJsonPath = path24.join(rootDir, "package.json");
8804
+ let name = path24.basename(rootDir);
8372
8805
  let version;
8373
8806
  if (await pathExists(packageJsonPath)) {
8374
8807
  try {
@@ -8463,7 +8896,7 @@ function parseNonNegativeNumber(value, label) {
8463
8896
  return parsed;
8464
8897
  }
8465
8898
  var scanCommand = new Command3("scan").description("Scan a project for upgrade drift").argument("[path]", "Path to scan", ".").option("--out <file>", "Output file path").option("--format <format>", "Output format (text|json|sarif|md)", "text").option("--fail-on <level>", "Fail on warn or error").option("--baseline <file>", "Compare against baseline").option("--changed-only", "Only scan changed files").option("--concurrency <n>", "Max concurrent npm calls", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--install-tools", "Auto-install missing security scanners via Homebrew").option("--ui-purpose", "Enable optional UI purpose evidence extraction (slower)").option("--no-local-artifacts", "Do not write .vibgrate JSON artifacts to disk").option("--max-privacy", "Enable strongest privacy mode (minimal scanners, no local artifacts)").option("--offline", "Run without network calls; do not upload results").option("--package-manifest <file>", "Use local package-version manifest JSON/ZIP (for offline mode)").option("--drift-budget <score>", "Fail if drift score is above budget (0-100)").option("--drift-worsening <percent>", "Fail if drift worsens by more than % since baseline").action(async (targetPath, opts) => {
8466
- const rootDir = path23.resolve(targetPath);
8899
+ const rootDir = path24.resolve(targetPath);
8467
8900
  if (!await pathExists(rootDir)) {
8468
8901
  console.error(chalk6.red(`Path does not exist: ${rootDir}`));
8469
8902
  process.exit(1);
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  baselineCommand
4
- } from "./chunk-GCMUJKM7.js";
4
+ } from "./chunk-VGZRUFB4.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-IQ4T2HLG.js";
13
+ } from "./chunk-XDZGC6KJ.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-HGBNL6CN.js");
42
+ const { runBaseline } = await import("./baseline-WRLXEJGA.js");
43
43
  await runBaseline(rootDir);
44
44
  }
45
45
  console.log("");
package/dist/index.d.ts CHANGED
@@ -196,6 +196,12 @@ interface ScannersConfig {
196
196
  codeQuality?: ScannerToggle;
197
197
  owaspCategoryMapping?: OwaspScannerConfig;
198
198
  uiPurpose?: ScannerToggle;
199
+ runtimeConfiguration?: ScannerToggle;
200
+ dataStores?: ScannerToggle;
201
+ apiSurface?: ScannerToggle;
202
+ operationalResilience?: ScannerToggle;
203
+ assetBranding?: ScannerToggle;
204
+ ossGovernance?: ScannerToggle;
199
205
  }
200
206
  interface VibgrateConfig {
201
207
  include?: string[];
@@ -487,6 +493,111 @@ interface UiPurposeResult {
487
493
  topEvidence: UiPurposeEvidenceItem[];
488
494
  unknownSignals: string[];
489
495
  }
496
+ interface RuntimeConfigurationResult {
497
+ environmentVariables: string[];
498
+ featureFlags: string[];
499
+ hiddenConfigFiles: string[];
500
+ dotEnvFiles: string[];
501
+ secretsInjectionPaths: string[];
502
+ containerEntrypoints: string[];
503
+ startupArguments: string[];
504
+ jvmFlags: string[];
505
+ threadPoolSettings: string[];
506
+ }
507
+ interface DatabaseTechnology {
508
+ kind: 'sql' | 'nosql';
509
+ brand: string;
510
+ version: string | null;
511
+ evidence: string;
512
+ }
513
+ interface DataStoresResult {
514
+ databaseTechnologies: DatabaseTechnology[];
515
+ connectionStrings: string[];
516
+ connectionPoolSettings: string[];
517
+ replicationSettings: string[];
518
+ readReplicaSettings: string[];
519
+ failoverSettings: string[];
520
+ collationAndEncoding: string[];
521
+ queryTimeoutDefaults: string[];
522
+ manualIndexes: string[];
523
+ tables: string[];
524
+ views: string[];
525
+ storedProcedures: string[];
526
+ triggers: string[];
527
+ rowLevelSecurityPolicies: string[];
528
+ otherServices: string[];
529
+ }
530
+ interface OpenApiSpecification {
531
+ path: string;
532
+ format: 'json' | 'yaml' | 'yml';
533
+ version: string | null;
534
+ title: string | null;
535
+ endpointCount: number | null;
536
+ }
537
+ interface ApiIntegration {
538
+ provider: string;
539
+ endpoint: string;
540
+ version: string | null;
541
+ parameters: string[];
542
+ configOptions: string[];
543
+ authHints: string[];
544
+ }
545
+ interface ApiSurfaceResult {
546
+ integrations: ApiIntegration[];
547
+ openApiSpecifications: OpenApiSpecification[];
548
+ webhookUrls: string[];
549
+ callbackEndpoints: string[];
550
+ apiVersionPins: string[];
551
+ tokenExpirationPolicies: string[];
552
+ rateLimitOverrides: string[];
553
+ customHeaders: string[];
554
+ corsPolicies: string[];
555
+ oauthScopes: string[];
556
+ apiTokens: string[];
557
+ }
558
+ interface OperationalResilienceResult {
559
+ implicitTimeouts: string[];
560
+ defaultPaginationSize: string[];
561
+ implicitRetryLogic: string[];
562
+ defaultLocale: string[];
563
+ defaultCurrency: string[];
564
+ implicitTimezone: string[];
565
+ defaultCharacterEncoding: string[];
566
+ sessionStores: string[];
567
+ distributedLocks: string[];
568
+ jobSchedulers: string[];
569
+ idempotencyKeys: string[];
570
+ rateLimitingCounters: string[];
571
+ circuitBreakerState: string[];
572
+ abTestToggles: string[];
573
+ regionalEnablementRules: string[];
574
+ betaAccessGroups: string[];
575
+ licensingEnforcementLogic: string[];
576
+ killSwitches: string[];
577
+ connectorRetryLogic: string[];
578
+ apiPollingIntervals: string[];
579
+ fieldMappings: string[];
580
+ schemaRegistryRules: string[];
581
+ deadLetterQueueBehavior: string[];
582
+ dataMaskingRules: string[];
583
+ transformationLogic: string[];
584
+ timezoneHandling: string[];
585
+ encryptionSettings: string[];
586
+ hardcodedSecretSignals: string[];
587
+ }
588
+ interface AssetBrandingResult {
589
+ faviconFiles: Array<{
590
+ path: string;
591
+ base64: string;
592
+ }>;
593
+ productLogos: string[];
594
+ }
595
+ interface OssGovernanceResult {
596
+ directDependencies: number;
597
+ transitiveDependencies: number;
598
+ knownVulnerabilities: string[];
599
+ licenseRisks: string[];
600
+ }
490
601
  interface ExtendedScanResults {
491
602
  platformMatrix?: PlatformMatrixResult;
492
603
  dependencyRisk?: DependencyRiskResult;
@@ -503,6 +614,12 @@ interface ExtendedScanResults {
503
614
  codeQuality?: CodeQualityResult;
504
615
  owaspCategoryMapping?: OwaspCategoryMappingResult;
505
616
  uiPurpose?: UiPurposeResult;
617
+ runtimeConfiguration?: RuntimeConfigurationResult;
618
+ dataStores?: DataStoresResult;
619
+ apiSurface?: ApiSurfaceResult;
620
+ operationalResilience?: OperationalResilienceResult;
621
+ assetBranding?: AssetBrandingResult;
622
+ ossGovernance?: OssGovernanceResult;
506
623
  }
507
624
  interface OwaspFinding {
508
625
  ruleId: string;
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  formatText,
6
6
  generateFindings,
7
7
  runScan
8
- } from "./chunk-IQ4T2HLG.js";
8
+ } from "./chunk-XDZGC6KJ.js";
9
9
  import "./chunk-TBE6NQ5Z.js";
10
10
  export {
11
11
  computeDriftScore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibgrate/cli",
3
- "version": "1.0.64",
3
+ "version": "1.0.65",
4
4
  "description": "CLI for measuring upgrade drift across Node, .NET, Python & Java projects",
5
5
  "type": "module",
6
6
  "bin": {