@kinkai.cloud/vibecheck 0.2.0 → 0.3.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/README.md +40 -0
- package/dist/cli.js +3543 -479
- package/engines/engines.json +7 -0
- package/engines/install.py +20 -0
- package/engines/requirements.txt +66 -0
- package/package.json +6 -3
package/dist/cli.js
CHANGED
|
@@ -5,20 +5,9 @@ var __export = (target, all2) => {
|
|
|
5
5
|
__defProp(target, name, { get: all2[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"browser",
|
|
12
|
-
"app",
|
|
13
|
-
"api",
|
|
14
|
-
"db",
|
|
15
|
-
"storage",
|
|
16
|
-
"auth",
|
|
17
|
-
"ai",
|
|
18
|
-
"payments",
|
|
19
|
-
"secrets"
|
|
20
|
-
];
|
|
21
|
-
var CATEGORIES = ["secrets", "data", "storage", "auth", "ai", "config"];
|
|
8
|
+
// src/analyze.ts
|
|
9
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync, lstatSync as lstatSync2 } from "fs";
|
|
10
|
+
import { resolve as resolve2, join as join3 } from "path";
|
|
22
11
|
|
|
23
12
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
24
13
|
var external_exports = {};
|
|
@@ -804,14 +793,14 @@ function promiseAllObject(promisesObj) {
|
|
|
804
793
|
}
|
|
805
794
|
function randomString(length = 10) {
|
|
806
795
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
807
|
-
let
|
|
796
|
+
let str2 = "";
|
|
808
797
|
for (let i = 0; i < length; i++) {
|
|
809
|
-
|
|
798
|
+
str2 += chars[Math.floor(Math.random() * chars.length)];
|
|
810
799
|
}
|
|
811
|
-
return
|
|
800
|
+
return str2;
|
|
812
801
|
}
|
|
813
|
-
function esc(
|
|
814
|
-
return JSON.stringify(
|
|
802
|
+
function esc(str2) {
|
|
803
|
+
return JSON.stringify(str2);
|
|
815
804
|
}
|
|
816
805
|
function slugify(input) {
|
|
817
806
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -925,8 +914,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
925
914
|
"symbol",
|
|
926
915
|
"undefined"
|
|
927
916
|
]);
|
|
928
|
-
function escapeRegex(
|
|
929
|
-
return
|
|
917
|
+
function escapeRegex(str2) {
|
|
918
|
+
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
930
919
|
}
|
|
931
920
|
function clone(inst, def, params) {
|
|
932
921
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -7658,8 +7647,8 @@ function ko_default() {
|
|
|
7658
7647
|
}
|
|
7659
7648
|
|
|
7660
7649
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/locales/lt.js
|
|
7661
|
-
var capitalizeFirstCharacter = (
|
|
7662
|
-
return
|
|
7650
|
+
var capitalizeFirstCharacter = (text2) => {
|
|
7651
|
+
return text2.charAt(0).toUpperCase() + text2.slice(1);
|
|
7663
7652
|
};
|
|
7664
7653
|
function getUnitTypeFromNumber(number4) {
|
|
7665
7654
|
const abs = Math.abs(number4);
|
|
@@ -14534,200 +14523,3469 @@ function date4(params) {
|
|
|
14534
14523
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
14535
14524
|
config(en_default());
|
|
14536
14525
|
|
|
14537
|
-
// ../../packages/
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
|
|
14546
|
-
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
var MAX_HINT_BUCKETS = 24;
|
|
14552
|
-
var MAX_HINT_COLLECTIONS = 24;
|
|
14553
|
-
var EMPTY_DATA = { tables: [], buckets: [], collections: [] };
|
|
14554
|
-
var pathSchema = external_exports.string().min(1).max(MAX_PATH);
|
|
14555
|
-
var nodeSchema = external_exports.object({
|
|
14556
|
-
id: external_exports.string().min(1).max(MAX_ID),
|
|
14557
|
-
kind: external_exports.enum(NODE_KINDS),
|
|
14558
|
-
label: external_exports.string().min(1).max(MAX_LABEL),
|
|
14559
|
-
files: external_exports.array(pathSchema).max(MAX_FILES_PER_NODE)
|
|
14560
|
-
});
|
|
14561
|
-
var edgeSchema = external_exports.object({
|
|
14562
|
-
from: external_exports.string().min(1).max(MAX_ID),
|
|
14563
|
-
to: external_exports.string().min(1).max(MAX_ID),
|
|
14564
|
-
type: external_exports.enum(EDGE_TYPES)
|
|
14565
|
-
});
|
|
14566
|
-
var communitySchema = external_exports.object({
|
|
14567
|
-
nodeKind: external_exports.enum(NODE_KINDS),
|
|
14568
|
-
label: external_exports.string().min(1).max(MAX_LABEL),
|
|
14569
|
-
files: external_exports.array(pathSchema).max(MAX_COMMUNITY_FILES)
|
|
14570
|
-
});
|
|
14571
|
-
var tableName = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/);
|
|
14572
|
-
var bucketName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/);
|
|
14573
|
-
var dataSchema = external_exports.object({
|
|
14574
|
-
tables: external_exports.array(tableName).max(MAX_HINT_TABLES),
|
|
14575
|
-
buckets: external_exports.array(bucketName).max(MAX_HINT_BUCKETS),
|
|
14576
|
-
collections: external_exports.array(tableName).max(MAX_HINT_COLLECTIONS)
|
|
14577
|
-
});
|
|
14578
|
-
var scanGraphSchema = external_exports.object({
|
|
14579
|
-
version: external_exports.literal(1),
|
|
14580
|
-
advanced: external_exports.boolean(),
|
|
14581
|
-
nodes: external_exports.array(nodeSchema).max(MAX_NODES),
|
|
14582
|
-
edges: external_exports.array(edgeSchema).max(MAX_EDGES),
|
|
14583
|
-
communities: external_exports.array(communitySchema).max(MAX_COMMUNITIES),
|
|
14584
|
-
packages: external_exports.array(external_exports.string().min(1).max(MAX_LABEL)).max(MAX_PACKAGES),
|
|
14585
|
-
data: dataSchema.optional()
|
|
14586
|
-
});
|
|
14587
|
-
var EMPTY_HINTS = {
|
|
14588
|
-
apiRoutes: [],
|
|
14589
|
-
tableNames: [],
|
|
14590
|
-
buckets: [],
|
|
14591
|
-
collections: []
|
|
14592
|
-
};
|
|
14526
|
+
// ../../packages/analysis/src/index.ts
|
|
14527
|
+
import { randomUUID } from "crypto";
|
|
14528
|
+
import {
|
|
14529
|
+
mkdtempSync,
|
|
14530
|
+
readFileSync as readFileSync2,
|
|
14531
|
+
writeFileSync,
|
|
14532
|
+
rmSync,
|
|
14533
|
+
readdirSync,
|
|
14534
|
+
lstatSync,
|
|
14535
|
+
mkdirSync,
|
|
14536
|
+
copyFileSync
|
|
14537
|
+
} from "fs";
|
|
14538
|
+
import { tmpdir } from "os";
|
|
14539
|
+
import { join as join2, relative, resolve, sep } from "path";
|
|
14593
14540
|
|
|
14594
|
-
// ../../packages/
|
|
14595
|
-
|
|
14596
|
-
|
|
14541
|
+
// ../../packages/checks/src/code/types.ts
|
|
14542
|
+
var API_ROUTE_FILE = /(^|\/)(app\/.*\/route\.(ts|js|tsx|jsx)|pages\/api\/.+\.(ts|js))$/;
|
|
14543
|
+
function isApiRoute(path) {
|
|
14544
|
+
return API_ROUTE_FILE.test(path);
|
|
14545
|
+
}
|
|
14546
|
+
|
|
14547
|
+
// ../../packages/checks/src/code/checks.ts
|
|
14548
|
+
var MIGRATION_FILE = /(^|\/)supabase\/migrations\/[^/]+\.sql$/;
|
|
14549
|
+
var CREATE_TABLE = /create\s+table\s+(?:if\s+not\s+exists\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?/gi;
|
|
14550
|
+
var ENABLE_RLS = /alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?\s+enable\s+row\s+level\s+security/gi;
|
|
14551
|
+
function all(pattern, text2) {
|
|
14597
14552
|
const out = [];
|
|
14598
|
-
for (const
|
|
14599
|
-
|
|
14600
|
-
if (
|
|
14601
|
-
seen.add(value);
|
|
14602
|
-
out.push(value);
|
|
14553
|
+
for (const m of text2.matchAll(pattern)) {
|
|
14554
|
+
const nome = m[1];
|
|
14555
|
+
if (nome !== void 0) out.push(nome.toLowerCase());
|
|
14603
14556
|
}
|
|
14604
14557
|
return out;
|
|
14605
14558
|
}
|
|
14606
|
-
function
|
|
14607
|
-
|
|
14608
|
-
|
|
14609
|
-
|
|
14610
|
-
const segments = app[1].split("/").filter((s) => s.length > 0 && !s.startsWith("(") && !s.startsWith("@")).map((s) => s.replace(/^\[+\.\.\.(.+)\]+$/, "*").replace(/^\[+(.+)\]+$/, ":$1"));
|
|
14611
|
-
if (segments.length === 0) return "/api";
|
|
14612
|
-
return `/${segments.join("/")}`;
|
|
14613
|
-
}
|
|
14614
|
-
const pages = /(?:^|\/)pages\/api\/(.+)\.(t|j)sx?$/i.exec(normal);
|
|
14615
|
-
if (pages?.[1] !== void 0) {
|
|
14616
|
-
const withoutIndex = pages[1].replace(/\/index$/i, "");
|
|
14617
|
-
return `/api/${withoutIndex}`;
|
|
14618
|
-
}
|
|
14619
|
-
return null;
|
|
14620
|
-
}
|
|
14621
|
-
function filesFromGraph(graph) {
|
|
14622
|
-
return [
|
|
14623
|
-
...graph.nodes.flatMap((n) => n.files),
|
|
14624
|
-
...graph.communities.flatMap((c) => c.files)
|
|
14625
|
-
];
|
|
14626
|
-
}
|
|
14627
|
-
function hintsFromGraph(graph) {
|
|
14628
|
-
if (graph === null || graph === void 0 || !graph.advanced) return EMPTY_HINTS;
|
|
14629
|
-
const routes = [];
|
|
14630
|
-
for (const file2 of filesFromGraph(graph)) {
|
|
14631
|
-
const route = routeFromFile(file2);
|
|
14632
|
-
if (route !== null) routes.push(route);
|
|
14633
|
-
}
|
|
14634
|
-
routes.sort((a, b) => a.localeCompare(b));
|
|
14635
|
-
const data = graph.data ?? EMPTY_DATA;
|
|
14636
|
-
return {
|
|
14637
|
-
apiRoutes: uniqueCapped(routes, MAX_HINT_ROUTES),
|
|
14638
|
-
tableNames: uniqueCapped(data.tables, MAX_HINT_TABLES),
|
|
14639
|
-
buckets: uniqueCapped(data.buckets, MAX_HINT_BUCKETS),
|
|
14640
|
-
collections: uniqueCapped(data.collections, MAX_HINT_COLLECTIONS)
|
|
14641
|
-
};
|
|
14559
|
+
function isClientFile(file2) {
|
|
14560
|
+
if (/^\s*['"]use client['"]/.test(file2.content)) return true;
|
|
14561
|
+
if (/\.client\.(tsx|jsx|ts|js)$/.test(file2.path)) return true;
|
|
14562
|
+
return /(^|\/)pages\/(?!api\/).+\.(tsx|jsx|ts|js)$/.test(file2.path);
|
|
14642
14563
|
}
|
|
14643
|
-
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
14648
|
-
|
|
14649
|
-
|
|
14650
|
-
|
|
14651
|
-
|
|
14652
|
-
|
|
14653
|
-
|
|
14654
|
-
|
|
14655
|
-
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
} else if (naPilha.has(vizinho)) {
|
|
14682
|
-
lowlink.set(
|
|
14683
|
-
quadro.no,
|
|
14684
|
-
Math.min(lowlink.get(quadro.no) ?? 0, indice.get(vizinho) ?? 0)
|
|
14685
|
-
);
|
|
14686
|
-
}
|
|
14687
|
-
continue;
|
|
14688
|
-
}
|
|
14689
|
-
quadros.pop();
|
|
14690
|
-
const pai = quadros[quadros.length - 1];
|
|
14691
|
-
if (pai !== void 0) {
|
|
14692
|
-
lowlink.set(
|
|
14693
|
-
pai.no,
|
|
14694
|
-
Math.min(lowlink.get(pai.no) ?? 0, lowlink.get(quadro.no) ?? 0)
|
|
14695
|
-
);
|
|
14696
|
-
}
|
|
14697
|
-
if (lowlink.get(quadro.no) === indice.get(quadro.no)) {
|
|
14698
|
-
let tamanho = 0;
|
|
14699
|
-
let atual;
|
|
14700
|
-
do {
|
|
14701
|
-
atual = pilha.pop();
|
|
14702
|
-
if (atual === void 0) break;
|
|
14703
|
-
naPilha.delete(atual);
|
|
14704
|
-
tamanho += 1;
|
|
14705
|
-
} while (atual !== quadro.no);
|
|
14706
|
-
if (tamanho > 1) ciclos += 1;
|
|
14564
|
+
var TOUCHES_DB = /\b(supabase\.(?:from|rpc|storage)\b|prisma\.\w+\.\w+\(|drizzle\(|\bdb\.(?:select|insert|update|delete|query|execute|from)\(|\.from\(['"]|sql`|pool\.query\(|knex\(|mongoose\.|\.collection\(['"])/;
|
|
14565
|
+
var HAS_AUTH = /\b(getSession|getUser|auth\(\)|currentUser|requireAuth|verifyToken|verifyJwt|jwtVerify|authorization|Bearer|withAuth|clerk|next-auth|getServerSession|session)\b/i;
|
|
14566
|
+
var PUBLIC_ROUTE = /(^|\/)(webhooks?|health|healthz|ping|status|robots|sitemap|og|opengraph|cron)(\/|\.|$)/i;
|
|
14567
|
+
var AI_SDK = /from\s+['"](openai|@anthropic-ai\/sdk|ai|@ai-sdk\/[^'"]+|@google\/generative-ai|groq-sdk|cohere-ai|@mistralai\/mistralai)['"]/;
|
|
14568
|
+
var HAS_LIMITER = /\b(ratelimit|rateLimit|Ratelimit|upstash|limiter|throttle|slidingWindow|tokenBucket)\b/;
|
|
14569
|
+
var STRIPE_IMPORT = /from\s+['"]stripe['"]/;
|
|
14570
|
+
var WEBHOOK_PATH = /webhook/i;
|
|
14571
|
+
var SIGNATURE_CHECK = /constructEvent(?:Async)?\(|stripe-signature/;
|
|
14572
|
+
var SERVICE_ROLE_REF = /\b(SUPABASE_SERVICE_ROLE_KEY|SERVICE_ROLE_KEY|sb_secret_[A-Za-z0-9_]+|NEXT_PUBLIC_[A-Z0-9_]*(?:SECRET|SERVICE_ROLE|PRIVATE)[A-Z0-9_]*)\b/;
|
|
14573
|
+
var ENV_FILES = [
|
|
14574
|
+
".env",
|
|
14575
|
+
".env.local",
|
|
14576
|
+
".env.production",
|
|
14577
|
+
".env.development",
|
|
14578
|
+
".env.staging"
|
|
14579
|
+
];
|
|
14580
|
+
var codeRlsMissing = {
|
|
14581
|
+
id: "code-rls-missing",
|
|
14582
|
+
source: "code",
|
|
14583
|
+
category: "data",
|
|
14584
|
+
severity: "critical",
|
|
14585
|
+
title: "Table created without row level security",
|
|
14586
|
+
impact: "A migration creates a table and never turns on row level security for it. Once it has rows, anyone with the public key can read them \u2014 this is the hole the RLS check lights up from outside.",
|
|
14587
|
+
howToDetect: (ctx) => {
|
|
14588
|
+
const migrations = ctx.files.filter((f) => MIGRATION_FILE.test(f.path));
|
|
14589
|
+
if (migrations.length === 0) return [];
|
|
14590
|
+
const comRls = /* @__PURE__ */ new Set();
|
|
14591
|
+
for (const m of migrations) for (const t of all(ENABLE_RLS, m.content)) comRls.add(t);
|
|
14592
|
+
const out = [];
|
|
14593
|
+
const vistas = /* @__PURE__ */ new Set();
|
|
14594
|
+
for (const m of migrations) {
|
|
14595
|
+
for (const tabela of all(CREATE_TABLE, m.content)) {
|
|
14596
|
+
if (comRls.has(tabela) || vistas.has(tabela)) continue;
|
|
14597
|
+
vistas.add(tabela);
|
|
14598
|
+
out.push({
|
|
14599
|
+
path: m.path,
|
|
14600
|
+
reason: `table \`${tabela}\` is created without enable row level security`
|
|
14601
|
+
});
|
|
14707
14602
|
}
|
|
14708
14603
|
}
|
|
14709
|
-
|
|
14710
|
-
|
|
14711
|
-
}
|
|
14712
|
-
function graphMetrics(graph) {
|
|
14713
|
-
return {
|
|
14714
|
-
nodes: graph.nodes.length,
|
|
14715
|
-
edges: graph.edges.length,
|
|
14716
|
-
routes: hintsFromGraph(graph).apiRoutes.length,
|
|
14717
|
-
packages: graph.packages.length,
|
|
14718
|
-
cycles: importCycles(graph)
|
|
14719
|
-
};
|
|
14720
|
-
}
|
|
14604
|
+
return out;
|
|
14605
|
+
},
|
|
14606
|
+
fixPrompt: (a) => `In your code, ${a.reason} (${a.path}).
|
|
14721
14607
|
|
|
14722
|
-
|
|
14723
|
-
|
|
14724
|
-
|
|
14725
|
-
|
|
14726
|
-
|
|
14727
|
-
|
|
14728
|
-
|
|
14729
|
-
"
|
|
14730
|
-
"
|
|
14608
|
+
We do not know the columns or who should read what. We cannot write the policy from here.
|
|
14609
|
+
|
|
14610
|
+
Good practice: every table starts with row level security on, in the same migration that creates it. Then one policy per real access: a person reads their own rows; a public catalog, if it exists, is a deliberate read policy; writes always go through a session. Do this in a new migration, never by editing one that already ran.
|
|
14611
|
+
|
|
14612
|
+
After, deploy and read the table with the public key and no session \u2014 it must answer empty or denied.`
|
|
14613
|
+
};
|
|
14614
|
+
var codeServiceRoleInClient = {
|
|
14615
|
+
id: "code-service-role-in-client",
|
|
14616
|
+
source: "code",
|
|
14617
|
+
category: "secrets",
|
|
14618
|
+
severity: "fatal",
|
|
14619
|
+
title: "Service key referenced in client code",
|
|
14620
|
+
impact: "A file that runs in the browser mentions the Supabase service key or a NEXT_PUBLIC_ secret. Whatever the bundler inlines there ships to every visitor \u2014 this is the fatal finding from outside, one build away.",
|
|
14621
|
+
howToDetect: (ctx) => ctx.files.filter(isClientFile).filter((f) => SERVICE_ROLE_REF.test(f.content)).map((f) => ({
|
|
14622
|
+
path: f.path,
|
|
14623
|
+
reason: "client-side file references a service or secret key"
|
|
14624
|
+
})),
|
|
14625
|
+
fixPrompt: (a) => `In your code, a client-side file references a service or secret key (${a.path}).
|
|
14626
|
+
|
|
14627
|
+
We do not have the value and we do not want it. The problem is the place, not the string.
|
|
14628
|
+
|
|
14629
|
+
Good practice: the service key lives only in server code \u2014 a route handler, a server action, an edge function \u2014 read from the environment without the NEXT_PUBLIC_ prefix. The browser talks to your API; your API talks to the database. Remove the reference from the client file and, if the key was ever bundled, rotate it in the dashboard.
|
|
14630
|
+
|
|
14631
|
+
After, search the built bundle for the key prefix and confirm it is gone.`
|
|
14632
|
+
};
|
|
14633
|
+
var codeRouteWithoutAuth = {
|
|
14634
|
+
id: "code-route-without-auth",
|
|
14635
|
+
source: "code",
|
|
14636
|
+
category: "auth",
|
|
14637
|
+
severity: "high",
|
|
14638
|
+
title: "API route touches data without a session check",
|
|
14639
|
+
impact: "An API route reads or writes the database and nothing in it checks who is asking. If the middleware does not cover it, anyone can call it \u2014 this is what api-route-noauth lights up from outside.",
|
|
14640
|
+
howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && !PUBLIC_ROUTE.test(f.path)).filter((f) => TOUCHES_DB.test(f.content) && !HAS_AUTH.test(f.content)).map((f) => ({
|
|
14641
|
+
path: f.path,
|
|
14642
|
+
reason: "route reads or writes data with no session check in the file"
|
|
14643
|
+
})),
|
|
14644
|
+
fixPrompt: (a) => `In your code, a route reads or writes data with no session check in the file (${a.path}).
|
|
14645
|
+
|
|
14646
|
+
We cannot see your middleware. If it already protects this path, mark the finding as known.
|
|
14647
|
+
|
|
14648
|
+
Good practice: the route resolves the session first and answers 401 before touching data; what it reads or writes is scoped to that user. A public route is public on purpose, with a comment saying so, and never exposes rows that belong to someone.
|
|
14649
|
+
|
|
14650
|
+
After, call the route from a logged-out client and confirm it answers 401 or 403.`
|
|
14651
|
+
};
|
|
14652
|
+
var codeEnvCommitted = {
|
|
14653
|
+
id: "code-env-committed",
|
|
14654
|
+
source: "code",
|
|
14655
|
+
category: "secrets",
|
|
14656
|
+
severity: "critical",
|
|
14657
|
+
title: ".env file tracked by git",
|
|
14658
|
+
impact: "An environment file is committed to the repository. Every clone, fork, CI log and AI tool that reads the repo has the secrets in it. Even after deleting it, the history keeps the values.",
|
|
14659
|
+
// Só o nome. O conteúdo nunca é lido — é o único check que não recebe
|
|
14660
|
+
// `content`, e é assim de propósito.
|
|
14661
|
+
howToDetect: (ctx) => ENV_FILES.filter((nome) => ctx.tracked(nome)).map((nome) => ({
|
|
14662
|
+
path: nome,
|
|
14663
|
+
reason: `${nome} is tracked by git`
|
|
14664
|
+
})),
|
|
14665
|
+
fixPrompt: (a) => `In your code, ${a.reason}.
|
|
14666
|
+
|
|
14667
|
+
We did not read it. We only saw that git tracks it.
|
|
14668
|
+
|
|
14669
|
+
Good practice: environment files stay out of version control \u2014 add them to .gitignore, remove them from the index, and keep a committed .env.example with the variable names and no values. Every secret that was ever in the file is compromised: rotate each one in its provider.
|
|
14670
|
+
|
|
14671
|
+
After, run git ls-files and confirm no .env file is listed.`
|
|
14672
|
+
};
|
|
14673
|
+
var codeAiRouteWithoutLimit = {
|
|
14674
|
+
id: "code-ai-route-without-limit",
|
|
14675
|
+
source: "code",
|
|
14676
|
+
category: "ai",
|
|
14677
|
+
severity: "high",
|
|
14678
|
+
title: "AI route with no rate limit",
|
|
14679
|
+
impact: "A route calls an LLM provider and nothing in it limits how often. A loop from one client turns your provider account into an open bill \u2014 this is ai-proxy-open, seen from inside.",
|
|
14680
|
+
howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path)).filter((f) => AI_SDK.test(f.content) && !HAS_LIMITER.test(f.content)).map((f) => ({
|
|
14681
|
+
path: f.path,
|
|
14682
|
+
reason: "route calls an AI provider with no rate limit in the file"
|
|
14683
|
+
})),
|
|
14684
|
+
fixPrompt: (a) => `In your code, a route calls an AI provider with no rate limit in the file (${a.path}).
|
|
14685
|
+
|
|
14686
|
+
We cannot see limits applied elsewhere. If your gateway already throttles this path, mark the finding as known.
|
|
14687
|
+
|
|
14688
|
+
Good practice: the route requires a session, then applies a per-user limit (so many calls per minute, so many tokens per day) before calling the provider, and answers 429 when it is hit. Cost caps at the provider are the second net, not the first.
|
|
14689
|
+
|
|
14690
|
+
After, call the route in a loop from one client and confirm it starts answering 429.`
|
|
14691
|
+
};
|
|
14692
|
+
var codeWebhookUnsigned = {
|
|
14693
|
+
id: "code-webhook-unsigned",
|
|
14694
|
+
source: "code",
|
|
14695
|
+
category: "config",
|
|
14696
|
+
severity: "high",
|
|
14697
|
+
title: "Stripe webhook without signature verification",
|
|
14698
|
+
impact: 'A webhook route imports Stripe and never verifies the event signature. Anyone can POST a fake "payment succeeded" and your app will believe it \u2014 the one check we refuse to run from outside, because proving it would mean doing exactly that.',
|
|
14699
|
+
howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && WEBHOOK_PATH.test(f.path)).filter((f) => STRIPE_IMPORT.test(f.content) && !SIGNATURE_CHECK.test(f.content)).map((f) => ({
|
|
14700
|
+
path: f.path,
|
|
14701
|
+
reason: "Stripe webhook route never verifies the event signature"
|
|
14702
|
+
})),
|
|
14703
|
+
fixPrompt: (a) => `In your code, a Stripe webhook route never verifies the event signature (${a.path}).
|
|
14704
|
+
|
|
14705
|
+
We cannot see the secret and do not want it.
|
|
14706
|
+
|
|
14707
|
+
Good practice: the route reads the raw body and the stripe-signature header, builds the event with the webhook secret from the environment, and rejects anything that fails. Only the verified event drives state. Each event id is processed once.
|
|
14708
|
+
|
|
14709
|
+
After, POST a fabricated event to the route and confirm it answers 400.`
|
|
14710
|
+
};
|
|
14711
|
+
var CODE_CHECKS = [
|
|
14712
|
+
codeRlsMissing,
|
|
14713
|
+
codeServiceRoleInClient,
|
|
14714
|
+
codeRouteWithoutAuth,
|
|
14715
|
+
codeEnvCommitted,
|
|
14716
|
+
codeAiRouteWithoutLimit,
|
|
14717
|
+
codeWebhookUnsigned
|
|
14718
|
+
];
|
|
14719
|
+
function runCodeChecks(ctx, catalog = CODE_CHECKS, options = {}) {
|
|
14720
|
+
const out = [];
|
|
14721
|
+
for (const check2 of catalog) {
|
|
14722
|
+
let advisories = [];
|
|
14723
|
+
try {
|
|
14724
|
+
advisories = check2.howToDetect(ctx);
|
|
14725
|
+
} catch {
|
|
14726
|
+
if (options.failOnError) throw new Error(`Code check failed: ${check2.id}`);
|
|
14727
|
+
advisories = [];
|
|
14728
|
+
}
|
|
14729
|
+
for (const a of advisories) {
|
|
14730
|
+
out.push({
|
|
14731
|
+
checkId: check2.id,
|
|
14732
|
+
severity: check2.severity,
|
|
14733
|
+
path: a.path,
|
|
14734
|
+
reason: a.reason
|
|
14735
|
+
});
|
|
14736
|
+
}
|
|
14737
|
+
}
|
|
14738
|
+
return out;
|
|
14739
|
+
}
|
|
14740
|
+
|
|
14741
|
+
// ../../packages/schema/src/analysis.ts
|
|
14742
|
+
var text = external_exports.string().max(400);
|
|
14743
|
+
var relativePath = external_exports.string().min(1).max(400).refine(
|
|
14744
|
+
(p) => !p.startsWith("/") && !p.includes("\\") && !p.includes(":") && !p.split("/").includes("..") && ![...p].some((c) => c.charCodeAt(0) < 32),
|
|
14745
|
+
"Expected repository-relative path"
|
|
14746
|
+
);
|
|
14747
|
+
var location = external_exports.object({ path: relativePath, line: external_exports.number().int().positive().optional() }).strict();
|
|
14748
|
+
var analysisFindingSchema = external_exports.object({
|
|
14749
|
+
fingerprint: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
14750
|
+
matchKey: external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
14751
|
+
observed: external_exports.boolean().optional(),
|
|
14752
|
+
rule: text,
|
|
14753
|
+
engine: text,
|
|
14754
|
+
category: external_exports.enum(["security", "quality", "dependency", "secret"]),
|
|
14755
|
+
severity: external_exports.enum(["critical", "high", "medium", "low", "unknown"]),
|
|
14756
|
+
language: text,
|
|
14757
|
+
title: text,
|
|
14758
|
+
explanation: text,
|
|
14759
|
+
remediation: text,
|
|
14760
|
+
location,
|
|
14761
|
+
flow: external_exports.array(location).max(100).default([]),
|
|
14762
|
+
state: external_exports.enum(["new", "persisting", "resolved", "unverified", "accepted"]).default("new"),
|
|
14763
|
+
package: external_exports.object({
|
|
14764
|
+
name: text,
|
|
14765
|
+
version: text,
|
|
14766
|
+
ecosystem: text,
|
|
14767
|
+
advisory: text,
|
|
14768
|
+
fixed: external_exports.array(text).max(50),
|
|
14769
|
+
relationship: external_exports.enum(["direct", "transitive", "unknown"])
|
|
14770
|
+
}).strict().optional()
|
|
14771
|
+
}).strict();
|
|
14772
|
+
var engineSchema = external_exports.object({
|
|
14773
|
+
id: text,
|
|
14774
|
+
version: text,
|
|
14775
|
+
rules: text,
|
|
14776
|
+
status: external_exports.enum(["ran", "unavailable", "not-applicable", "failed"]),
|
|
14777
|
+
durationMs: external_exports.number().nonnegative(),
|
|
14778
|
+
files: external_exports.array(relativePath).max(1e5),
|
|
14779
|
+
excluded: external_exports.array(relativePath).max(1e5),
|
|
14780
|
+
message: text
|
|
14781
|
+
}).strict();
|
|
14782
|
+
var policySchema = external_exports.object({
|
|
14783
|
+
version: external_exports.number().int().positive(),
|
|
14784
|
+
coverage: external_exports.number().min(0).max(100),
|
|
14785
|
+
requiredEngines: external_exports.array(external_exports.enum(["semgrep", "osv", "gitleaks", "builtin"])).max(4)
|
|
14786
|
+
}).strict();
|
|
14787
|
+
var DEFAULT_ANALYSIS_POLICY = {
|
|
14788
|
+
version: 1,
|
|
14789
|
+
coverage: 80,
|
|
14790
|
+
requiredEngines: ["semgrep", "osv", "gitleaks", "builtin"]
|
|
14791
|
+
};
|
|
14792
|
+
var conditionSchema = external_exports.object({
|
|
14793
|
+
id: external_exports.enum(["baseline", "engines", "security", "coverage", "tests"]),
|
|
14794
|
+
status: external_exports.enum(["passed", "failed", "inconclusive", "not-applicable"]),
|
|
14795
|
+
value: external_exports.number().nullable(),
|
|
14796
|
+
limit: external_exports.number().nullable()
|
|
14797
|
+
}).strict();
|
|
14798
|
+
var analysisReportSchema = external_exports.object({
|
|
14799
|
+
version: external_exports.literal(2),
|
|
14800
|
+
projectId: external_exports.string().uuid(),
|
|
14801
|
+
runId: external_exports.string().uuid(),
|
|
14802
|
+
provenance: external_exports.literal("local-ci"),
|
|
14803
|
+
startedAt: external_exports.string().datetime(),
|
|
14804
|
+
commit: external_exports.string().regex(/^[a-f0-9]{40,64}$/).nullable(),
|
|
14805
|
+
branch: text,
|
|
14806
|
+
reference: text,
|
|
14807
|
+
mergeBase: external_exports.string().regex(/^[a-f0-9]{40,64}$/).nullable(),
|
|
14808
|
+
baselineRunId: external_exports.string().uuid().nullable(),
|
|
14809
|
+
scope: external_exports.string().max(100),
|
|
14810
|
+
engines: external_exports.array(engineSchema).max(30),
|
|
14811
|
+
findings: external_exports.array(analysisFindingSchema).max(5e4),
|
|
14812
|
+
coverage: external_exports.object({
|
|
14813
|
+
status: external_exports.enum(["ran", "missing", "failed"]),
|
|
14814
|
+
files: external_exports.array(
|
|
14815
|
+
external_exports.object({
|
|
14816
|
+
path: relativePath,
|
|
14817
|
+
lines: external_exports.array(
|
|
14818
|
+
external_exports.object({
|
|
14819
|
+
line: external_exports.number().int().positive(),
|
|
14820
|
+
hits: external_exports.number().int().nonnegative()
|
|
14821
|
+
}).strict()
|
|
14822
|
+
).max(2e5)
|
|
14823
|
+
}).strict()
|
|
14824
|
+
).max(1e5)
|
|
14825
|
+
}).strict(),
|
|
14826
|
+
tests: external_exports.object({
|
|
14827
|
+
status: external_exports.enum(["ran", "missing", "failed"]),
|
|
14828
|
+
total: external_exports.number().int().nonnegative(),
|
|
14829
|
+
failed: external_exports.number().int().nonnegative(),
|
|
14830
|
+
skipped: external_exports.number().int().nonnegative(),
|
|
14831
|
+
failures: external_exports.array(
|
|
14832
|
+
external_exports.object({
|
|
14833
|
+
id: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
14834
|
+
location: location.optional(),
|
|
14835
|
+
kind: external_exports.enum(["failure", "error"])
|
|
14836
|
+
}).strict()
|
|
14837
|
+
).max(1e4).optional()
|
|
14838
|
+
}).strict(),
|
|
14839
|
+
changes: external_exports.array(
|
|
14840
|
+
external_exports.object({
|
|
14841
|
+
path: relativePath,
|
|
14842
|
+
previousPath: relativePath.optional(),
|
|
14843
|
+
lines: external_exports.array(external_exports.number().int().positive()).max(2e5)
|
|
14844
|
+
}).strict()
|
|
14845
|
+
).max(1e5),
|
|
14846
|
+
policy: policySchema,
|
|
14847
|
+
conditions: external_exports.array(conditionSchema).max(10),
|
|
14848
|
+
status: external_exports.enum(["passed", "failed", "inconclusive"])
|
|
14849
|
+
}).strict();
|
|
14850
|
+
function evaluateAnalysis(input, baseline, policy = DEFAULT_ANALYSIS_POLICY, exceptions = [], now = /* @__PURE__ */ new Date()) {
|
|
14851
|
+
const comparable = baseline !== null && input.mergeBase !== null && baseline.commit === input.mergeBase && baseline.projectId === input.projectId && baseline.scope === input.scope;
|
|
14852
|
+
const previous = new Map(
|
|
14853
|
+
(comparable ? baseline.findings : []).filter((f) => f.observed !== false && f.state !== "resolved").map((f) => [f.fingerprint, f])
|
|
14854
|
+
);
|
|
14855
|
+
const observations = input.findings.filter((f) => f.observed !== false);
|
|
14856
|
+
const current = new Set(observations.map((f) => f.fingerprint));
|
|
14857
|
+
const renamed = new Map(
|
|
14858
|
+
input.changes.filter((c) => c.previousPath).map((c) => [c.path, c.previousPath])
|
|
14859
|
+
);
|
|
14860
|
+
const matchedPrevious = /* @__PURE__ */ new Set();
|
|
14861
|
+
const active = new Set(
|
|
14862
|
+
exceptions.filter((e) => e.reason.trim() && new Date(e.expiresAt) > now).map((e) => e.fingerprint)
|
|
14863
|
+
);
|
|
14864
|
+
const findings = observations.map((f) => {
|
|
14865
|
+
const engine = input.engines.find((e) => e.id === f.engine);
|
|
14866
|
+
const oldEngine = baseline?.engines.find((e) => e.id === f.engine);
|
|
14867
|
+
const old = previous.get(f.fingerprint) ?? (f.matchKey && renamed.has(f.location.path) ? [...previous.values()].find(
|
|
14868
|
+
(p) => p.matchKey === f.matchKey && p.location.path === renamed.get(f.location.path)
|
|
14869
|
+
) : void 0);
|
|
14870
|
+
const known = old !== void 0;
|
|
14871
|
+
if (old) matchedPrevious.add(old.fingerprint);
|
|
14872
|
+
const compatibleEngine = engine?.status === "ran" && oldEngine !== void 0 && ["ran", "not-applicable"].includes(oldEngine.status) && engine.version === oldEngine.version && engine.rules === oldEngine.rules;
|
|
14873
|
+
return {
|
|
14874
|
+
...f,
|
|
14875
|
+
observed: true,
|
|
14876
|
+
state: active.has(f.fingerprint) ? "accepted" : !comparable || !compatibleEngine ? "unverified" : known ? "persisting" : "new"
|
|
14877
|
+
};
|
|
14878
|
+
});
|
|
14879
|
+
if (comparable)
|
|
14880
|
+
for (const old of previous.values()) {
|
|
14881
|
+
if (current.has(old.fingerprint) || matchedPrevious.has(old.fingerprint)) continue;
|
|
14882
|
+
const engine = input.engines.find((e) => e.id === old.engine);
|
|
14883
|
+
const before = baseline.engines.find((e) => e.id === old.engine);
|
|
14884
|
+
const covered2 = engine?.status === "ran" && engine.version === before?.version && engine.rules === before?.rules && engine.files.includes(old.location.path) && !engine.excluded.includes(old.location.path);
|
|
14885
|
+
findings.push({
|
|
14886
|
+
...old,
|
|
14887
|
+
observed: false,
|
|
14888
|
+
state: covered2 ? "resolved" : "unverified"
|
|
14889
|
+
});
|
|
14890
|
+
}
|
|
14891
|
+
const missingEngine = input.engines.some((e) => ["failed", "unavailable"].includes(e.status)) || input.engines.some(
|
|
14892
|
+
(e) => input.changes.some((c) => c.lines.length > 0 && e.excluded.includes(c.path))
|
|
14893
|
+
) || comparable && input.engines.some((e) => {
|
|
14894
|
+
const before = baseline.engines.find((b) => b.id === e.id);
|
|
14895
|
+
return !before || before.rules !== e.rules || before.version !== e.version || !["ran", "not-applicable"].includes(before.status);
|
|
14896
|
+
}) || policy.requiredEngines.some(
|
|
14897
|
+
(id) => !input.engines.some(
|
|
14898
|
+
(e) => e.id === id && ["ran", "not-applicable"].includes(e.status)
|
|
14899
|
+
)
|
|
14900
|
+
);
|
|
14901
|
+
const coverageByPath = new Map(
|
|
14902
|
+
input.coverage.files.map((f) => [
|
|
14903
|
+
f.path,
|
|
14904
|
+
new Map(f.lines.map((l) => [l.line, l.hits]))
|
|
14905
|
+
])
|
|
14906
|
+
);
|
|
14907
|
+
let executable = 0, covered = 0;
|
|
14908
|
+
let unknownFile = false;
|
|
14909
|
+
for (const change of input.changes) {
|
|
14910
|
+
if (!change.lines.length || !/\.(tsx?|jsx?|py|go|java|cs)$/.test(change.path))
|
|
14911
|
+
continue;
|
|
14912
|
+
const lines = coverageByPath.get(change.path);
|
|
14913
|
+
if (!lines) {
|
|
14914
|
+
unknownFile = true;
|
|
14915
|
+
continue;
|
|
14916
|
+
}
|
|
14917
|
+
for (const line of change.lines)
|
|
14918
|
+
if (lines.has(line)) {
|
|
14919
|
+
executable++;
|
|
14920
|
+
if ((lines.get(line) ?? 0) > 0) covered++;
|
|
14921
|
+
}
|
|
14922
|
+
}
|
|
14923
|
+
const percent = executable ? covered / executable * 100 : null;
|
|
14924
|
+
const blockers = findings.filter(
|
|
14925
|
+
(f) => f.state === "new" && (f.category === "secret" || f.category !== "quality" && ["critical", "high"].includes(f.severity))
|
|
14926
|
+
).length;
|
|
14927
|
+
const uncertain = findings.some(
|
|
14928
|
+
(f) => (f.state === "unverified" || f.state === "new" && f.severity === "unknown") && f.category !== "quality"
|
|
14929
|
+
);
|
|
14930
|
+
const conditions = [
|
|
14931
|
+
{
|
|
14932
|
+
id: "baseline",
|
|
14933
|
+
status: comparable ? "passed" : "inconclusive",
|
|
14934
|
+
value: null,
|
|
14935
|
+
limit: null
|
|
14936
|
+
},
|
|
14937
|
+
{
|
|
14938
|
+
id: "engines",
|
|
14939
|
+
status: missingEngine || uncertain ? "inconclusive" : "passed",
|
|
14940
|
+
value: null,
|
|
14941
|
+
limit: null
|
|
14942
|
+
},
|
|
14943
|
+
{
|
|
14944
|
+
id: "security",
|
|
14945
|
+
status: blockers ? "failed" : comparable ? "passed" : "inconclusive",
|
|
14946
|
+
value: blockers,
|
|
14947
|
+
limit: 0
|
|
14948
|
+
},
|
|
14949
|
+
{
|
|
14950
|
+
id: "coverage",
|
|
14951
|
+
status: !comparable || unknownFile || input.coverage.status === "failed" || executable > 0 && input.coverage.status !== "ran" ? "inconclusive" : percent === null ? "not-applicable" : percent < policy.coverage ? "failed" : "passed",
|
|
14952
|
+
value: percent,
|
|
14953
|
+
limit: policy.coverage
|
|
14954
|
+
},
|
|
14955
|
+
{
|
|
14956
|
+
id: "tests",
|
|
14957
|
+
status: input.tests.status === "failed" ? "inconclusive" : input.tests.failed ? "failed" : input.tests.status === "missing" ? "not-applicable" : "passed",
|
|
14958
|
+
value: input.tests.failed,
|
|
14959
|
+
limit: 0
|
|
14960
|
+
}
|
|
14961
|
+
];
|
|
14962
|
+
return {
|
|
14963
|
+
...input,
|
|
14964
|
+
findings,
|
|
14965
|
+
policy,
|
|
14966
|
+
conditions,
|
|
14967
|
+
status: conditions.some((c) => c.status === "failed") ? "failed" : conditions.some((c) => c.status === "inconclusive") ? "inconclusive" : "passed"
|
|
14968
|
+
};
|
|
14969
|
+
}
|
|
14970
|
+
|
|
14971
|
+
// ../../packages/analysis/src/process.ts
|
|
14972
|
+
import { spawn } from "child_process";
|
|
14973
|
+
function execute(file2, args, cwd, timeoutMs = 12e4) {
|
|
14974
|
+
return new Promise((resolve4) => {
|
|
14975
|
+
const child = spawn(file2, args, {
|
|
14976
|
+
cwd,
|
|
14977
|
+
shell: false,
|
|
14978
|
+
windowsHide: true,
|
|
14979
|
+
detached: process.platform !== "win32",
|
|
14980
|
+
env: { ...process.env, SEMGREP_SEND_METRICS: "off" },
|
|
14981
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
14982
|
+
});
|
|
14983
|
+
let stdout = "", bytes = 0, done = false;
|
|
14984
|
+
const finish = (code, unavailable = false) => {
|
|
14985
|
+
if (done) return;
|
|
14986
|
+
done = true;
|
|
14987
|
+
clearTimeout(timer);
|
|
14988
|
+
resolve4({ code, stdout, unavailable });
|
|
14989
|
+
};
|
|
14990
|
+
const timer = setTimeout(() => {
|
|
14991
|
+
stop();
|
|
14992
|
+
finish(124);
|
|
14993
|
+
}, timeoutMs);
|
|
14994
|
+
function stop() {
|
|
14995
|
+
try {
|
|
14996
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, "SIGKILL");
|
|
14997
|
+
else child.kill("SIGKILL");
|
|
14998
|
+
} catch {
|
|
14999
|
+
}
|
|
15000
|
+
}
|
|
15001
|
+
child.stdout.on("data", (chunk) => {
|
|
15002
|
+
bytes += chunk.length;
|
|
15003
|
+
if (bytes > 2e7) {
|
|
15004
|
+
stop();
|
|
15005
|
+
finish(125);
|
|
15006
|
+
} else stdout += chunk.toString();
|
|
15007
|
+
});
|
|
15008
|
+
child.stderr.on("data", (chunk) => {
|
|
15009
|
+
bytes += chunk.length;
|
|
15010
|
+
if (bytes > 2e7) {
|
|
15011
|
+
stop();
|
|
15012
|
+
finish(125);
|
|
15013
|
+
}
|
|
15014
|
+
});
|
|
15015
|
+
child.on("error", () => finish(127, true));
|
|
15016
|
+
child.on("close", (code) => finish(code ?? 126));
|
|
15017
|
+
});
|
|
15018
|
+
}
|
|
15019
|
+
|
|
15020
|
+
// ../../packages/analysis/src/importers.ts
|
|
15021
|
+
import { createHash } from "crypto";
|
|
15022
|
+
|
|
15023
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/util.js
|
|
15024
|
+
var nameStartChar = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
|
15025
|
+
var nameChar = nameStartChar + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
|
|
15026
|
+
var nameRegexp = "[" + nameStartChar + "][" + nameChar + "]*";
|
|
15027
|
+
var regexName = new RegExp("^" + nameRegexp + "$");
|
|
15028
|
+
function getAllMatches(string4, regex) {
|
|
15029
|
+
const matches = [];
|
|
15030
|
+
let match = regex.exec(string4);
|
|
15031
|
+
while (match) {
|
|
15032
|
+
const allmatches = [];
|
|
15033
|
+
allmatches.startIndex = regex.lastIndex - match[0].length;
|
|
15034
|
+
const len = match.length;
|
|
15035
|
+
for (let index = 0; index < len; index++) {
|
|
15036
|
+
allmatches.push(match[index]);
|
|
15037
|
+
}
|
|
15038
|
+
matches.push(allmatches);
|
|
15039
|
+
match = regex.exec(string4);
|
|
15040
|
+
}
|
|
15041
|
+
return matches;
|
|
15042
|
+
}
|
|
15043
|
+
var isName = function(string4) {
|
|
15044
|
+
const match = regexName.exec(string4);
|
|
15045
|
+
return !(match === null || typeof match === "undefined");
|
|
15046
|
+
};
|
|
15047
|
+
function isExist(v) {
|
|
15048
|
+
return typeof v !== "undefined";
|
|
15049
|
+
}
|
|
15050
|
+
|
|
15051
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/validator.js
|
|
15052
|
+
var defaultOptions = {
|
|
15053
|
+
allowBooleanAttributes: false,
|
|
15054
|
+
//A tag can have attributes without any value
|
|
15055
|
+
unpairedTags: []
|
|
15056
|
+
};
|
|
15057
|
+
function validate(xmlData, options) {
|
|
15058
|
+
options = Object.assign({}, defaultOptions, options);
|
|
15059
|
+
const tags = [];
|
|
15060
|
+
let tagFound = false;
|
|
15061
|
+
let reachedRoot = false;
|
|
15062
|
+
if (xmlData[0] === "\uFEFF") {
|
|
15063
|
+
xmlData = xmlData.substr(1);
|
|
15064
|
+
}
|
|
15065
|
+
for (let i = 0; i < xmlData.length; i++) {
|
|
15066
|
+
if (xmlData[i] === "<" && xmlData[i + 1] === "?") {
|
|
15067
|
+
i += 2;
|
|
15068
|
+
i = readPI(xmlData, i);
|
|
15069
|
+
if (i.err) return i;
|
|
15070
|
+
} else if (xmlData[i] === "<") {
|
|
15071
|
+
let tagStartPos = i;
|
|
15072
|
+
i++;
|
|
15073
|
+
if (xmlData[i] === "!") {
|
|
15074
|
+
i = readCommentAndCDATA(xmlData, i);
|
|
15075
|
+
continue;
|
|
15076
|
+
} else {
|
|
15077
|
+
let closingTag = false;
|
|
15078
|
+
if (xmlData[i] === "/") {
|
|
15079
|
+
closingTag = true;
|
|
15080
|
+
i++;
|
|
15081
|
+
}
|
|
15082
|
+
let tagName = "";
|
|
15083
|
+
for (; i < xmlData.length && xmlData[i] !== ">" && xmlData[i] !== " " && xmlData[i] !== " " && xmlData[i] !== "\n" && xmlData[i] !== "\r"; i++) {
|
|
15084
|
+
tagName += xmlData[i];
|
|
15085
|
+
}
|
|
15086
|
+
tagName = tagName.trim();
|
|
15087
|
+
if (tagName[tagName.length - 1] === "/") {
|
|
15088
|
+
tagName = tagName.substring(0, tagName.length - 1);
|
|
15089
|
+
i--;
|
|
15090
|
+
}
|
|
15091
|
+
if (!validateTagName(tagName)) {
|
|
15092
|
+
let msg;
|
|
15093
|
+
if (tagName.trim().length === 0) {
|
|
15094
|
+
msg = "Invalid space after '<'.";
|
|
15095
|
+
} else {
|
|
15096
|
+
msg = "Tag '" + tagName + "' is an invalid name.";
|
|
15097
|
+
}
|
|
15098
|
+
return getErrorObject("InvalidTag", msg, getLineNumberForPosition(xmlData, i));
|
|
15099
|
+
}
|
|
15100
|
+
const result = readAttributeStr(xmlData, i);
|
|
15101
|
+
if (result === false) {
|
|
15102
|
+
return getErrorObject("InvalidAttr", "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i));
|
|
15103
|
+
}
|
|
15104
|
+
let attrStr = result.value;
|
|
15105
|
+
i = result.index;
|
|
15106
|
+
if (attrStr[attrStr.length - 1] === "/") {
|
|
15107
|
+
const attrStrStart = i - attrStr.length;
|
|
15108
|
+
attrStr = attrStr.substring(0, attrStr.length - 1);
|
|
15109
|
+
const isValid = validateAttributeString(attrStr, options);
|
|
15110
|
+
if (isValid === true) {
|
|
15111
|
+
tagFound = true;
|
|
15112
|
+
} else {
|
|
15113
|
+
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
|
|
15114
|
+
}
|
|
15115
|
+
} else if (closingTag) {
|
|
15116
|
+
if (!result.tagClosed) {
|
|
15117
|
+
return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
|
|
15118
|
+
} else if (attrStr.trim().length > 0) {
|
|
15119
|
+
return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
15120
|
+
} else if (tags.length === 0) {
|
|
15121
|
+
return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
15122
|
+
} else {
|
|
15123
|
+
const otg = tags.pop();
|
|
15124
|
+
if (tagName !== otg.tagName) {
|
|
15125
|
+
let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
|
|
15126
|
+
return getErrorObject(
|
|
15127
|
+
"InvalidTag",
|
|
15128
|
+
"Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ", col " + openPos.col + ") instead of closing tag '" + tagName + "'.",
|
|
15129
|
+
getLineNumberForPosition(xmlData, tagStartPos)
|
|
15130
|
+
);
|
|
15131
|
+
}
|
|
15132
|
+
if (tags.length == 0) {
|
|
15133
|
+
reachedRoot = true;
|
|
15134
|
+
}
|
|
15135
|
+
}
|
|
15136
|
+
} else {
|
|
15137
|
+
const isValid = validateAttributeString(attrStr, options);
|
|
15138
|
+
if (isValid !== true) {
|
|
15139
|
+
return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
|
|
15140
|
+
}
|
|
15141
|
+
if (reachedRoot === true) {
|
|
15142
|
+
return getErrorObject("InvalidXml", "Multiple possible root nodes found.", getLineNumberForPosition(xmlData, i));
|
|
15143
|
+
} else if (options.unpairedTags.indexOf(tagName) !== -1) {
|
|
15144
|
+
} else {
|
|
15145
|
+
tags.push({ tagName, tagStartPos });
|
|
15146
|
+
}
|
|
15147
|
+
tagFound = true;
|
|
15148
|
+
}
|
|
15149
|
+
for (i++; i < xmlData.length; i++) {
|
|
15150
|
+
if (xmlData[i] === "<") {
|
|
15151
|
+
if (xmlData[i + 1] === "!") {
|
|
15152
|
+
i++;
|
|
15153
|
+
i = readCommentAndCDATA(xmlData, i);
|
|
15154
|
+
continue;
|
|
15155
|
+
} else if (xmlData[i + 1] === "?") {
|
|
15156
|
+
i = readPI(xmlData, ++i);
|
|
15157
|
+
if (i.err) return i;
|
|
15158
|
+
} else {
|
|
15159
|
+
break;
|
|
15160
|
+
}
|
|
15161
|
+
} else if (xmlData[i] === "&") {
|
|
15162
|
+
const afterAmp = validateAmpersand(xmlData, i);
|
|
15163
|
+
if (afterAmp == -1)
|
|
15164
|
+
return getErrorObject("InvalidChar", "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
15165
|
+
i = afterAmp;
|
|
15166
|
+
} else {
|
|
15167
|
+
if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
|
|
15168
|
+
return getErrorObject("InvalidXml", "Extra text at the end", getLineNumberForPosition(xmlData, i));
|
|
15169
|
+
}
|
|
15170
|
+
}
|
|
15171
|
+
}
|
|
15172
|
+
if (xmlData[i] === "<") {
|
|
15173
|
+
i--;
|
|
15174
|
+
}
|
|
15175
|
+
}
|
|
15176
|
+
} else {
|
|
15177
|
+
if (isWhiteSpace(xmlData[i])) {
|
|
15178
|
+
continue;
|
|
15179
|
+
}
|
|
15180
|
+
return getErrorObject("InvalidChar", "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i));
|
|
15181
|
+
}
|
|
15182
|
+
}
|
|
15183
|
+
if (!tagFound) {
|
|
15184
|
+
return getErrorObject("InvalidXml", "Start tag expected.", 1);
|
|
15185
|
+
} else if (tags.length == 1) {
|
|
15186
|
+
return getErrorObject("InvalidTag", "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
|
|
15187
|
+
} else if (tags.length > 0) {
|
|
15188
|
+
return getErrorObject("InvalidXml", "Invalid '" + JSON.stringify(tags.map((t) => t.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 });
|
|
15189
|
+
}
|
|
15190
|
+
return true;
|
|
15191
|
+
}
|
|
15192
|
+
function isWhiteSpace(char) {
|
|
15193
|
+
return char === " " || char === " " || char === "\n" || char === "\r";
|
|
15194
|
+
}
|
|
15195
|
+
function readPI(xmlData, i) {
|
|
15196
|
+
const start = i;
|
|
15197
|
+
for (; i < xmlData.length; i++) {
|
|
15198
|
+
if (xmlData[i] == "?" || xmlData[i] == " ") {
|
|
15199
|
+
const tagname = xmlData.substr(start, i - start);
|
|
15200
|
+
if (i > 5 && tagname === "xml") {
|
|
15201
|
+
return getErrorObject("InvalidXml", "XML declaration allowed only at the start of the document.", getLineNumberForPosition(xmlData, i));
|
|
15202
|
+
} else if (xmlData[i] == "?" && xmlData[i + 1] == ">") {
|
|
15203
|
+
i++;
|
|
15204
|
+
break;
|
|
15205
|
+
} else {
|
|
15206
|
+
continue;
|
|
15207
|
+
}
|
|
15208
|
+
}
|
|
15209
|
+
}
|
|
15210
|
+
return i;
|
|
15211
|
+
}
|
|
15212
|
+
function readCommentAndCDATA(xmlData, i) {
|
|
15213
|
+
if (xmlData.length > i + 5 && xmlData[i + 1] === "-" && xmlData[i + 2] === "-") {
|
|
15214
|
+
for (i += 3; i < xmlData.length; i++) {
|
|
15215
|
+
if (xmlData[i] === "-" && xmlData[i + 1] === "-" && xmlData[i + 2] === ">") {
|
|
15216
|
+
i += 2;
|
|
15217
|
+
break;
|
|
15218
|
+
}
|
|
15219
|
+
}
|
|
15220
|
+
} else if (xmlData.length > i + 8 && xmlData[i + 1] === "D" && xmlData[i + 2] === "O" && xmlData[i + 3] === "C" && xmlData[i + 4] === "T" && xmlData[i + 5] === "Y" && xmlData[i + 6] === "P" && xmlData[i + 7] === "E") {
|
|
15221
|
+
let angleBracketsCount = 1;
|
|
15222
|
+
for (i += 8; i < xmlData.length; i++) {
|
|
15223
|
+
if (xmlData[i] === "<") {
|
|
15224
|
+
angleBracketsCount++;
|
|
15225
|
+
} else if (xmlData[i] === ">") {
|
|
15226
|
+
angleBracketsCount--;
|
|
15227
|
+
if (angleBracketsCount === 0) {
|
|
15228
|
+
break;
|
|
15229
|
+
}
|
|
15230
|
+
}
|
|
15231
|
+
}
|
|
15232
|
+
} else if (xmlData.length > i + 9 && xmlData[i + 1] === "[" && xmlData[i + 2] === "C" && xmlData[i + 3] === "D" && xmlData[i + 4] === "A" && xmlData[i + 5] === "T" && xmlData[i + 6] === "A" && xmlData[i + 7] === "[") {
|
|
15233
|
+
for (i += 8; i < xmlData.length; i++) {
|
|
15234
|
+
if (xmlData[i] === "]" && xmlData[i + 1] === "]" && xmlData[i + 2] === ">") {
|
|
15235
|
+
i += 2;
|
|
15236
|
+
break;
|
|
15237
|
+
}
|
|
15238
|
+
}
|
|
15239
|
+
}
|
|
15240
|
+
return i;
|
|
15241
|
+
}
|
|
15242
|
+
var doubleQuote = '"';
|
|
15243
|
+
var singleQuote = "'";
|
|
15244
|
+
function readAttributeStr(xmlData, i) {
|
|
15245
|
+
let attrStr = "";
|
|
15246
|
+
let startChar = "";
|
|
15247
|
+
let tagClosed = false;
|
|
15248
|
+
for (; i < xmlData.length; i++) {
|
|
15249
|
+
if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
|
|
15250
|
+
if (startChar === "") {
|
|
15251
|
+
startChar = xmlData[i];
|
|
15252
|
+
} else if (startChar !== xmlData[i]) {
|
|
15253
|
+
} else {
|
|
15254
|
+
startChar = "";
|
|
15255
|
+
}
|
|
15256
|
+
} else if (xmlData[i] === ">") {
|
|
15257
|
+
if (startChar === "") {
|
|
15258
|
+
tagClosed = true;
|
|
15259
|
+
break;
|
|
15260
|
+
}
|
|
15261
|
+
}
|
|
15262
|
+
attrStr += xmlData[i];
|
|
15263
|
+
}
|
|
15264
|
+
if (startChar !== "") {
|
|
15265
|
+
return false;
|
|
15266
|
+
}
|
|
15267
|
+
return {
|
|
15268
|
+
value: attrStr,
|
|
15269
|
+
index: i,
|
|
15270
|
+
tagClosed
|
|
15271
|
+
};
|
|
15272
|
+
}
|
|
15273
|
+
var validAttrStrRegxp = new RegExp(`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['"])(([\\s\\S])*?)\\5)?`, "g");
|
|
15274
|
+
function validateAttributeString(attrStr, options) {
|
|
15275
|
+
const matches = getAllMatches(attrStr, validAttrStrRegxp);
|
|
15276
|
+
const attrNames = {};
|
|
15277
|
+
for (let i = 0; i < matches.length; i++) {
|
|
15278
|
+
if (matches[i][1].length === 0) {
|
|
15279
|
+
return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i]));
|
|
15280
|
+
} else if (matches[i][3] !== void 0 && matches[i][4] === void 0) {
|
|
15281
|
+
return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i]));
|
|
15282
|
+
} else if (matches[i][3] === void 0 && !options.allowBooleanAttributes) {
|
|
15283
|
+
return getErrorObject("InvalidAttr", "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i]));
|
|
15284
|
+
}
|
|
15285
|
+
const attrName = matches[i][2];
|
|
15286
|
+
if (!validateAttrName(attrName)) {
|
|
15287
|
+
return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i]));
|
|
15288
|
+
}
|
|
15289
|
+
if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {
|
|
15290
|
+
attrNames[attrName] = 1;
|
|
15291
|
+
} else {
|
|
15292
|
+
return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i]));
|
|
15293
|
+
}
|
|
15294
|
+
}
|
|
15295
|
+
return true;
|
|
15296
|
+
}
|
|
15297
|
+
function validateNumberAmpersand(xmlData, i) {
|
|
15298
|
+
let re = /\d/;
|
|
15299
|
+
if (xmlData[i] === "x") {
|
|
15300
|
+
i++;
|
|
15301
|
+
re = /[\da-fA-F]/;
|
|
15302
|
+
}
|
|
15303
|
+
for (; i < xmlData.length; i++) {
|
|
15304
|
+
if (xmlData[i] === ";")
|
|
15305
|
+
return i;
|
|
15306
|
+
if (!xmlData[i].match(re))
|
|
15307
|
+
break;
|
|
15308
|
+
}
|
|
15309
|
+
return -1;
|
|
15310
|
+
}
|
|
15311
|
+
function validateAmpersand(xmlData, i) {
|
|
15312
|
+
i++;
|
|
15313
|
+
if (xmlData[i] === ";")
|
|
15314
|
+
return -1;
|
|
15315
|
+
if (xmlData[i] === "#") {
|
|
15316
|
+
i++;
|
|
15317
|
+
return validateNumberAmpersand(xmlData, i);
|
|
15318
|
+
}
|
|
15319
|
+
let count = 0;
|
|
15320
|
+
for (; i < xmlData.length; i++, count++) {
|
|
15321
|
+
if (xmlData[i].match(/\w/) && count < 20)
|
|
15322
|
+
continue;
|
|
15323
|
+
if (xmlData[i] === ";")
|
|
15324
|
+
break;
|
|
15325
|
+
return -1;
|
|
15326
|
+
}
|
|
15327
|
+
return i;
|
|
15328
|
+
}
|
|
15329
|
+
function getErrorObject(code, message, lineNumber) {
|
|
15330
|
+
return {
|
|
15331
|
+
err: {
|
|
15332
|
+
code,
|
|
15333
|
+
msg: message,
|
|
15334
|
+
line: lineNumber.line || lineNumber,
|
|
15335
|
+
col: lineNumber.col
|
|
15336
|
+
}
|
|
15337
|
+
};
|
|
15338
|
+
}
|
|
15339
|
+
function validateAttrName(attrName) {
|
|
15340
|
+
return isName(attrName);
|
|
15341
|
+
}
|
|
15342
|
+
function validateTagName(tagname) {
|
|
15343
|
+
return isName(tagname);
|
|
15344
|
+
}
|
|
15345
|
+
function getLineNumberForPosition(xmlData, index) {
|
|
15346
|
+
const lines = xmlData.substring(0, index).split(/\r?\n/);
|
|
15347
|
+
return {
|
|
15348
|
+
line: lines.length,
|
|
15349
|
+
// column number is last line's length + 1, because column numbering starts at 1:
|
|
15350
|
+
col: lines[lines.length - 1].length + 1
|
|
15351
|
+
};
|
|
15352
|
+
}
|
|
15353
|
+
function getPositionFromMatch(match) {
|
|
15354
|
+
return match.startIndex + match[1].length;
|
|
15355
|
+
}
|
|
15356
|
+
|
|
15357
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js
|
|
15358
|
+
var defaultOptions2 = {
|
|
15359
|
+
preserveOrder: false,
|
|
15360
|
+
attributeNamePrefix: "@_",
|
|
15361
|
+
attributesGroupName: false,
|
|
15362
|
+
textNodeName: "#text",
|
|
15363
|
+
ignoreAttributes: true,
|
|
15364
|
+
removeNSPrefix: false,
|
|
15365
|
+
// remove NS from tag name or attribute name if true
|
|
15366
|
+
allowBooleanAttributes: false,
|
|
15367
|
+
//a tag can have attributes without any value
|
|
15368
|
+
//ignoreRootElement : false,
|
|
15369
|
+
parseTagValue: true,
|
|
15370
|
+
parseAttributeValue: false,
|
|
15371
|
+
trimValues: true,
|
|
15372
|
+
//Trim string values of tag and attributes
|
|
15373
|
+
cdataPropName: false,
|
|
15374
|
+
numberParseOptions: {
|
|
15375
|
+
hex: true,
|
|
15376
|
+
leadingZeros: true,
|
|
15377
|
+
eNotation: true
|
|
15378
|
+
},
|
|
15379
|
+
tagValueProcessor: function(tagName, val) {
|
|
15380
|
+
return val;
|
|
15381
|
+
},
|
|
15382
|
+
attributeValueProcessor: function(attrName, val) {
|
|
15383
|
+
return val;
|
|
15384
|
+
},
|
|
15385
|
+
stopNodes: [],
|
|
15386
|
+
//nested tags will not be parsed even for errors
|
|
15387
|
+
alwaysCreateTextNode: false,
|
|
15388
|
+
isArray: () => false,
|
|
15389
|
+
commentPropName: false,
|
|
15390
|
+
unpairedTags: [],
|
|
15391
|
+
processEntities: true,
|
|
15392
|
+
htmlEntities: false,
|
|
15393
|
+
ignoreDeclaration: false,
|
|
15394
|
+
ignorePiTags: false,
|
|
15395
|
+
transformTagName: false,
|
|
15396
|
+
transformAttributeName: false,
|
|
15397
|
+
updateTag: function(tagName, jPath, attrs) {
|
|
15398
|
+
return tagName;
|
|
15399
|
+
},
|
|
15400
|
+
// skipEmptyListItem: false
|
|
15401
|
+
captureMetaData: false,
|
|
15402
|
+
maxNestedTags: 100
|
|
15403
|
+
};
|
|
15404
|
+
function normalizeProcessEntities(value) {
|
|
15405
|
+
if (typeof value === "boolean") {
|
|
15406
|
+
return {
|
|
15407
|
+
enabled: value,
|
|
15408
|
+
// true or false
|
|
15409
|
+
maxEntitySize: 1e4,
|
|
15410
|
+
maxExpansionDepth: 10,
|
|
15411
|
+
maxTotalExpansions: 1e3,
|
|
15412
|
+
maxExpandedLength: 1e5,
|
|
15413
|
+
allowedTags: null,
|
|
15414
|
+
tagFilter: null
|
|
15415
|
+
};
|
|
15416
|
+
}
|
|
15417
|
+
if (typeof value === "object" && value !== null) {
|
|
15418
|
+
return {
|
|
15419
|
+
enabled: value.enabled !== false,
|
|
15420
|
+
// default true if not specified
|
|
15421
|
+
maxEntitySize: value.maxEntitySize ?? 1e4,
|
|
15422
|
+
maxExpansionDepth: value.maxExpansionDepth ?? 10,
|
|
15423
|
+
maxTotalExpansions: value.maxTotalExpansions ?? 1e3,
|
|
15424
|
+
maxExpandedLength: value.maxExpandedLength ?? 1e5,
|
|
15425
|
+
allowedTags: value.allowedTags ?? null,
|
|
15426
|
+
tagFilter: value.tagFilter ?? null
|
|
15427
|
+
};
|
|
15428
|
+
}
|
|
15429
|
+
return normalizeProcessEntities(true);
|
|
15430
|
+
}
|
|
15431
|
+
var buildOptions = function(options) {
|
|
15432
|
+
const built = Object.assign({}, defaultOptions2, options);
|
|
15433
|
+
built.processEntities = normalizeProcessEntities(built.processEntities);
|
|
15434
|
+
return built;
|
|
15435
|
+
};
|
|
15436
|
+
|
|
15437
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js
|
|
15438
|
+
var METADATA_SYMBOL;
|
|
15439
|
+
if (typeof Symbol !== "function") {
|
|
15440
|
+
METADATA_SYMBOL = "@@xmlMetadata";
|
|
15441
|
+
} else {
|
|
15442
|
+
METADATA_SYMBOL = /* @__PURE__ */ Symbol("XML Node Metadata");
|
|
15443
|
+
}
|
|
15444
|
+
var XmlNode = class {
|
|
15445
|
+
constructor(tagname) {
|
|
15446
|
+
this.tagname = tagname;
|
|
15447
|
+
this.child = [];
|
|
15448
|
+
this[":@"] = /* @__PURE__ */ Object.create(null);
|
|
15449
|
+
}
|
|
15450
|
+
add(key, val) {
|
|
15451
|
+
if (key === "__proto__") key = "#__proto__";
|
|
15452
|
+
this.child.push({ [key]: val });
|
|
15453
|
+
}
|
|
15454
|
+
addChild(node, startIndex) {
|
|
15455
|
+
if (node.tagname === "__proto__") node.tagname = "#__proto__";
|
|
15456
|
+
if (node[":@"] && Object.keys(node[":@"]).length > 0) {
|
|
15457
|
+
this.child.push({ [node.tagname]: node.child, [":@"]: node[":@"] });
|
|
15458
|
+
} else {
|
|
15459
|
+
this.child.push({ [node.tagname]: node.child });
|
|
15460
|
+
}
|
|
15461
|
+
if (startIndex !== void 0) {
|
|
15462
|
+
this.child[this.child.length - 1][METADATA_SYMBOL] = { startIndex };
|
|
15463
|
+
}
|
|
15464
|
+
}
|
|
15465
|
+
/** symbol used for metadata */
|
|
15466
|
+
static getMetaDataSymbol() {
|
|
15467
|
+
return METADATA_SYMBOL;
|
|
15468
|
+
}
|
|
15469
|
+
};
|
|
15470
|
+
|
|
15471
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js
|
|
15472
|
+
var DocTypeReader = class {
|
|
15473
|
+
constructor(options) {
|
|
15474
|
+
this.suppressValidationErr = !options;
|
|
15475
|
+
this.options = options;
|
|
15476
|
+
}
|
|
15477
|
+
readDocType(xmlData, i) {
|
|
15478
|
+
const entities = /* @__PURE__ */ Object.create(null);
|
|
15479
|
+
if (xmlData[i + 3] === "O" && xmlData[i + 4] === "C" && xmlData[i + 5] === "T" && xmlData[i + 6] === "Y" && xmlData[i + 7] === "P" && xmlData[i + 8] === "E") {
|
|
15480
|
+
i = i + 9;
|
|
15481
|
+
let angleBracketsCount = 1;
|
|
15482
|
+
let hasBody = false, comment = false;
|
|
15483
|
+
let exp = "";
|
|
15484
|
+
for (; i < xmlData.length; i++) {
|
|
15485
|
+
if (xmlData[i] === "<" && !comment) {
|
|
15486
|
+
if (hasBody && hasSeq(xmlData, "!ENTITY", i)) {
|
|
15487
|
+
i += 7;
|
|
15488
|
+
let entityName, val;
|
|
15489
|
+
[entityName, val, i] = this.readEntityExp(xmlData, i + 1, this.suppressValidationErr);
|
|
15490
|
+
if (val.indexOf("&") === -1) {
|
|
15491
|
+
const escaped = entityName.replace(/[.\-+*:]/g, "\\.");
|
|
15492
|
+
entities[entityName] = {
|
|
15493
|
+
regx: RegExp(`&${escaped};`, "g"),
|
|
15494
|
+
val
|
|
15495
|
+
};
|
|
15496
|
+
}
|
|
15497
|
+
} else if (hasBody && hasSeq(xmlData, "!ELEMENT", i)) {
|
|
15498
|
+
i += 8;
|
|
15499
|
+
const { index } = this.readElementExp(xmlData, i + 1);
|
|
15500
|
+
i = index;
|
|
15501
|
+
} else if (hasBody && hasSeq(xmlData, "!ATTLIST", i)) {
|
|
15502
|
+
i += 8;
|
|
15503
|
+
} else if (hasBody && hasSeq(xmlData, "!NOTATION", i)) {
|
|
15504
|
+
i += 9;
|
|
15505
|
+
const { index } = this.readNotationExp(xmlData, i + 1, this.suppressValidationErr);
|
|
15506
|
+
i = index;
|
|
15507
|
+
} else if (hasSeq(xmlData, "!--", i)) comment = true;
|
|
15508
|
+
else throw new Error(`Invalid DOCTYPE`);
|
|
15509
|
+
angleBracketsCount++;
|
|
15510
|
+
exp = "";
|
|
15511
|
+
} else if (xmlData[i] === ">") {
|
|
15512
|
+
if (comment) {
|
|
15513
|
+
if (xmlData[i - 1] === "-" && xmlData[i - 2] === "-") {
|
|
15514
|
+
comment = false;
|
|
15515
|
+
angleBracketsCount--;
|
|
15516
|
+
}
|
|
15517
|
+
} else {
|
|
15518
|
+
angleBracketsCount--;
|
|
15519
|
+
}
|
|
15520
|
+
if (angleBracketsCount === 0) {
|
|
15521
|
+
break;
|
|
15522
|
+
}
|
|
15523
|
+
} else if (xmlData[i] === "[") {
|
|
15524
|
+
hasBody = true;
|
|
15525
|
+
} else {
|
|
15526
|
+
exp += xmlData[i];
|
|
15527
|
+
}
|
|
15528
|
+
}
|
|
15529
|
+
if (angleBracketsCount !== 0) {
|
|
15530
|
+
throw new Error(`Unclosed DOCTYPE`);
|
|
15531
|
+
}
|
|
15532
|
+
} else {
|
|
15533
|
+
throw new Error(`Invalid Tag instead of DOCTYPE`);
|
|
15534
|
+
}
|
|
15535
|
+
return { entities, i };
|
|
15536
|
+
}
|
|
15537
|
+
readEntityExp(xmlData, i) {
|
|
15538
|
+
i = skipWhitespace(xmlData, i);
|
|
15539
|
+
let entityName = "";
|
|
15540
|
+
while (i < xmlData.length && !/\s/.test(xmlData[i]) && xmlData[i] !== '"' && xmlData[i] !== "'") {
|
|
15541
|
+
entityName += xmlData[i];
|
|
15542
|
+
i++;
|
|
15543
|
+
}
|
|
15544
|
+
validateEntityName(entityName);
|
|
15545
|
+
i = skipWhitespace(xmlData, i);
|
|
15546
|
+
if (!this.suppressValidationErr) {
|
|
15547
|
+
if (xmlData.substring(i, i + 6).toUpperCase() === "SYSTEM") {
|
|
15548
|
+
throw new Error("External entities are not supported");
|
|
15549
|
+
} else if (xmlData[i] === "%") {
|
|
15550
|
+
throw new Error("Parameter entities are not supported");
|
|
15551
|
+
}
|
|
15552
|
+
}
|
|
15553
|
+
let entityValue = "";
|
|
15554
|
+
[i, entityValue] = this.readIdentifierVal(xmlData, i, "entity");
|
|
15555
|
+
if (this.options.enabled !== false && this.options.maxEntitySize && entityValue.length > this.options.maxEntitySize) {
|
|
15556
|
+
throw new Error(
|
|
15557
|
+
`Entity "${entityName}" size (${entityValue.length}) exceeds maximum allowed size (${this.options.maxEntitySize})`
|
|
15558
|
+
);
|
|
15559
|
+
}
|
|
15560
|
+
i--;
|
|
15561
|
+
return [entityName, entityValue, i];
|
|
15562
|
+
}
|
|
15563
|
+
readNotationExp(xmlData, i) {
|
|
15564
|
+
i = skipWhitespace(xmlData, i);
|
|
15565
|
+
let notationName = "";
|
|
15566
|
+
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
15567
|
+
notationName += xmlData[i];
|
|
15568
|
+
i++;
|
|
15569
|
+
}
|
|
15570
|
+
!this.suppressValidationErr && validateEntityName(notationName);
|
|
15571
|
+
i = skipWhitespace(xmlData, i);
|
|
15572
|
+
const identifierType = xmlData.substring(i, i + 6).toUpperCase();
|
|
15573
|
+
if (!this.suppressValidationErr && identifierType !== "SYSTEM" && identifierType !== "PUBLIC") {
|
|
15574
|
+
throw new Error(`Expected SYSTEM or PUBLIC, found "${identifierType}"`);
|
|
15575
|
+
}
|
|
15576
|
+
i += identifierType.length;
|
|
15577
|
+
i = skipWhitespace(xmlData, i);
|
|
15578
|
+
let publicIdentifier = null;
|
|
15579
|
+
let systemIdentifier = null;
|
|
15580
|
+
if (identifierType === "PUBLIC") {
|
|
15581
|
+
[i, publicIdentifier] = this.readIdentifierVal(xmlData, i, "publicIdentifier");
|
|
15582
|
+
i = skipWhitespace(xmlData, i);
|
|
15583
|
+
if (xmlData[i] === '"' || xmlData[i] === "'") {
|
|
15584
|
+
[i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier");
|
|
15585
|
+
}
|
|
15586
|
+
} else if (identifierType === "SYSTEM") {
|
|
15587
|
+
[i, systemIdentifier] = this.readIdentifierVal(xmlData, i, "systemIdentifier");
|
|
15588
|
+
if (!this.suppressValidationErr && !systemIdentifier) {
|
|
15589
|
+
throw new Error("Missing mandatory system identifier for SYSTEM notation");
|
|
15590
|
+
}
|
|
15591
|
+
}
|
|
15592
|
+
return { notationName, publicIdentifier, systemIdentifier, index: --i };
|
|
15593
|
+
}
|
|
15594
|
+
readIdentifierVal(xmlData, i, type) {
|
|
15595
|
+
let identifierVal = "";
|
|
15596
|
+
const startChar = xmlData[i];
|
|
15597
|
+
if (startChar !== '"' && startChar !== "'") {
|
|
15598
|
+
throw new Error(`Expected quoted string, found "${startChar}"`);
|
|
15599
|
+
}
|
|
15600
|
+
i++;
|
|
15601
|
+
while (i < xmlData.length && xmlData[i] !== startChar) {
|
|
15602
|
+
identifierVal += xmlData[i];
|
|
15603
|
+
i++;
|
|
15604
|
+
}
|
|
15605
|
+
if (xmlData[i] !== startChar) {
|
|
15606
|
+
throw new Error(`Unterminated ${type} value`);
|
|
15607
|
+
}
|
|
15608
|
+
i++;
|
|
15609
|
+
return [i, identifierVal];
|
|
15610
|
+
}
|
|
15611
|
+
readElementExp(xmlData, i) {
|
|
15612
|
+
i = skipWhitespace(xmlData, i);
|
|
15613
|
+
let elementName = "";
|
|
15614
|
+
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
15615
|
+
elementName += xmlData[i];
|
|
15616
|
+
i++;
|
|
15617
|
+
}
|
|
15618
|
+
if (!this.suppressValidationErr && !isName(elementName)) {
|
|
15619
|
+
throw new Error(`Invalid element name: "${elementName}"`);
|
|
15620
|
+
}
|
|
15621
|
+
i = skipWhitespace(xmlData, i);
|
|
15622
|
+
let contentModel = "";
|
|
15623
|
+
if (xmlData[i] === "E" && hasSeq(xmlData, "MPTY", i)) i += 4;
|
|
15624
|
+
else if (xmlData[i] === "A" && hasSeq(xmlData, "NY", i)) i += 2;
|
|
15625
|
+
else if (xmlData[i] === "(") {
|
|
15626
|
+
i++;
|
|
15627
|
+
while (i < xmlData.length && xmlData[i] !== ")") {
|
|
15628
|
+
contentModel += xmlData[i];
|
|
15629
|
+
i++;
|
|
15630
|
+
}
|
|
15631
|
+
if (xmlData[i] !== ")") {
|
|
15632
|
+
throw new Error("Unterminated content model");
|
|
15633
|
+
}
|
|
15634
|
+
} else if (!this.suppressValidationErr) {
|
|
15635
|
+
throw new Error(`Invalid Element Expression, found "${xmlData[i]}"`);
|
|
15636
|
+
}
|
|
15637
|
+
return {
|
|
15638
|
+
elementName,
|
|
15639
|
+
contentModel: contentModel.trim(),
|
|
15640
|
+
index: i
|
|
15641
|
+
};
|
|
15642
|
+
}
|
|
15643
|
+
readAttlistExp(xmlData, i) {
|
|
15644
|
+
i = skipWhitespace(xmlData, i);
|
|
15645
|
+
let elementName = "";
|
|
15646
|
+
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
15647
|
+
elementName += xmlData[i];
|
|
15648
|
+
i++;
|
|
15649
|
+
}
|
|
15650
|
+
validateEntityName(elementName);
|
|
15651
|
+
i = skipWhitespace(xmlData, i);
|
|
15652
|
+
let attributeName = "";
|
|
15653
|
+
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
15654
|
+
attributeName += xmlData[i];
|
|
15655
|
+
i++;
|
|
15656
|
+
}
|
|
15657
|
+
if (!validateEntityName(attributeName)) {
|
|
15658
|
+
throw new Error(`Invalid attribute name: "${attributeName}"`);
|
|
15659
|
+
}
|
|
15660
|
+
i = skipWhitespace(xmlData, i);
|
|
15661
|
+
let attributeType = "";
|
|
15662
|
+
if (xmlData.substring(i, i + 8).toUpperCase() === "NOTATION") {
|
|
15663
|
+
attributeType = "NOTATION";
|
|
15664
|
+
i += 8;
|
|
15665
|
+
i = skipWhitespace(xmlData, i);
|
|
15666
|
+
if (xmlData[i] !== "(") {
|
|
15667
|
+
throw new Error(`Expected '(', found "${xmlData[i]}"`);
|
|
15668
|
+
}
|
|
15669
|
+
i++;
|
|
15670
|
+
let allowedNotations = [];
|
|
15671
|
+
while (i < xmlData.length && xmlData[i] !== ")") {
|
|
15672
|
+
let notation = "";
|
|
15673
|
+
while (i < xmlData.length && xmlData[i] !== "|" && xmlData[i] !== ")") {
|
|
15674
|
+
notation += xmlData[i];
|
|
15675
|
+
i++;
|
|
15676
|
+
}
|
|
15677
|
+
notation = notation.trim();
|
|
15678
|
+
if (!validateEntityName(notation)) {
|
|
15679
|
+
throw new Error(`Invalid notation name: "${notation}"`);
|
|
15680
|
+
}
|
|
15681
|
+
allowedNotations.push(notation);
|
|
15682
|
+
if (xmlData[i] === "|") {
|
|
15683
|
+
i++;
|
|
15684
|
+
i = skipWhitespace(xmlData, i);
|
|
15685
|
+
}
|
|
15686
|
+
}
|
|
15687
|
+
if (xmlData[i] !== ")") {
|
|
15688
|
+
throw new Error("Unterminated list of notations");
|
|
15689
|
+
}
|
|
15690
|
+
i++;
|
|
15691
|
+
attributeType += " (" + allowedNotations.join("|") + ")";
|
|
15692
|
+
} else {
|
|
15693
|
+
while (i < xmlData.length && !/\s/.test(xmlData[i])) {
|
|
15694
|
+
attributeType += xmlData[i];
|
|
15695
|
+
i++;
|
|
15696
|
+
}
|
|
15697
|
+
const validTypes = ["CDATA", "ID", "IDREF", "IDREFS", "ENTITY", "ENTITIES", "NMTOKEN", "NMTOKENS"];
|
|
15698
|
+
if (!this.suppressValidationErr && !validTypes.includes(attributeType.toUpperCase())) {
|
|
15699
|
+
throw new Error(`Invalid attribute type: "${attributeType}"`);
|
|
15700
|
+
}
|
|
15701
|
+
}
|
|
15702
|
+
i = skipWhitespace(xmlData, i);
|
|
15703
|
+
let defaultValue = "";
|
|
15704
|
+
if (xmlData.substring(i, i + 8).toUpperCase() === "#REQUIRED") {
|
|
15705
|
+
defaultValue = "#REQUIRED";
|
|
15706
|
+
i += 8;
|
|
15707
|
+
} else if (xmlData.substring(i, i + 7).toUpperCase() === "#IMPLIED") {
|
|
15708
|
+
defaultValue = "#IMPLIED";
|
|
15709
|
+
i += 7;
|
|
15710
|
+
} else {
|
|
15711
|
+
[i, defaultValue] = this.readIdentifierVal(xmlData, i, "ATTLIST");
|
|
15712
|
+
}
|
|
15713
|
+
return {
|
|
15714
|
+
elementName,
|
|
15715
|
+
attributeName,
|
|
15716
|
+
attributeType,
|
|
15717
|
+
defaultValue,
|
|
15718
|
+
index: i
|
|
15719
|
+
};
|
|
15720
|
+
}
|
|
15721
|
+
};
|
|
15722
|
+
var skipWhitespace = (data, index) => {
|
|
15723
|
+
while (index < data.length && /\s/.test(data[index])) {
|
|
15724
|
+
index++;
|
|
15725
|
+
}
|
|
15726
|
+
return index;
|
|
15727
|
+
};
|
|
15728
|
+
function hasSeq(data, seq, i) {
|
|
15729
|
+
for (let j = 0; j < seq.length; j++) {
|
|
15730
|
+
if (seq[j] !== data[i + j + 1]) return false;
|
|
15731
|
+
}
|
|
15732
|
+
return true;
|
|
15733
|
+
}
|
|
15734
|
+
function validateEntityName(name) {
|
|
15735
|
+
if (isName(name))
|
|
15736
|
+
return name;
|
|
15737
|
+
else
|
|
15738
|
+
throw new Error(`Invalid entity name ${name}`);
|
|
15739
|
+
}
|
|
15740
|
+
|
|
15741
|
+
// ../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/digitTable.js
|
|
15742
|
+
var SCRIPT_ZEROS = [
|
|
15743
|
+
// Basic Latin (ASCII) — included for completeness / pass-through
|
|
15744
|
+
48,
|
|
15745
|
+
// 0-9
|
|
15746
|
+
// Arabic scripts
|
|
15747
|
+
1632,
|
|
15748
|
+
// Arabic-Indic ٠١٢٣٤٥٦٧٨٩
|
|
15749
|
+
1776,
|
|
15750
|
+
// Extended Arabic-Indic (Urdu/Persian/Sindhi) ۰۱۲۳
|
|
15751
|
+
// Indic scripts
|
|
15752
|
+
2406,
|
|
15753
|
+
// Devanagari ०१२३४५६७८९
|
|
15754
|
+
2534,
|
|
15755
|
+
// Bengali ০১২৩৪৫৬৭৮৯
|
|
15756
|
+
2662,
|
|
15757
|
+
// Gurmukhi ੦੧੨੩੪੫੬੭੮੯
|
|
15758
|
+
2790,
|
|
15759
|
+
// Gujarati ૦૧૨૩૪૫૬૭૮૯
|
|
15760
|
+
2918,
|
|
15761
|
+
// Odia ୦୧୨୩୪୫୬୭୮୯
|
|
15762
|
+
3046,
|
|
15763
|
+
// Tamil ௦௧௨௩௪௫௬௭௮௯
|
|
15764
|
+
3174,
|
|
15765
|
+
// Telugu ౦౧౨౩౪౫౬౭౮౯
|
|
15766
|
+
3302,
|
|
15767
|
+
// Kannada ೦೧೨೩೪೫೬೭೮೯
|
|
15768
|
+
3430,
|
|
15769
|
+
// Malayalam ൦൧൨൩൪൫൬൭൮൯
|
|
15770
|
+
3558,
|
|
15771
|
+
// Sinhala Archaic ෦෧෨෩෪෫෬෭෮෯
|
|
15772
|
+
// Southeast Asian scripts
|
|
15773
|
+
3664,
|
|
15774
|
+
// Thai ๐๑๒๓๔๕๖๗๘๙
|
|
15775
|
+
3792,
|
|
15776
|
+
// Lao ໐໑໒໓໔໕໖໗໘໙
|
|
15777
|
+
3872,
|
|
15778
|
+
// Tibetan ༠༡༢༣༤༥༦༧༨༩
|
|
15779
|
+
4160,
|
|
15780
|
+
// Myanmar ၀၁၂၃၄၅၆၇၈၉
|
|
15781
|
+
4240,
|
|
15782
|
+
// Myanmar Shan ႐႑႒႓႔႕႖႗႘႙
|
|
15783
|
+
6112,
|
|
15784
|
+
// Khmer ០១២៣៤៥៦៧៨៩
|
|
15785
|
+
6160,
|
|
15786
|
+
// Mongolian ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙
|
|
15787
|
+
6470,
|
|
15788
|
+
// Limbu ᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏
|
|
15789
|
+
6608,
|
|
15790
|
+
// New Tai Lue ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙
|
|
15791
|
+
6784,
|
|
15792
|
+
// Tai Tham Hora ᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉
|
|
15793
|
+
6800,
|
|
15794
|
+
// Tai Tham Tham ᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙
|
|
15795
|
+
6992,
|
|
15796
|
+
// Balinese ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙
|
|
15797
|
+
7088,
|
|
15798
|
+
// Sundanese ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹
|
|
15799
|
+
7232,
|
|
15800
|
+
// Lepcha ᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉
|
|
15801
|
+
7248,
|
|
15802
|
+
// Ol Chiki ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙
|
|
15803
|
+
// Fullwidth (CJK context)
|
|
15804
|
+
65296,
|
|
15805
|
+
// Fullwidth 0123456789
|
|
15806
|
+
// Mathematical digit variants (Unicode math block)
|
|
15807
|
+
120782,
|
|
15808
|
+
// Mathematical Bold
|
|
15809
|
+
120792,
|
|
15810
|
+
// Mathematical Double-Struck
|
|
15811
|
+
120802,
|
|
15812
|
+
// Mathematical Sans-Serif
|
|
15813
|
+
120812,
|
|
15814
|
+
// Mathematical Sans-Serif Bold
|
|
15815
|
+
120822,
|
|
15816
|
+
// Mathematical Monospace
|
|
15817
|
+
// Other scripts
|
|
15818
|
+
66720,
|
|
15819
|
+
// Osmanya 𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩
|
|
15820
|
+
68912,
|
|
15821
|
+
// Hanifi Rohingya 𐴰𐴱𐴲𐴳𐴴𐴵𐴶𐴷𐴸𐴹
|
|
15822
|
+
69734,
|
|
15823
|
+
// Brahmi 𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯
|
|
15824
|
+
69872,
|
|
15825
|
+
// Sora Sompeng 𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹
|
|
15826
|
+
69942,
|
|
15827
|
+
// Chakma 𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿
|
|
15828
|
+
70096,
|
|
15829
|
+
// Sharada 𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙
|
|
15830
|
+
70384,
|
|
15831
|
+
// Khudawadi 𑋰𑋱𑋲𑋳𑋴𑋵𑋶𑋷𑋸𑋹
|
|
15832
|
+
70736,
|
|
15833
|
+
// Newa 𑑐𑑑𑑒𑑓𑑔𑑕𑑖𑑗𑑘𑑙
|
|
15834
|
+
70864,
|
|
15835
|
+
// Tirhuta 𑓐𑓑𑓒𑓓𑓔𑓕𑓖𑓗𑓘𑓙
|
|
15836
|
+
71248,
|
|
15837
|
+
// Modi 𑙐𑙑𑙒𑙓𑙔𑙕𑙖𑙗𑙘𑙙
|
|
15838
|
+
71360,
|
|
15839
|
+
// Takri 𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉
|
|
15840
|
+
71472,
|
|
15841
|
+
// Ahom 𑜰𑜱𑜲𑜳𑜴𑜵𑜶𑜷𑜸𑜹
|
|
15842
|
+
71904,
|
|
15843
|
+
// Warang Citi 𑣠𑣡𑣢𑣣𑣤𑣥𑣦𑣧𑣨𑣩
|
|
15844
|
+
72016,
|
|
15845
|
+
// Dives Akuru 𑥐𑥑𑥒𑥓𑥔𑥕𑥖𑥗𑥘𑥙
|
|
15846
|
+
72688,
|
|
15847
|
+
// Khitan Small Script
|
|
15848
|
+
72784,
|
|
15849
|
+
// Bhaiksuki 𑱐𑱑𑱒𑱓𑱔𑱕𑱖𑱗𑱘𑱙
|
|
15850
|
+
73040,
|
|
15851
|
+
// Masaram Gondi 𑵐𑵑𑵒𑵓𑵔𑵕𑵖𑵗𑵘𑵙
|
|
15852
|
+
73120,
|
|
15853
|
+
// Gunjala Gondi 𑶠𑶡𑶢𑶣𑶤𑶥𑶦𑶧𑶨𑶩
|
|
15854
|
+
73552,
|
|
15855
|
+
// Kawi 𑽐𑽑𑽒𑽓𑽔𑽕𑽖𑽗𑽘𑽙
|
|
15856
|
+
92768,
|
|
15857
|
+
// Mro 𖩠𖩡𖩢𖩣𖩤𖩥𖩦𖩧𖩨𖩩
|
|
15858
|
+
92864,
|
|
15859
|
+
// Tangsa 𖫀𖫁𖫂𖫃𖫄𖫅𖫆𖫇𖫈𖫉
|
|
15860
|
+
93008,
|
|
15861
|
+
// Pahawh Hmong 𖭐𖭑𖭒𖭓𖭔𖭕𖭖𖭗𖭘𖭙
|
|
15862
|
+
123200,
|
|
15863
|
+
// Nyiakeng Puachue Hmong 𞅀𞅁𞅂𞅃𞅄𞅅𞅆𞅇𞅈𞅉
|
|
15864
|
+
123632,
|
|
15865
|
+
// Wancho 𞋰𞋱𞋲𞋳𞋴𞋵𞋶𞋷𞋸𞋹
|
|
15866
|
+
124144,
|
|
15867
|
+
// Nag Mundari 𞓰𞓱𞓲𞓳𞓴𞓵𞓶𞓷𞓸𞓹
|
|
15868
|
+
125264,
|
|
15869
|
+
// Adlam 𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙
|
|
15870
|
+
130032
|
|
15871
|
+
// Segmented digit symbols 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹
|
|
15872
|
+
];
|
|
15873
|
+
var NOT_DIGIT = 255;
|
|
15874
|
+
var HIGH_MAP = /* @__PURE__ */ new Map();
|
|
15875
|
+
var LOW_MAX = 65535;
|
|
15876
|
+
var LOW_MIN = 1632;
|
|
15877
|
+
var TABLE_OFFSET = LOW_MIN;
|
|
15878
|
+
var TABLE_SIZE = LOW_MAX - LOW_MIN + 1;
|
|
15879
|
+
var TABLE = new Uint8Array(TABLE_SIZE).fill(NOT_DIGIT);
|
|
15880
|
+
for (const zero of SCRIPT_ZEROS) {
|
|
15881
|
+
for (let d = 0; d < 10; d++) {
|
|
15882
|
+
const cp = zero + d;
|
|
15883
|
+
if (cp <= LOW_MAX) {
|
|
15884
|
+
TABLE[cp - TABLE_OFFSET] = d;
|
|
15885
|
+
} else {
|
|
15886
|
+
HIGH_MAP.set(cp, d);
|
|
15887
|
+
}
|
|
15888
|
+
}
|
|
15889
|
+
}
|
|
15890
|
+
|
|
15891
|
+
// ../../node_modules/.pnpm/anynum@1.0.1/node_modules/anynum/anynum.js
|
|
15892
|
+
var CHAR_0 = 48;
|
|
15893
|
+
var CHAR_9 = 57;
|
|
15894
|
+
var CHAR_MINUS = 45;
|
|
15895
|
+
var MINUS_SET = /* @__PURE__ */ new Set([8722, 65293, 65123]);
|
|
15896
|
+
function anynum(str2) {
|
|
15897
|
+
if (typeof str2 !== "string") return str2;
|
|
15898
|
+
const len = str2.length;
|
|
15899
|
+
if (len === 0) return str2;
|
|
15900
|
+
let firstHit = -1;
|
|
15901
|
+
for (let i = 0; i < len; i++) {
|
|
15902
|
+
const cc = str2.charCodeAt(i);
|
|
15903
|
+
if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) continue;
|
|
15904
|
+
if (cc < TABLE_OFFSET) {
|
|
15905
|
+
if (MINUS_SET.has(cc)) {
|
|
15906
|
+
firstHit = i;
|
|
15907
|
+
break;
|
|
15908
|
+
}
|
|
15909
|
+
continue;
|
|
15910
|
+
}
|
|
15911
|
+
if (cc >= 55296 && cc <= 56319) {
|
|
15912
|
+
if (i + 1 < len) {
|
|
15913
|
+
const low = str2.charCodeAt(i + 1);
|
|
15914
|
+
if (low >= 56320 && low <= 57343) {
|
|
15915
|
+
const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
|
|
15916
|
+
if (HIGH_MAP.has(cp)) {
|
|
15917
|
+
firstHit = i;
|
|
15918
|
+
break;
|
|
15919
|
+
}
|
|
15920
|
+
}
|
|
15921
|
+
}
|
|
15922
|
+
continue;
|
|
15923
|
+
}
|
|
15924
|
+
if (TABLE[cc - TABLE_OFFSET] !== NOT_DIGIT || MINUS_SET.has(cc)) {
|
|
15925
|
+
firstHit = i;
|
|
15926
|
+
break;
|
|
15927
|
+
}
|
|
15928
|
+
}
|
|
15929
|
+
if (firstHit === -1) return str2;
|
|
15930
|
+
const chars = [];
|
|
15931
|
+
if (firstHit > 0) chars.push(str2.slice(0, firstHit));
|
|
15932
|
+
for (let i = firstHit; i < len; i++) {
|
|
15933
|
+
const cc = str2.charCodeAt(i);
|
|
15934
|
+
if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) {
|
|
15935
|
+
chars.push(str2[i]);
|
|
15936
|
+
continue;
|
|
15937
|
+
}
|
|
15938
|
+
if (cc < TABLE_OFFSET) {
|
|
15939
|
+
chars.push(MINUS_SET.has(cc) ? "-" : str2[i]);
|
|
15940
|
+
continue;
|
|
15941
|
+
}
|
|
15942
|
+
if (cc >= 55296 && cc <= 56319) {
|
|
15943
|
+
if (i + 1 < len) {
|
|
15944
|
+
const low = str2.charCodeAt(i + 1);
|
|
15945
|
+
if (low >= 56320 && low <= 57343) {
|
|
15946
|
+
const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
|
|
15947
|
+
const d2 = HIGH_MAP.get(cp);
|
|
15948
|
+
if (d2 !== void 0) {
|
|
15949
|
+
chars.push(String.fromCharCode(d2 + 48));
|
|
15950
|
+
i++;
|
|
15951
|
+
continue;
|
|
15952
|
+
}
|
|
15953
|
+
}
|
|
15954
|
+
}
|
|
15955
|
+
chars.push(str2[i]);
|
|
15956
|
+
continue;
|
|
15957
|
+
}
|
|
15958
|
+
if (MINUS_SET.has(cc)) {
|
|
15959
|
+
chars.push("-");
|
|
15960
|
+
continue;
|
|
15961
|
+
}
|
|
15962
|
+
const d = TABLE[cc - TABLE_OFFSET];
|
|
15963
|
+
chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str2[i]);
|
|
15964
|
+
}
|
|
15965
|
+
return chars.join("");
|
|
15966
|
+
}
|
|
15967
|
+
var anynum_default = anynum;
|
|
15968
|
+
|
|
15969
|
+
// ../../node_modules/.pnpm/strnum@2.4.2/node_modules/strnum/strnum.js
|
|
15970
|
+
var hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;
|
|
15971
|
+
var binRegex = /^0b[01]+$/;
|
|
15972
|
+
var octRegex = /^0o[0-7]+$/;
|
|
15973
|
+
var numRegex = /^([\-\+])?(0*)([0-9]*(\.[0-9]*)?)$/;
|
|
15974
|
+
var consider = {
|
|
15975
|
+
hex: true,
|
|
15976
|
+
binary: false,
|
|
15977
|
+
octal: false,
|
|
15978
|
+
leadingZeros: true,
|
|
15979
|
+
decimalPoint: ".",
|
|
15980
|
+
eNotation: true,
|
|
15981
|
+
//skipLike: /regex/,
|
|
15982
|
+
infinity: "original",
|
|
15983
|
+
// "null", "infinity" (Infinity type), "string" ("Infinity" (the string literal))
|
|
15984
|
+
unicode: false
|
|
15985
|
+
};
|
|
15986
|
+
function toNumber(str2, options = {}) {
|
|
15987
|
+
options = Object.assign({}, consider, options);
|
|
15988
|
+
if (!str2 || typeof str2 !== "string") return str2;
|
|
15989
|
+
let trimmedStr = str2.trim();
|
|
15990
|
+
if (trimmedStr.length === 0) return str2;
|
|
15991
|
+
else if (options.skipLike !== void 0 && options.skipLike.test(trimmedStr)) return str2;
|
|
15992
|
+
else if (trimmedStr === "0") return 0;
|
|
15993
|
+
if (options.unicode) {
|
|
15994
|
+
trimmedStr = anynum_default(trimmedStr);
|
|
15995
|
+
if (trimmedStr === "0") return 0;
|
|
15996
|
+
}
|
|
15997
|
+
if (options.hex && hexRegex.test(trimmedStr)) {
|
|
15998
|
+
return parse_int(trimmedStr, 16);
|
|
15999
|
+
} else if (options.binary && binRegex.test(trimmedStr)) {
|
|
16000
|
+
return parse_int(trimmedStr, 2);
|
|
16001
|
+
} else if (options.octal && octRegex.test(trimmedStr)) {
|
|
16002
|
+
return parse_int(trimmedStr, 8);
|
|
16003
|
+
} else if (!isFinite(trimmedStr)) {
|
|
16004
|
+
return handleInfinity(str2, Number(trimmedStr), options);
|
|
16005
|
+
} else if (trimmedStr.includes("e") || trimmedStr.includes("E")) {
|
|
16006
|
+
return resolveEnotation(str2, trimmedStr, options);
|
|
16007
|
+
} else {
|
|
16008
|
+
const match = numRegex.exec(trimmedStr);
|
|
16009
|
+
if (match) {
|
|
16010
|
+
const sign = match[1] || "";
|
|
16011
|
+
const leadingZeros = match[2];
|
|
16012
|
+
let numTrimmedByZeros = trimZeros(match[3]);
|
|
16013
|
+
const decimalAdjacentToLeadingZeros = sign ? (
|
|
16014
|
+
// 0., -00., 000.
|
|
16015
|
+
str2[leadingZeros.length + 1] === "."
|
|
16016
|
+
) : str2[leadingZeros.length] === ".";
|
|
16017
|
+
if (!options.leadingZeros && (leadingZeros.length > 1 || leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros)) {
|
|
16018
|
+
return str2;
|
|
16019
|
+
} else {
|
|
16020
|
+
const num2 = Number(trimmedStr);
|
|
16021
|
+
const parsedStr = String(num2);
|
|
16022
|
+
if (num2 === 0) return num2;
|
|
16023
|
+
if (parsedStr.search(/[eE]/) !== -1) {
|
|
16024
|
+
if (options.eNotation) return num2;
|
|
16025
|
+
else return str2;
|
|
16026
|
+
} else if (trimmedStr.indexOf(".") !== -1) {
|
|
16027
|
+
if (parsedStr === "0") return num2;
|
|
16028
|
+
else if (parsedStr === numTrimmedByZeros) return num2;
|
|
16029
|
+
else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num2;
|
|
16030
|
+
else return str2;
|
|
16031
|
+
}
|
|
16032
|
+
let n = leadingZeros ? numTrimmedByZeros : trimmedStr;
|
|
16033
|
+
if (leadingZeros) {
|
|
16034
|
+
return n === parsedStr || sign + n === parsedStr ? num2 : str2;
|
|
16035
|
+
} else {
|
|
16036
|
+
return n === parsedStr || n === sign + parsedStr ? num2 : str2;
|
|
16037
|
+
}
|
|
16038
|
+
}
|
|
16039
|
+
} else {
|
|
16040
|
+
return str2;
|
|
16041
|
+
}
|
|
16042
|
+
}
|
|
16043
|
+
}
|
|
16044
|
+
var eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/;
|
|
16045
|
+
function resolveEnotation(str2, trimmedStr, options) {
|
|
16046
|
+
if (!options.eNotation) return str2;
|
|
16047
|
+
const notation = trimmedStr.match(eNotationRegx);
|
|
16048
|
+
if (notation) {
|
|
16049
|
+
let sign = notation[1] || "";
|
|
16050
|
+
const eChar = notation[3].indexOf("e") === -1 ? "E" : "e";
|
|
16051
|
+
const leadingZeros = notation[2];
|
|
16052
|
+
const eAdjacentToLeadingZeros = sign ? (
|
|
16053
|
+
// 0E.
|
|
16054
|
+
str2[leadingZeros.length + 1] === eChar
|
|
16055
|
+
) : str2[leadingZeros.length] === eChar;
|
|
16056
|
+
if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str2;
|
|
16057
|
+
else if (leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {
|
|
16058
|
+
return Number(trimmedStr);
|
|
16059
|
+
} else if (leadingZeros.length > 0) {
|
|
16060
|
+
if (options.leadingZeros && !eAdjacentToLeadingZeros) {
|
|
16061
|
+
trimmedStr = (notation[1] || "") + notation[3];
|
|
16062
|
+
return Number(trimmedStr);
|
|
16063
|
+
} else return str2;
|
|
16064
|
+
} else {
|
|
16065
|
+
return Number(trimmedStr);
|
|
16066
|
+
}
|
|
16067
|
+
} else {
|
|
16068
|
+
return str2;
|
|
16069
|
+
}
|
|
16070
|
+
}
|
|
16071
|
+
function trimZeros(numStr) {
|
|
16072
|
+
if (numStr && numStr.indexOf(".") !== -1) {
|
|
16073
|
+
let end = numStr.length;
|
|
16074
|
+
while (end > 0 && numStr.charCodeAt(end - 1) === 48) end--;
|
|
16075
|
+
numStr = numStr.slice(0, end);
|
|
16076
|
+
if (numStr === ".") numStr = "0";
|
|
16077
|
+
else if (numStr[0] === ".") numStr = "0" + numStr;
|
|
16078
|
+
else if (numStr[numStr.length - 1] === ".") numStr = numStr.substring(0, numStr.length - 1);
|
|
16079
|
+
return numStr;
|
|
16080
|
+
}
|
|
16081
|
+
return numStr;
|
|
16082
|
+
}
|
|
16083
|
+
function parse_int(numStr, base) {
|
|
16084
|
+
const str2 = numStr.trim();
|
|
16085
|
+
if (base === 2 || base === 8) numStr = str2.substring(2);
|
|
16086
|
+
if (parseInt) return parseInt(numStr, base);
|
|
16087
|
+
else if (Number.parseInt) return Number.parseInt(numStr, base);
|
|
16088
|
+
else if (window && window.parseInt) return window.parseInt(numStr, base);
|
|
16089
|
+
else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported");
|
|
16090
|
+
}
|
|
16091
|
+
function handleInfinity(str2, num2, options) {
|
|
16092
|
+
const isPositive = num2 === Infinity;
|
|
16093
|
+
switch (options.infinity.toLowerCase()) {
|
|
16094
|
+
case "null":
|
|
16095
|
+
return null;
|
|
16096
|
+
case "infinity":
|
|
16097
|
+
return num2;
|
|
16098
|
+
// Return Infinity or -Infinity
|
|
16099
|
+
case "string":
|
|
16100
|
+
return isPositive ? "Infinity" : "-Infinity";
|
|
16101
|
+
case "original":
|
|
16102
|
+
default:
|
|
16103
|
+
return str2;
|
|
16104
|
+
}
|
|
16105
|
+
}
|
|
16106
|
+
|
|
16107
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/ignoreAttributes.js
|
|
16108
|
+
function getIgnoreAttributesFn(ignoreAttributes) {
|
|
16109
|
+
if (typeof ignoreAttributes === "function") {
|
|
16110
|
+
return ignoreAttributes;
|
|
16111
|
+
}
|
|
16112
|
+
if (Array.isArray(ignoreAttributes)) {
|
|
16113
|
+
return (attrName) => {
|
|
16114
|
+
for (const pattern of ignoreAttributes) {
|
|
16115
|
+
if (typeof pattern === "string" && attrName === pattern) {
|
|
16116
|
+
return true;
|
|
16117
|
+
}
|
|
16118
|
+
if (pattern instanceof RegExp && pattern.test(attrName)) {
|
|
16119
|
+
return true;
|
|
16120
|
+
}
|
|
16121
|
+
}
|
|
16122
|
+
};
|
|
16123
|
+
}
|
|
16124
|
+
return () => false;
|
|
16125
|
+
}
|
|
16126
|
+
|
|
16127
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js
|
|
16128
|
+
var OrderedObjParser = class {
|
|
16129
|
+
constructor(options) {
|
|
16130
|
+
this.options = options;
|
|
16131
|
+
this.currentNode = null;
|
|
16132
|
+
this.tagsNodeStack = [];
|
|
16133
|
+
this.docTypeEntities = {};
|
|
16134
|
+
this.lastEntities = {
|
|
16135
|
+
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
|
16136
|
+
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
|
16137
|
+
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
|
16138
|
+
"quot": { regex: /&(quot|#34|#x22);/g, val: '"' }
|
|
16139
|
+
};
|
|
16140
|
+
this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
|
16141
|
+
this.htmlEntities = {
|
|
16142
|
+
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
|
16143
|
+
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
|
16144
|
+
// "gt" : { regex: /&(gt|#62);/g, val: ">" },
|
|
16145
|
+
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
|
16146
|
+
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
|
16147
|
+
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
|
16148
|
+
"cent": { regex: /&(cent|#162);/g, val: "\xA2" },
|
|
16149
|
+
"pound": { regex: /&(pound|#163);/g, val: "\xA3" },
|
|
16150
|
+
"yen": { regex: /&(yen|#165);/g, val: "\xA5" },
|
|
16151
|
+
"euro": { regex: /&(euro|#8364);/g, val: "\u20AC" },
|
|
16152
|
+
"copyright": { regex: /&(copy|#169);/g, val: "\xA9" },
|
|
16153
|
+
"reg": { regex: /&(reg|#174);/g, val: "\xAE" },
|
|
16154
|
+
"inr": { regex: /&(inr|#8377);/g, val: "\u20B9" },
|
|
16155
|
+
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str2) => fromCodePoint(str2, 10, "&#") },
|
|
16156
|
+
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str2) => fromCodePoint(str2, 16, "&#x") }
|
|
16157
|
+
};
|
|
16158
|
+
this.addExternalEntities = addExternalEntities;
|
|
16159
|
+
this.parseXml = parseXml;
|
|
16160
|
+
this.parseTextData = parseTextData;
|
|
16161
|
+
this.resolveNameSpace = resolveNameSpace;
|
|
16162
|
+
this.buildAttributesMap = buildAttributesMap;
|
|
16163
|
+
this.isItStopNode = isItStopNode;
|
|
16164
|
+
this.replaceEntitiesValue = replaceEntitiesValue;
|
|
16165
|
+
this.readStopNodeData = readStopNodeData;
|
|
16166
|
+
this.saveTextToParentTag = saveTextToParentTag;
|
|
16167
|
+
this.addChild = addChild;
|
|
16168
|
+
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes);
|
|
16169
|
+
this.entityExpansionCount = 0;
|
|
16170
|
+
this.currentExpandedLength = 0;
|
|
16171
|
+
if (this.options.stopNodes && this.options.stopNodes.length > 0) {
|
|
16172
|
+
this.stopNodesExact = /* @__PURE__ */ new Set();
|
|
16173
|
+
this.stopNodesWildcard = /* @__PURE__ */ new Set();
|
|
16174
|
+
for (let i = 0; i < this.options.stopNodes.length; i++) {
|
|
16175
|
+
const stopNodeExp = this.options.stopNodes[i];
|
|
16176
|
+
if (typeof stopNodeExp !== "string") continue;
|
|
16177
|
+
if (stopNodeExp.startsWith("*.")) {
|
|
16178
|
+
this.stopNodesWildcard.add(stopNodeExp.substring(2));
|
|
16179
|
+
} else {
|
|
16180
|
+
this.stopNodesExact.add(stopNodeExp);
|
|
16181
|
+
}
|
|
16182
|
+
}
|
|
16183
|
+
}
|
|
16184
|
+
}
|
|
16185
|
+
};
|
|
16186
|
+
function addExternalEntities(externalEntities) {
|
|
16187
|
+
const entKeys = Object.keys(externalEntities);
|
|
16188
|
+
for (let i = 0; i < entKeys.length; i++) {
|
|
16189
|
+
const ent = entKeys[i];
|
|
16190
|
+
const escaped = ent.replace(/[.\-+*:]/g, "\\.");
|
|
16191
|
+
this.lastEntities[ent] = {
|
|
16192
|
+
regex: new RegExp("&" + escaped + ";", "g"),
|
|
16193
|
+
val: externalEntities[ent]
|
|
16194
|
+
};
|
|
16195
|
+
}
|
|
16196
|
+
}
|
|
16197
|
+
function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
|
|
16198
|
+
if (val !== void 0) {
|
|
16199
|
+
if (this.options.trimValues && !dontTrim) {
|
|
16200
|
+
val = val.trim();
|
|
16201
|
+
}
|
|
16202
|
+
if (val.length > 0) {
|
|
16203
|
+
if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);
|
|
16204
|
+
const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
|
|
16205
|
+
if (newval === null || newval === void 0) {
|
|
16206
|
+
return val;
|
|
16207
|
+
} else if (typeof newval !== typeof val || newval !== val) {
|
|
16208
|
+
return newval;
|
|
16209
|
+
} else if (this.options.trimValues) {
|
|
16210
|
+
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
|
16211
|
+
} else {
|
|
16212
|
+
const trimmedVal = val.trim();
|
|
16213
|
+
if (trimmedVal === val) {
|
|
16214
|
+
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
|
16215
|
+
} else {
|
|
16216
|
+
return val;
|
|
16217
|
+
}
|
|
16218
|
+
}
|
|
16219
|
+
}
|
|
16220
|
+
}
|
|
16221
|
+
}
|
|
16222
|
+
function resolveNameSpace(tagname) {
|
|
16223
|
+
if (this.options.removeNSPrefix) {
|
|
16224
|
+
const tags = tagname.split(":");
|
|
16225
|
+
const prefix = tagname.charAt(0) === "/" ? "/" : "";
|
|
16226
|
+
if (tags[0] === "xmlns") {
|
|
16227
|
+
return "";
|
|
16228
|
+
}
|
|
16229
|
+
if (tags.length === 2) {
|
|
16230
|
+
tagname = prefix + tags[1];
|
|
16231
|
+
}
|
|
16232
|
+
}
|
|
16233
|
+
return tagname;
|
|
16234
|
+
}
|
|
16235
|
+
var attrsRegx = new RegExp(`([^\\s=]+)\\s*(=\\s*(['"])([\\s\\S]*?)\\3)?`, "gm");
|
|
16236
|
+
function buildAttributesMap(attrStr, jPath, tagName) {
|
|
16237
|
+
if (this.options.ignoreAttributes !== true && typeof attrStr === "string") {
|
|
16238
|
+
const matches = getAllMatches(attrStr, attrsRegx);
|
|
16239
|
+
const len = matches.length;
|
|
16240
|
+
const attrs = {};
|
|
16241
|
+
for (let i = 0; i < len; i++) {
|
|
16242
|
+
const attrName = this.resolveNameSpace(matches[i][1]);
|
|
16243
|
+
if (this.ignoreAttributesFn(attrName, jPath)) {
|
|
16244
|
+
continue;
|
|
16245
|
+
}
|
|
16246
|
+
let oldVal = matches[i][4];
|
|
16247
|
+
let aName = this.options.attributeNamePrefix + attrName;
|
|
16248
|
+
if (attrName.length) {
|
|
16249
|
+
if (this.options.transformAttributeName) {
|
|
16250
|
+
aName = this.options.transformAttributeName(aName);
|
|
16251
|
+
}
|
|
16252
|
+
if (aName === "__proto__") aName = "#__proto__";
|
|
16253
|
+
if (oldVal !== void 0) {
|
|
16254
|
+
if (this.options.trimValues) {
|
|
16255
|
+
oldVal = oldVal.trim();
|
|
16256
|
+
}
|
|
16257
|
+
oldVal = this.replaceEntitiesValue(oldVal, tagName, jPath);
|
|
16258
|
+
const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
|
|
16259
|
+
if (newVal === null || newVal === void 0) {
|
|
16260
|
+
attrs[aName] = oldVal;
|
|
16261
|
+
} else if (typeof newVal !== typeof oldVal || newVal !== oldVal) {
|
|
16262
|
+
attrs[aName] = newVal;
|
|
16263
|
+
} else {
|
|
16264
|
+
attrs[aName] = parseValue(
|
|
16265
|
+
oldVal,
|
|
16266
|
+
this.options.parseAttributeValue,
|
|
16267
|
+
this.options.numberParseOptions
|
|
16268
|
+
);
|
|
16269
|
+
}
|
|
16270
|
+
} else if (this.options.allowBooleanAttributes) {
|
|
16271
|
+
attrs[aName] = true;
|
|
16272
|
+
}
|
|
16273
|
+
}
|
|
16274
|
+
}
|
|
16275
|
+
if (!Object.keys(attrs).length) {
|
|
16276
|
+
return;
|
|
16277
|
+
}
|
|
16278
|
+
if (this.options.attributesGroupName) {
|
|
16279
|
+
const attrCollection = {};
|
|
16280
|
+
attrCollection[this.options.attributesGroupName] = attrs;
|
|
16281
|
+
return attrCollection;
|
|
16282
|
+
}
|
|
16283
|
+
return attrs;
|
|
16284
|
+
}
|
|
16285
|
+
}
|
|
16286
|
+
var parseXml = function(xmlData) {
|
|
16287
|
+
xmlData = xmlData.replace(/\r\n?/g, "\n");
|
|
16288
|
+
const xmlObj = new XmlNode("!xml");
|
|
16289
|
+
let currentNode = xmlObj;
|
|
16290
|
+
let textData = "";
|
|
16291
|
+
let jPath = "";
|
|
16292
|
+
this.entityExpansionCount = 0;
|
|
16293
|
+
this.currentExpandedLength = 0;
|
|
16294
|
+
const docTypeReader = new DocTypeReader(this.options.processEntities);
|
|
16295
|
+
for (let i = 0; i < xmlData.length; i++) {
|
|
16296
|
+
const ch = xmlData[i];
|
|
16297
|
+
if (ch === "<") {
|
|
16298
|
+
if (xmlData[i + 1] === "/") {
|
|
16299
|
+
const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
|
|
16300
|
+
let tagName = xmlData.substring(i + 2, closeIndex).trim();
|
|
16301
|
+
if (this.options.removeNSPrefix) {
|
|
16302
|
+
const colonIndex = tagName.indexOf(":");
|
|
16303
|
+
if (colonIndex !== -1) {
|
|
16304
|
+
tagName = tagName.substr(colonIndex + 1);
|
|
16305
|
+
}
|
|
16306
|
+
}
|
|
16307
|
+
if (this.options.transformTagName) {
|
|
16308
|
+
tagName = this.options.transformTagName(tagName);
|
|
16309
|
+
}
|
|
16310
|
+
if (currentNode) {
|
|
16311
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
16312
|
+
}
|
|
16313
|
+
const lastTagName = jPath.substring(jPath.lastIndexOf(".") + 1);
|
|
16314
|
+
if (tagName && this.options.unpairedTags.indexOf(tagName) !== -1) {
|
|
16315
|
+
throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
|
|
16316
|
+
}
|
|
16317
|
+
let propIndex = 0;
|
|
16318
|
+
if (lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1) {
|
|
16319
|
+
propIndex = jPath.lastIndexOf(".", jPath.lastIndexOf(".") - 1);
|
|
16320
|
+
this.tagsNodeStack.pop();
|
|
16321
|
+
} else {
|
|
16322
|
+
propIndex = jPath.lastIndexOf(".");
|
|
16323
|
+
}
|
|
16324
|
+
jPath = jPath.substring(0, propIndex);
|
|
16325
|
+
currentNode = this.tagsNodeStack.pop();
|
|
16326
|
+
textData = "";
|
|
16327
|
+
i = closeIndex;
|
|
16328
|
+
} else if (xmlData[i + 1] === "?") {
|
|
16329
|
+
let tagData = readTagExp(xmlData, i, false, "?>");
|
|
16330
|
+
if (!tagData) throw new Error("Pi Tag is not closed.");
|
|
16331
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
16332
|
+
if (this.options.ignoreDeclaration && tagData.tagName === "?xml" || this.options.ignorePiTags) {
|
|
16333
|
+
} else {
|
|
16334
|
+
const childNode = new XmlNode(tagData.tagName);
|
|
16335
|
+
childNode.add(this.options.textNodeName, "");
|
|
16336
|
+
if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) {
|
|
16337
|
+
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
|
|
16338
|
+
}
|
|
16339
|
+
this.addChild(currentNode, childNode, jPath, i);
|
|
16340
|
+
}
|
|
16341
|
+
i = tagData.closeIndex + 1;
|
|
16342
|
+
} else if (xmlData.substr(i + 1, 3) === "!--") {
|
|
16343
|
+
const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.");
|
|
16344
|
+
if (this.options.commentPropName) {
|
|
16345
|
+
const comment = xmlData.substring(i + 4, endIndex - 2);
|
|
16346
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
16347
|
+
currentNode.add(this.options.commentPropName, [{ [this.options.textNodeName]: comment }]);
|
|
16348
|
+
}
|
|
16349
|
+
i = endIndex;
|
|
16350
|
+
} else if (xmlData.substr(i + 1, 2) === "!D") {
|
|
16351
|
+
const result = docTypeReader.readDocType(xmlData, i);
|
|
16352
|
+
this.docTypeEntities = result.entities;
|
|
16353
|
+
i = result.i;
|
|
16354
|
+
} else if (xmlData.substr(i + 1, 2) === "![") {
|
|
16355
|
+
const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
|
|
16356
|
+
const tagExp = xmlData.substring(i + 9, closeIndex);
|
|
16357
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
16358
|
+
let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true);
|
|
16359
|
+
if (val == void 0) val = "";
|
|
16360
|
+
if (this.options.cdataPropName) {
|
|
16361
|
+
currentNode.add(this.options.cdataPropName, [{ [this.options.textNodeName]: tagExp }]);
|
|
16362
|
+
} else {
|
|
16363
|
+
currentNode.add(this.options.textNodeName, val);
|
|
16364
|
+
}
|
|
16365
|
+
i = closeIndex + 2;
|
|
16366
|
+
} else {
|
|
16367
|
+
let result = readTagExp(xmlData, i, this.options.removeNSPrefix);
|
|
16368
|
+
let tagName = result.tagName;
|
|
16369
|
+
const rawTagName = result.rawTagName;
|
|
16370
|
+
let tagExp = result.tagExp;
|
|
16371
|
+
let attrExpPresent = result.attrExpPresent;
|
|
16372
|
+
let closeIndex = result.closeIndex;
|
|
16373
|
+
if (this.options.transformTagName) {
|
|
16374
|
+
const newTagName = this.options.transformTagName(tagName);
|
|
16375
|
+
if (tagExp === tagName) {
|
|
16376
|
+
tagExp = newTagName;
|
|
16377
|
+
}
|
|
16378
|
+
tagName = newTagName;
|
|
16379
|
+
}
|
|
16380
|
+
if (currentNode && textData) {
|
|
16381
|
+
if (currentNode.tagname !== "!xml") {
|
|
16382
|
+
textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
|
|
16383
|
+
}
|
|
16384
|
+
}
|
|
16385
|
+
const lastTag = currentNode;
|
|
16386
|
+
if (lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1) {
|
|
16387
|
+
currentNode = this.tagsNodeStack.pop();
|
|
16388
|
+
jPath = jPath.substring(0, jPath.lastIndexOf("."));
|
|
16389
|
+
}
|
|
16390
|
+
if (tagName !== xmlObj.tagname) {
|
|
16391
|
+
jPath += jPath ? "." + tagName : tagName;
|
|
16392
|
+
}
|
|
16393
|
+
const startIndex = i;
|
|
16394
|
+
if (this.isItStopNode(this.stopNodesExact, this.stopNodesWildcard, jPath, tagName)) {
|
|
16395
|
+
let tagContent = "";
|
|
16396
|
+
if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
|
|
16397
|
+
if (tagName[tagName.length - 1] === "/") {
|
|
16398
|
+
tagName = tagName.substr(0, tagName.length - 1);
|
|
16399
|
+
jPath = jPath.substr(0, jPath.length - 1);
|
|
16400
|
+
tagExp = tagName;
|
|
16401
|
+
} else {
|
|
16402
|
+
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
16403
|
+
}
|
|
16404
|
+
i = result.closeIndex;
|
|
16405
|
+
} else if (this.options.unpairedTags.indexOf(tagName) !== -1) {
|
|
16406
|
+
i = result.closeIndex;
|
|
16407
|
+
} else {
|
|
16408
|
+
const result2 = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
|
|
16409
|
+
if (!result2) throw new Error(`Unexpected end of ${rawTagName}`);
|
|
16410
|
+
i = result2.i;
|
|
16411
|
+
tagContent = result2.tagContent;
|
|
16412
|
+
}
|
|
16413
|
+
const childNode = new XmlNode(tagName);
|
|
16414
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
16415
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
16416
|
+
}
|
|
16417
|
+
if (tagContent) {
|
|
16418
|
+
tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
|
|
16419
|
+
}
|
|
16420
|
+
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
16421
|
+
childNode.add(this.options.textNodeName, tagContent);
|
|
16422
|
+
this.addChild(currentNode, childNode, jPath, startIndex);
|
|
16423
|
+
} else {
|
|
16424
|
+
if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
|
|
16425
|
+
if (tagName[tagName.length - 1] === "/") {
|
|
16426
|
+
tagName = tagName.substr(0, tagName.length - 1);
|
|
16427
|
+
jPath = jPath.substr(0, jPath.length - 1);
|
|
16428
|
+
tagExp = tagName;
|
|
16429
|
+
} else {
|
|
16430
|
+
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
16431
|
+
}
|
|
16432
|
+
if (this.options.transformTagName) {
|
|
16433
|
+
const newTagName = this.options.transformTagName(tagName);
|
|
16434
|
+
if (tagExp === tagName) {
|
|
16435
|
+
tagExp = newTagName;
|
|
16436
|
+
}
|
|
16437
|
+
tagName = newTagName;
|
|
16438
|
+
}
|
|
16439
|
+
const childNode = new XmlNode(tagName);
|
|
16440
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
16441
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
16442
|
+
}
|
|
16443
|
+
this.addChild(currentNode, childNode, jPath, startIndex);
|
|
16444
|
+
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
16445
|
+
} else {
|
|
16446
|
+
const childNode = new XmlNode(tagName);
|
|
16447
|
+
if (this.tagsNodeStack.length > this.options.maxNestedTags) {
|
|
16448
|
+
throw new Error("Maximum nested tags exceeded");
|
|
16449
|
+
}
|
|
16450
|
+
this.tagsNodeStack.push(currentNode);
|
|
16451
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
16452
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
16453
|
+
}
|
|
16454
|
+
this.addChild(currentNode, childNode, jPath, startIndex);
|
|
16455
|
+
currentNode = childNode;
|
|
16456
|
+
}
|
|
16457
|
+
textData = "";
|
|
16458
|
+
i = closeIndex;
|
|
16459
|
+
}
|
|
16460
|
+
}
|
|
16461
|
+
} else {
|
|
16462
|
+
textData += xmlData[i];
|
|
16463
|
+
}
|
|
16464
|
+
}
|
|
16465
|
+
return xmlObj.child;
|
|
16466
|
+
};
|
|
16467
|
+
function addChild(currentNode, childNode, jPath, startIndex) {
|
|
16468
|
+
if (!this.options.captureMetaData) startIndex = void 0;
|
|
16469
|
+
const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"]);
|
|
16470
|
+
if (result === false) {
|
|
16471
|
+
} else if (typeof result === "string") {
|
|
16472
|
+
childNode.tagname = result;
|
|
16473
|
+
currentNode.addChild(childNode, startIndex);
|
|
16474
|
+
} else {
|
|
16475
|
+
currentNode.addChild(childNode, startIndex);
|
|
16476
|
+
}
|
|
16477
|
+
}
|
|
16478
|
+
var replaceEntitiesValue = function(val, tagName, jPath) {
|
|
16479
|
+
if (val.indexOf("&") === -1) {
|
|
16480
|
+
return val;
|
|
16481
|
+
}
|
|
16482
|
+
const entityConfig = this.options.processEntities;
|
|
16483
|
+
if (!entityConfig.enabled) {
|
|
16484
|
+
return val;
|
|
16485
|
+
}
|
|
16486
|
+
if (entityConfig.allowedTags) {
|
|
16487
|
+
if (!entityConfig.allowedTags.includes(tagName)) {
|
|
16488
|
+
return val;
|
|
16489
|
+
}
|
|
16490
|
+
}
|
|
16491
|
+
if (entityConfig.tagFilter) {
|
|
16492
|
+
if (!entityConfig.tagFilter(tagName, jPath)) {
|
|
16493
|
+
return val;
|
|
16494
|
+
}
|
|
16495
|
+
}
|
|
16496
|
+
for (let entityName in this.docTypeEntities) {
|
|
16497
|
+
const entity = this.docTypeEntities[entityName];
|
|
16498
|
+
const matches = val.match(entity.regx);
|
|
16499
|
+
if (matches) {
|
|
16500
|
+
this.entityExpansionCount += matches.length;
|
|
16501
|
+
if (entityConfig.maxTotalExpansions && this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
16502
|
+
throw new Error(
|
|
16503
|
+
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
16504
|
+
);
|
|
16505
|
+
}
|
|
16506
|
+
const lengthBefore = val.length;
|
|
16507
|
+
val = val.replace(entity.regx, entity.val);
|
|
16508
|
+
if (entityConfig.maxExpandedLength) {
|
|
16509
|
+
this.currentExpandedLength += val.length - lengthBefore;
|
|
16510
|
+
if (this.currentExpandedLength > entityConfig.maxExpandedLength) {
|
|
16511
|
+
throw new Error(
|
|
16512
|
+
`Total expanded content size exceeded: ${this.currentExpandedLength} > ${entityConfig.maxExpandedLength}`
|
|
16513
|
+
);
|
|
16514
|
+
}
|
|
16515
|
+
}
|
|
16516
|
+
}
|
|
16517
|
+
}
|
|
16518
|
+
if (val.indexOf("&") === -1) return val;
|
|
16519
|
+
for (let entityName in this.lastEntities) {
|
|
16520
|
+
const entity = this.lastEntities[entityName];
|
|
16521
|
+
val = val.replace(entity.regex, entity.val);
|
|
16522
|
+
}
|
|
16523
|
+
if (val.indexOf("&") === -1) return val;
|
|
16524
|
+
if (this.options.htmlEntities) {
|
|
16525
|
+
for (let entityName in this.htmlEntities) {
|
|
16526
|
+
const entity = this.htmlEntities[entityName];
|
|
16527
|
+
val = val.replace(entity.regex, entity.val);
|
|
16528
|
+
}
|
|
16529
|
+
}
|
|
16530
|
+
val = val.replace(this.ampEntity.regex, this.ampEntity.val);
|
|
16531
|
+
return val;
|
|
16532
|
+
};
|
|
16533
|
+
function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
|
|
16534
|
+
if (textData) {
|
|
16535
|
+
if (isLeafNode === void 0) isLeafNode = currentNode.child.length === 0;
|
|
16536
|
+
textData = this.parseTextData(
|
|
16537
|
+
textData,
|
|
16538
|
+
currentNode.tagname,
|
|
16539
|
+
jPath,
|
|
16540
|
+
false,
|
|
16541
|
+
currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
|
|
16542
|
+
isLeafNode
|
|
16543
|
+
);
|
|
16544
|
+
if (textData !== void 0 && textData !== "")
|
|
16545
|
+
currentNode.add(this.options.textNodeName, textData);
|
|
16546
|
+
textData = "";
|
|
16547
|
+
}
|
|
16548
|
+
return textData;
|
|
16549
|
+
}
|
|
16550
|
+
function isItStopNode(stopNodesExact, stopNodesWildcard, jPath, currentTagName) {
|
|
16551
|
+
if (stopNodesWildcard && stopNodesWildcard.has(currentTagName)) return true;
|
|
16552
|
+
if (stopNodesExact && stopNodesExact.has(jPath)) return true;
|
|
16553
|
+
return false;
|
|
16554
|
+
}
|
|
16555
|
+
function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
|
16556
|
+
let attrBoundary;
|
|
16557
|
+
let tagExp = "";
|
|
16558
|
+
for (let index = i; index < xmlData.length; index++) {
|
|
16559
|
+
let ch = xmlData[index];
|
|
16560
|
+
if (attrBoundary) {
|
|
16561
|
+
if (ch === attrBoundary) attrBoundary = "";
|
|
16562
|
+
} else if (ch === '"' || ch === "'") {
|
|
16563
|
+
attrBoundary = ch;
|
|
16564
|
+
} else if (ch === closingChar[0]) {
|
|
16565
|
+
if (closingChar[1]) {
|
|
16566
|
+
if (xmlData[index + 1] === closingChar[1]) {
|
|
16567
|
+
return {
|
|
16568
|
+
data: tagExp,
|
|
16569
|
+
index
|
|
16570
|
+
};
|
|
16571
|
+
}
|
|
16572
|
+
} else {
|
|
16573
|
+
return {
|
|
16574
|
+
data: tagExp,
|
|
16575
|
+
index
|
|
16576
|
+
};
|
|
16577
|
+
}
|
|
16578
|
+
} else if (ch === " ") {
|
|
16579
|
+
ch = " ";
|
|
16580
|
+
}
|
|
16581
|
+
tagExp += ch;
|
|
16582
|
+
}
|
|
16583
|
+
}
|
|
16584
|
+
function findClosingIndex(xmlData, str2, i, errMsg) {
|
|
16585
|
+
const closingIndex = xmlData.indexOf(str2, i);
|
|
16586
|
+
if (closingIndex === -1) {
|
|
16587
|
+
throw new Error(errMsg);
|
|
16588
|
+
} else {
|
|
16589
|
+
return closingIndex + str2.length - 1;
|
|
16590
|
+
}
|
|
16591
|
+
}
|
|
16592
|
+
function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") {
|
|
16593
|
+
const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);
|
|
16594
|
+
if (!result) return;
|
|
16595
|
+
let tagExp = result.data;
|
|
16596
|
+
const closeIndex = result.index;
|
|
16597
|
+
const separatorIndex = tagExp.search(/\s/);
|
|
16598
|
+
let tagName = tagExp;
|
|
16599
|
+
let attrExpPresent = true;
|
|
16600
|
+
if (separatorIndex !== -1) {
|
|
16601
|
+
tagName = tagExp.substring(0, separatorIndex);
|
|
16602
|
+
tagExp = tagExp.substring(separatorIndex + 1).trimStart();
|
|
16603
|
+
}
|
|
16604
|
+
const rawTagName = tagName;
|
|
16605
|
+
if (removeNSPrefix) {
|
|
16606
|
+
const colonIndex = tagName.indexOf(":");
|
|
16607
|
+
if (colonIndex !== -1) {
|
|
16608
|
+
tagName = tagName.substr(colonIndex + 1);
|
|
16609
|
+
attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
|
|
16610
|
+
}
|
|
16611
|
+
}
|
|
16612
|
+
return {
|
|
16613
|
+
tagName,
|
|
16614
|
+
tagExp,
|
|
16615
|
+
closeIndex,
|
|
16616
|
+
attrExpPresent,
|
|
16617
|
+
rawTagName
|
|
16618
|
+
};
|
|
16619
|
+
}
|
|
16620
|
+
function readStopNodeData(xmlData, tagName, i) {
|
|
16621
|
+
const startIndex = i;
|
|
16622
|
+
let openTagCount = 1;
|
|
16623
|
+
for (; i < xmlData.length; i++) {
|
|
16624
|
+
if (xmlData[i] === "<") {
|
|
16625
|
+
if (xmlData[i + 1] === "/") {
|
|
16626
|
+
const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
|
|
16627
|
+
let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
|
|
16628
|
+
if (closeTagName === tagName) {
|
|
16629
|
+
openTagCount--;
|
|
16630
|
+
if (openTagCount === 0) {
|
|
16631
|
+
return {
|
|
16632
|
+
tagContent: xmlData.substring(startIndex, i),
|
|
16633
|
+
i: closeIndex
|
|
16634
|
+
};
|
|
16635
|
+
}
|
|
16636
|
+
}
|
|
16637
|
+
i = closeIndex;
|
|
16638
|
+
} else if (xmlData[i + 1] === "?") {
|
|
16639
|
+
const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.");
|
|
16640
|
+
i = closeIndex;
|
|
16641
|
+
} else if (xmlData.substr(i + 1, 3) === "!--") {
|
|
16642
|
+
const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.");
|
|
16643
|
+
i = closeIndex;
|
|
16644
|
+
} else if (xmlData.substr(i + 1, 2) === "![") {
|
|
16645
|
+
const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
|
|
16646
|
+
i = closeIndex;
|
|
16647
|
+
} else {
|
|
16648
|
+
const tagData = readTagExp(xmlData, i, ">");
|
|
16649
|
+
if (tagData) {
|
|
16650
|
+
const openTagName = tagData && tagData.tagName;
|
|
16651
|
+
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") {
|
|
16652
|
+
openTagCount++;
|
|
16653
|
+
}
|
|
16654
|
+
i = tagData.closeIndex;
|
|
16655
|
+
}
|
|
16656
|
+
}
|
|
16657
|
+
}
|
|
16658
|
+
}
|
|
16659
|
+
}
|
|
16660
|
+
function parseValue(val, shouldParse, options) {
|
|
16661
|
+
if (shouldParse && typeof val === "string") {
|
|
16662
|
+
const newval = val.trim();
|
|
16663
|
+
if (newval === "true") return true;
|
|
16664
|
+
else if (newval === "false") return false;
|
|
16665
|
+
else return toNumber(val, options);
|
|
16666
|
+
} else {
|
|
16667
|
+
if (isExist(val)) {
|
|
16668
|
+
return val;
|
|
16669
|
+
} else {
|
|
16670
|
+
return "";
|
|
16671
|
+
}
|
|
16672
|
+
}
|
|
16673
|
+
}
|
|
16674
|
+
function fromCodePoint(str2, base, prefix) {
|
|
16675
|
+
const codePoint = Number.parseInt(str2, base);
|
|
16676
|
+
if (codePoint >= 0 && codePoint <= 1114111) {
|
|
16677
|
+
return String.fromCodePoint(codePoint);
|
|
16678
|
+
} else {
|
|
16679
|
+
return prefix + str2 + ";";
|
|
16680
|
+
}
|
|
16681
|
+
}
|
|
16682
|
+
|
|
16683
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/node2json.js
|
|
16684
|
+
var METADATA_SYMBOL2 = XmlNode.getMetaDataSymbol();
|
|
16685
|
+
function prettify(node, options) {
|
|
16686
|
+
return compress(node, options);
|
|
16687
|
+
}
|
|
16688
|
+
function compress(arr, options, jPath) {
|
|
16689
|
+
let text2;
|
|
16690
|
+
const compressedObj = {};
|
|
16691
|
+
for (let i = 0; i < arr.length; i++) {
|
|
16692
|
+
const tagObj = arr[i];
|
|
16693
|
+
const property = propName(tagObj);
|
|
16694
|
+
let newJpath = "";
|
|
16695
|
+
if (jPath === void 0) newJpath = property;
|
|
16696
|
+
else newJpath = jPath + "." + property;
|
|
16697
|
+
if (property === options.textNodeName) {
|
|
16698
|
+
if (text2 === void 0) text2 = tagObj[property];
|
|
16699
|
+
else text2 += "" + tagObj[property];
|
|
16700
|
+
} else if (property === void 0) {
|
|
16701
|
+
continue;
|
|
16702
|
+
} else if (tagObj[property]) {
|
|
16703
|
+
let val = compress(tagObj[property], options, newJpath);
|
|
16704
|
+
const isLeaf = isLeafTag(val, options);
|
|
16705
|
+
if (tagObj[":@"]) {
|
|
16706
|
+
assignAttributes(val, tagObj[":@"], newJpath, options);
|
|
16707
|
+
} else if (Object.keys(val).length === 1 && val[options.textNodeName] !== void 0 && !options.alwaysCreateTextNode) {
|
|
16708
|
+
val = val[options.textNodeName];
|
|
16709
|
+
} else if (Object.keys(val).length === 0) {
|
|
16710
|
+
if (options.alwaysCreateTextNode) val[options.textNodeName] = "";
|
|
16711
|
+
else val = "";
|
|
16712
|
+
}
|
|
16713
|
+
if (tagObj[METADATA_SYMBOL2] !== void 0 && typeof val === "object" && val !== null) {
|
|
16714
|
+
val[METADATA_SYMBOL2] = tagObj[METADATA_SYMBOL2];
|
|
16715
|
+
}
|
|
16716
|
+
if (compressedObj[property] !== void 0 && Object.prototype.hasOwnProperty.call(compressedObj, property)) {
|
|
16717
|
+
if (!Array.isArray(compressedObj[property])) {
|
|
16718
|
+
compressedObj[property] = [compressedObj[property]];
|
|
16719
|
+
}
|
|
16720
|
+
compressedObj[property].push(val);
|
|
16721
|
+
} else {
|
|
16722
|
+
if (options.isArray(property, newJpath, isLeaf)) {
|
|
16723
|
+
compressedObj[property] = [val];
|
|
16724
|
+
} else {
|
|
16725
|
+
compressedObj[property] = val;
|
|
16726
|
+
}
|
|
16727
|
+
}
|
|
16728
|
+
}
|
|
16729
|
+
}
|
|
16730
|
+
if (typeof text2 === "string") {
|
|
16731
|
+
if (text2.length > 0) compressedObj[options.textNodeName] = text2;
|
|
16732
|
+
} else if (text2 !== void 0) compressedObj[options.textNodeName] = text2;
|
|
16733
|
+
return compressedObj;
|
|
16734
|
+
}
|
|
16735
|
+
function propName(obj) {
|
|
16736
|
+
const keys = Object.keys(obj);
|
|
16737
|
+
for (let i = 0; i < keys.length; i++) {
|
|
16738
|
+
const key = keys[i];
|
|
16739
|
+
if (key !== ":@") return key;
|
|
16740
|
+
}
|
|
16741
|
+
}
|
|
16742
|
+
function assignAttributes(obj, attrMap, jpath, options) {
|
|
16743
|
+
if (attrMap) {
|
|
16744
|
+
const keys = Object.keys(attrMap);
|
|
16745
|
+
const len = keys.length;
|
|
16746
|
+
for (let i = 0; i < len; i++) {
|
|
16747
|
+
const atrrName = keys[i];
|
|
16748
|
+
if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
|
|
16749
|
+
obj[atrrName] = [attrMap[atrrName]];
|
|
16750
|
+
} else {
|
|
16751
|
+
obj[atrrName] = attrMap[atrrName];
|
|
16752
|
+
}
|
|
16753
|
+
}
|
|
16754
|
+
}
|
|
16755
|
+
}
|
|
16756
|
+
function isLeafTag(obj, options) {
|
|
16757
|
+
const { textNodeName } = options;
|
|
16758
|
+
const propCount = Object.keys(obj).length;
|
|
16759
|
+
if (propCount === 0) {
|
|
16760
|
+
return true;
|
|
16761
|
+
}
|
|
16762
|
+
if (propCount === 1 && (obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)) {
|
|
16763
|
+
return true;
|
|
16764
|
+
}
|
|
16765
|
+
return false;
|
|
16766
|
+
}
|
|
16767
|
+
|
|
16768
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js
|
|
16769
|
+
var XMLParser = class {
|
|
16770
|
+
constructor(options) {
|
|
16771
|
+
this.externalEntities = {};
|
|
16772
|
+
this.options = buildOptions(options);
|
|
16773
|
+
}
|
|
16774
|
+
/**
|
|
16775
|
+
* Parse XML dats to JS object
|
|
16776
|
+
* @param {string|Uint8Array} xmlData
|
|
16777
|
+
* @param {boolean|Object} validationOption
|
|
16778
|
+
*/
|
|
16779
|
+
parse(xmlData, validationOption) {
|
|
16780
|
+
if (typeof xmlData !== "string" && xmlData.toString) {
|
|
16781
|
+
xmlData = xmlData.toString();
|
|
16782
|
+
} else if (typeof xmlData !== "string") {
|
|
16783
|
+
throw new Error("XML data is accepted in String or Bytes[] form.");
|
|
16784
|
+
}
|
|
16785
|
+
if (validationOption) {
|
|
16786
|
+
if (validationOption === true) validationOption = {};
|
|
16787
|
+
const result = validate(xmlData, validationOption);
|
|
16788
|
+
if (result !== true) {
|
|
16789
|
+
throw Error(`${result.err.msg}:${result.err.line}:${result.err.col}`);
|
|
16790
|
+
}
|
|
16791
|
+
}
|
|
16792
|
+
const orderedObjParser = new OrderedObjParser(this.options);
|
|
16793
|
+
orderedObjParser.addExternalEntities(this.externalEntities);
|
|
16794
|
+
const orderedResult = orderedObjParser.parseXml(xmlData);
|
|
16795
|
+
if (this.options.preserveOrder || orderedResult === void 0) return orderedResult;
|
|
16796
|
+
else return prettify(orderedResult, this.options);
|
|
16797
|
+
}
|
|
16798
|
+
/**
|
|
16799
|
+
* Add Entity which is not by default supported by this library
|
|
16800
|
+
* @param {string} key
|
|
16801
|
+
* @param {string} value
|
|
16802
|
+
*/
|
|
16803
|
+
addEntity(key, value) {
|
|
16804
|
+
if (value.indexOf("&") !== -1) {
|
|
16805
|
+
throw new Error("Entity value can't have '&'");
|
|
16806
|
+
} else if (key.indexOf("&") !== -1 || key.indexOf(";") !== -1) {
|
|
16807
|
+
throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '
'");
|
|
16808
|
+
} else if (value === "&") {
|
|
16809
|
+
throw new Error("An entity with value '&' is not permitted");
|
|
16810
|
+
} else {
|
|
16811
|
+
this.externalEntities[key] = value;
|
|
16812
|
+
}
|
|
16813
|
+
}
|
|
16814
|
+
/**
|
|
16815
|
+
* Returns a Symbol that can be used to access the metadata
|
|
16816
|
+
* property on a node.
|
|
16817
|
+
*
|
|
16818
|
+
* If Symbol is not available in the environment, an ordinary property is used
|
|
16819
|
+
* and the name of the property is here returned.
|
|
16820
|
+
*
|
|
16821
|
+
* The XMLMetaData property is only present when `captureMetaData`
|
|
16822
|
+
* is true in the options.
|
|
16823
|
+
*/
|
|
16824
|
+
static getMetaDataSymbol() {
|
|
16825
|
+
return XmlNode.getMetaDataSymbol();
|
|
16826
|
+
}
|
|
16827
|
+
};
|
|
16828
|
+
|
|
16829
|
+
// ../../node_modules/.pnpm/fast-xml-parser@5.3.8/node_modules/fast-xml-parser/src/fxp.js
|
|
16830
|
+
var XMLValidator = {
|
|
16831
|
+
validate
|
|
16832
|
+
};
|
|
16833
|
+
|
|
16834
|
+
// ../../packages/analysis/src/cvss.ts
|
|
16835
|
+
function cvss3(vector) {
|
|
16836
|
+
if (!/^CVSS:3\.[01]\//.test(vector)) return null;
|
|
16837
|
+
const metrics = Object.fromEntries(
|
|
16838
|
+
vector.split("/").slice(1).map((m) => m.split(":"))
|
|
16839
|
+
);
|
|
16840
|
+
const pick2 = (key, values) => values[metrics[key] ?? ""];
|
|
16841
|
+
const changed = metrics.S === "C";
|
|
16842
|
+
if (!["C", "U"].includes(metrics.S ?? "")) return null;
|
|
16843
|
+
const av = pick2("AV", { N: 0.85, A: 0.62, L: 0.55, P: 0.2 });
|
|
16844
|
+
const ac = pick2("AC", { L: 0.77, H: 0.44 });
|
|
16845
|
+
const pr = pick2(
|
|
16846
|
+
"PR",
|
|
16847
|
+
changed ? { N: 0.85, L: 0.68, H: 0.5 } : { N: 0.85, L: 0.62, H: 0.27 }
|
|
16848
|
+
);
|
|
16849
|
+
const ui = pick2("UI", { N: 0.85, R: 0.62 });
|
|
16850
|
+
const c = pick2("C", { H: 0.56, L: 0.22, N: 0 });
|
|
16851
|
+
const i = pick2("I", { H: 0.56, L: 0.22, N: 0 });
|
|
16852
|
+
const a = pick2("A", { H: 0.56, L: 0.22, N: 0 });
|
|
16853
|
+
if ([av, ac, pr, ui, c, i, a].some((v) => v === void 0)) return null;
|
|
16854
|
+
const iss = 1 - (1 - c) * (1 - i) * (1 - a);
|
|
16855
|
+
const impact = changed ? 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02) ** 15 : 6.42 * iss;
|
|
16856
|
+
if (impact <= 0) return 0;
|
|
16857
|
+
const exploitability = 8.22 * av * ac * pr * ui;
|
|
16858
|
+
return Math.ceil(
|
|
16859
|
+
Math.min((changed ? 1.08 : 1) * (impact + exploitability), 10) * 10 - 1e-8
|
|
16860
|
+
) / 10;
|
|
16861
|
+
}
|
|
16862
|
+
|
|
16863
|
+
// ../../packages/analysis/src/importers.ts
|
|
16864
|
+
var record2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
16865
|
+
var list = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
|
|
16866
|
+
var str = (v) => typeof v === "string" ? v : "";
|
|
16867
|
+
var num = (v) => Number.isFinite(Number(v)) ? Math.max(0, Number(v)) : 0;
|
|
16868
|
+
function pathOf(v) {
|
|
16869
|
+
const result = relativePath.safeParse(str(v).replace(/^\.\//, ""));
|
|
16870
|
+
if (!result.success) throw new Error("Unsafe report path");
|
|
16871
|
+
return result.data;
|
|
16872
|
+
}
|
|
16873
|
+
var fingerprint = (...parts) => createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
16874
|
+
var language = (path) => ({
|
|
16875
|
+
ts: "typescript",
|
|
16876
|
+
tsx: "typescript",
|
|
16877
|
+
js: "javascript",
|
|
16878
|
+
jsx: "javascript",
|
|
16879
|
+
py: "python",
|
|
16880
|
+
go: "go",
|
|
16881
|
+
java: "java",
|
|
16882
|
+
cs: "csharp"
|
|
16883
|
+
})[path.split(".").pop() ?? ""] ?? "other";
|
|
16884
|
+
function finding(engine, rule, path, line, key, category = "security") {
|
|
16885
|
+
return {
|
|
16886
|
+
engine,
|
|
16887
|
+
rule: rule.slice(0, 400),
|
|
16888
|
+
fingerprint: fingerprint(engine, rule, path, key),
|
|
16889
|
+
matchKey: fingerprint(engine, rule, key),
|
|
16890
|
+
observed: true,
|
|
16891
|
+
category,
|
|
16892
|
+
severity: "high",
|
|
16893
|
+
language: language(path),
|
|
16894
|
+
title: rule.slice(0, 400),
|
|
16895
|
+
explanation: "Review the reported operation and its inputs.",
|
|
16896
|
+
remediation: "Validate untrusted input and verify the fix with a regression test.",
|
|
16897
|
+
location: { path: pathOf(path), ...line > 0 ? { line: Math.floor(line) } : {} },
|
|
16898
|
+
flow: [],
|
|
16899
|
+
state: "new"
|
|
16900
|
+
};
|
|
16901
|
+
}
|
|
16902
|
+
function semgrep(json2) {
|
|
16903
|
+
const root = record2(json2);
|
|
16904
|
+
if (!Array.isArray(root.results) || list(root.errors).length)
|
|
16905
|
+
throw new Error("Semgrep returned incomplete results");
|
|
16906
|
+
return root.results.map((raw) => {
|
|
16907
|
+
const r = record2(raw), extra = record2(r.extra), meta3 = record2(extra.metadata);
|
|
16908
|
+
const f = finding(
|
|
16909
|
+
"semgrep",
|
|
16910
|
+
str(r.check_id),
|
|
16911
|
+
pathOf(r.path),
|
|
16912
|
+
num(record2(r.start).line),
|
|
16913
|
+
fingerprint(str(extra.lines).replace(/\s+/g, " ").trim()),
|
|
16914
|
+
meta3.category === "quality" ? "quality" : "security"
|
|
16915
|
+
);
|
|
16916
|
+
f.severity = extra.severity === "WARNING" ? "medium" : extra.severity === "INFO" ? "low" : "high";
|
|
16917
|
+
return f;
|
|
16918
|
+
});
|
|
16919
|
+
}
|
|
16920
|
+
function gitleaks(json2) {
|
|
16921
|
+
if (!Array.isArray(json2)) throw new Error("Invalid Gitleaks report");
|
|
16922
|
+
return json2.map((raw) => {
|
|
16923
|
+
const r = record2(raw);
|
|
16924
|
+
const f = finding(
|
|
16925
|
+
"gitleaks",
|
|
16926
|
+
str(r.RuleID),
|
|
16927
|
+
pathOf(r.File),
|
|
16928
|
+
num(r.StartLine),
|
|
16929
|
+
fingerprint(str(r.Secret)),
|
|
16930
|
+
"secret"
|
|
16931
|
+
);
|
|
16932
|
+
f.severity = "critical";
|
|
16933
|
+
f.explanation = "A credential pattern was found. Its value was removed locally.";
|
|
16934
|
+
f.remediation = "Remove the credential, rotate it at the provider and scan again.";
|
|
16935
|
+
return f;
|
|
16936
|
+
});
|
|
16937
|
+
}
|
|
16938
|
+
function osv(json2) {
|
|
16939
|
+
const root = record2(json2);
|
|
16940
|
+
if (!Array.isArray(root.results)) throw new Error("Invalid OSV report");
|
|
16941
|
+
return root.results.flatMap((raw) => {
|
|
16942
|
+
const r = record2(raw), path = pathOf(record2(r.source).path);
|
|
16943
|
+
return list(r.packages).flatMap((rawPackage) => {
|
|
16944
|
+
const p = record2(rawPackage), pkg = record2(p.package);
|
|
16945
|
+
return list(p.vulnerabilities).map((rawV) => {
|
|
16946
|
+
const v = record2(rawV), id = str(v.id), f = finding(
|
|
16947
|
+
"osv",
|
|
16948
|
+
id,
|
|
16949
|
+
path,
|
|
16950
|
+
0,
|
|
16951
|
+
`${str(pkg.ecosystem)}:${str(pkg.name)}:${id}`,
|
|
16952
|
+
"dependency"
|
|
16953
|
+
);
|
|
16954
|
+
const affected = list(v.affected).filter((a) => {
|
|
16955
|
+
const target = record2(record2(a).package);
|
|
16956
|
+
return !target.name || target.name === pkg.name && target.ecosystem === pkg.ecosystem;
|
|
16957
|
+
});
|
|
16958
|
+
const fixed = affected.flatMap(
|
|
16959
|
+
(a) => list(record2(a).ranges).flatMap(
|
|
16960
|
+
(r2) => list(record2(r2).events).map((e) => str(record2(e).fixed)).filter(Boolean)
|
|
16961
|
+
)
|
|
16962
|
+
);
|
|
16963
|
+
const severity = str(record2(v.database_specific).severity).toLowerCase();
|
|
16964
|
+
const scores = list(v.severity).map((s) => cvss3(str(record2(s).score))).filter((n) => n !== null);
|
|
16965
|
+
const score = scores.length ? Math.max(...scores) : null;
|
|
16966
|
+
f.severity = ["critical", "high", "medium", "low"].includes(severity) ? severity : score === null ? "unknown" : score >= 9 ? "critical" : score >= 7 ? "high" : score >= 4 ? "medium" : "low";
|
|
16967
|
+
f.package = {
|
|
16968
|
+
name: str(pkg.name),
|
|
16969
|
+
version: str(pkg.version),
|
|
16970
|
+
ecosystem: str(pkg.ecosystem),
|
|
16971
|
+
advisory: id,
|
|
16972
|
+
fixed: [...new Set(fixed)].slice(0, 50),
|
|
16973
|
+
relationship: "unknown"
|
|
16974
|
+
};
|
|
16975
|
+
f.title = id;
|
|
16976
|
+
f.explanation = "The resolved package version is affected by a published advisory.";
|
|
16977
|
+
f.remediation = "Review the advisory and upgrade to a compatible fixed version; run your tests.";
|
|
16978
|
+
return f;
|
|
16979
|
+
});
|
|
16980
|
+
});
|
|
16981
|
+
});
|
|
16982
|
+
}
|
|
16983
|
+
function sarif(json2) {
|
|
16984
|
+
const root = record2(json2);
|
|
16985
|
+
if (root.version !== "2.1.0" || !Array.isArray(root.runs))
|
|
16986
|
+
throw new Error("Expected SARIF 2.1.0");
|
|
16987
|
+
return root.runs.flatMap((rawRun) => {
|
|
16988
|
+
const run = record2(rawRun);
|
|
16989
|
+
if (list(run.invocations).some((i) => record2(i).executionSuccessful === false))
|
|
16990
|
+
throw new Error("SARIF analysis did not complete");
|
|
16991
|
+
const artifactPath = (location2) => {
|
|
16992
|
+
const artifact = record2(location2.artifactLocation);
|
|
16993
|
+
const indexed = record2(list(run.artifacts)[Number(artifact.index)]);
|
|
16994
|
+
return pathOf(artifact.uri ?? record2(indexed.location).uri);
|
|
16995
|
+
};
|
|
16996
|
+
return list(run.results).map((raw) => {
|
|
16997
|
+
const r = record2(raw), loc = record2(record2(list(r.locations)[0]).physicalLocation);
|
|
16998
|
+
const f = finding(
|
|
16999
|
+
"sarif",
|
|
17000
|
+
str(r.ruleId),
|
|
17001
|
+
artifactPath(loc),
|
|
17002
|
+
num(record2(loc.region).startLine),
|
|
17003
|
+
str(record2(r.partialFingerprints)["vibecheck"]) || str(record2(r.partialFingerprints)["primaryLocationLineHash"]) || fingerprint(
|
|
17004
|
+
str(r.ruleId),
|
|
17005
|
+
String(num(record2(loc.region).startLine)),
|
|
17006
|
+
String(num(record2(loc.region).startColumn))
|
|
17007
|
+
)
|
|
17008
|
+
);
|
|
17009
|
+
f.severity = r.level === "warning" ? "medium" : r.level === "note" ? "low" : "high";
|
|
17010
|
+
f.flow = list(r.codeFlows).flatMap(
|
|
17011
|
+
(c) => list(record2(c).threadFlows).flatMap(
|
|
17012
|
+
(t) => list(record2(t).locations).map((rawLoc) => {
|
|
17013
|
+
const l = record2(record2(record2(rawLoc).location).physicalLocation);
|
|
17014
|
+
return {
|
|
17015
|
+
path: artifactPath(l),
|
|
17016
|
+
line: Math.max(1, num(record2(l.region).startLine))
|
|
17017
|
+
};
|
|
17018
|
+
})
|
|
17019
|
+
)
|
|
17020
|
+
).slice(0, 100);
|
|
17021
|
+
return f;
|
|
17022
|
+
});
|
|
17023
|
+
});
|
|
17024
|
+
}
|
|
17025
|
+
function importSarif(json2, reportPath) {
|
|
17026
|
+
const root = record2(json2);
|
|
17027
|
+
if (root.version !== "2.1.0" || !Array.isArray(root.runs) || !root.runs.length)
|
|
17028
|
+
throw new Error("Expected nonempty SARIF 2.1.0 runs");
|
|
17029
|
+
const engines = [];
|
|
17030
|
+
const findings = [];
|
|
17031
|
+
for (const [index, raw] of root.runs.entries()) {
|
|
17032
|
+
const run = record2(raw), driver = record2(record2(run.tool).driver);
|
|
17033
|
+
const name = str(driver.name);
|
|
17034
|
+
if (!name || !Array.isArray(run.results))
|
|
17035
|
+
throw new Error("SARIF tool or results missing");
|
|
17036
|
+
const id = "sarif:" + fingerprint(reportPath, name, String(index)).slice(0, 24);
|
|
17037
|
+
const rules = fingerprint(
|
|
17038
|
+
JSON.stringify(
|
|
17039
|
+
list(driver.rules).map((r) => record2(r)).sort((a, b) => str(a.id).localeCompare(str(b.id)))
|
|
17040
|
+
)
|
|
17041
|
+
);
|
|
17042
|
+
const imported = sarif({ version: "2.1.0", runs: [run] }).map((f) => ({
|
|
17043
|
+
...f,
|
|
17044
|
+
engine: id,
|
|
17045
|
+
fingerprint: fingerprint(id, f.fingerprint),
|
|
17046
|
+
matchKey: fingerprint(id, f.matchKey ?? f.fingerprint)
|
|
17047
|
+
}));
|
|
17048
|
+
const files = list(run.artifacts).filter((a) => list(record2(a).roles).includes("analysisTarget")).map((a) => pathOf(record2(record2(a).location).uri));
|
|
17049
|
+
engines.push({
|
|
17050
|
+
id,
|
|
17051
|
+
version: str(driver.semanticVersion) || str(driver.version) || "unknown",
|
|
17052
|
+
rules,
|
|
17053
|
+
status: "ran",
|
|
17054
|
+
durationMs: 0,
|
|
17055
|
+
files: [...new Set(files)],
|
|
17056
|
+
excluded: [],
|
|
17057
|
+
message: ""
|
|
17058
|
+
});
|
|
17059
|
+
findings.push(...imported);
|
|
17060
|
+
}
|
|
17061
|
+
return { engines, findings };
|
|
17062
|
+
}
|
|
17063
|
+
function xml(input) {
|
|
17064
|
+
input = input.replace(
|
|
17065
|
+
/<!DOCTYPE report PUBLIC "-\/\/JACOCO\/\/DTD Report 1\.1\/\/EN" "report\.dtd">/g,
|
|
17066
|
+
""
|
|
17067
|
+
);
|
|
17068
|
+
if (/<!DOCTYPE|<!ENTITY/i.test(input) || input.length > 2e7 || XMLValidator.validate(input) !== true)
|
|
17069
|
+
throw new Error("Invalid XML report");
|
|
17070
|
+
return record2(
|
|
17071
|
+
new XMLParser({
|
|
17072
|
+
ignoreAttributes: false,
|
|
17073
|
+
attributeNamePrefix: "",
|
|
17074
|
+
processEntities: false
|
|
17075
|
+
}).parse(input)
|
|
17076
|
+
);
|
|
17077
|
+
}
|
|
17078
|
+
function importCoverage(input, format) {
|
|
17079
|
+
const files = /* @__PURE__ */ new Map();
|
|
17080
|
+
function add(path, line, hits) {
|
|
17081
|
+
if (!Number.isInteger(line) || line < 1) throw new Error("Invalid coverage line");
|
|
17082
|
+
const lines = files.get(path) ?? /* @__PURE__ */ new Map();
|
|
17083
|
+
lines.set(line, Math.max(hits, lines.get(line) ?? 0));
|
|
17084
|
+
files.set(path, lines);
|
|
17085
|
+
}
|
|
17086
|
+
if (format === "lcov") {
|
|
17087
|
+
let path = "";
|
|
17088
|
+
for (const row of input.split(/\r?\n/)) {
|
|
17089
|
+
if (row.startsWith("SF:")) path = pathOf(row.slice(3));
|
|
17090
|
+
if (row.startsWith("DA:")) {
|
|
17091
|
+
const [line, hits] = row.slice(3).split(",");
|
|
17092
|
+
if (!path) throw new Error("LCOV record without file");
|
|
17093
|
+
add(path, Number(line), num(hits));
|
|
17094
|
+
}
|
|
17095
|
+
}
|
|
17096
|
+
} else if (format === "cobertura") {
|
|
17097
|
+
const root = record2(xml(input).coverage);
|
|
17098
|
+
for (const p of list(record2(root.packages).package))
|
|
17099
|
+
for (const c of list(record2(record2(p).classes).class)) {
|
|
17100
|
+
const item = record2(c), path = pathOf(item.filename);
|
|
17101
|
+
for (const l of list(record2(item.lines).line))
|
|
17102
|
+
add(path, num(record2(l).number), num(record2(l).hits));
|
|
17103
|
+
}
|
|
17104
|
+
} else {
|
|
17105
|
+
const root = record2(xml(input).report);
|
|
17106
|
+
for (const p of list(root.package))
|
|
17107
|
+
for (const s of list(record2(p).sourcefile)) {
|
|
17108
|
+
const source = record2(s), prefix = str(record2(p).name);
|
|
17109
|
+
const path = pathOf(`${prefix ? prefix + "/" : ""}${str(source.name)}`);
|
|
17110
|
+
for (const l of list(source.line))
|
|
17111
|
+
add(path, num(record2(l).nr), num(record2(l).ci) > 0 ? 1 : 0);
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
if (!files.size) throw new Error("Coverage report contains no files");
|
|
17115
|
+
return {
|
|
17116
|
+
status: "ran",
|
|
17117
|
+
files: [...files].map(([path, lines]) => ({
|
|
17118
|
+
path,
|
|
17119
|
+
lines: [...lines].map(([line, hits]) => ({ line, hits }))
|
|
17120
|
+
}))
|
|
17121
|
+
};
|
|
17122
|
+
}
|
|
17123
|
+
function importJunit(input) {
|
|
17124
|
+
const tree = xml(input);
|
|
17125
|
+
if (!("testsuite" in tree) && !("testsuites" in tree))
|
|
17126
|
+
throw new Error("Expected JUnit testsuite or testsuites");
|
|
17127
|
+
let total = 0, failed = 0, skipped = 0;
|
|
17128
|
+
const failures = [];
|
|
17129
|
+
function visit(node) {
|
|
17130
|
+
const r = record2(node);
|
|
17131
|
+
const before = { total, failed, skipped };
|
|
17132
|
+
for (const test of list(r.testcase)) {
|
|
17133
|
+
total++;
|
|
17134
|
+
const t = record2(test);
|
|
17135
|
+
if ("failure" in t || "error" in t) {
|
|
17136
|
+
failed++;
|
|
17137
|
+
const path = str(t.file) || str(r.file);
|
|
17138
|
+
failures.push({
|
|
17139
|
+
id: fingerprint(
|
|
17140
|
+
str(r.name),
|
|
17141
|
+
str(t.classname),
|
|
17142
|
+
str(t.name),
|
|
17143
|
+
path,
|
|
17144
|
+
String(total)
|
|
17145
|
+
),
|
|
17146
|
+
kind: "error" in t ? "error" : "failure",
|
|
17147
|
+
...path ? {
|
|
17148
|
+
location: {
|
|
17149
|
+
path: pathOf(path),
|
|
17150
|
+
...num(t.line) > 0 ? { line: Math.floor(num(t.line)) } : {}
|
|
17151
|
+
}
|
|
17152
|
+
} : {}
|
|
17153
|
+
});
|
|
17154
|
+
}
|
|
17155
|
+
if ("skipped" in t) skipped++;
|
|
17156
|
+
}
|
|
17157
|
+
for (const s of list(r.testsuite)) visit(s);
|
|
17158
|
+
if ("tests" in r && num(r.tests) !== total - before.total || "failures" in r && num(r.failures) + num(r.errors) !== failed - before.failed || "skipped" in r && num(r.skipped) !== skipped - before.skipped)
|
|
17159
|
+
throw new Error("JUnit totals do not match test cases");
|
|
17160
|
+
}
|
|
17161
|
+
visit(tree.testsuites ?? tree);
|
|
17162
|
+
return {
|
|
17163
|
+
status: "ran",
|
|
17164
|
+
total,
|
|
17165
|
+
failed,
|
|
17166
|
+
skipped,
|
|
17167
|
+
...failures.length ? { failures } : {}
|
|
17168
|
+
};
|
|
17169
|
+
}
|
|
17170
|
+
function toAnalysisSarif(report) {
|
|
17171
|
+
return {
|
|
17172
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
17173
|
+
version: "2.1.0",
|
|
17174
|
+
runs: [
|
|
17175
|
+
{
|
|
17176
|
+
tool: { driver: { name: "VibeCheck", version: "2" } },
|
|
17177
|
+
results: report.findings.filter((f) => f.state !== "resolved").map((f) => ({
|
|
17178
|
+
ruleId: f.rule,
|
|
17179
|
+
level: ["critical", "high"].includes(f.severity) ? "error" : "warning",
|
|
17180
|
+
message: { text: f.title },
|
|
17181
|
+
partialFingerprints: { vibecheck: f.fingerprint },
|
|
17182
|
+
locations: [
|
|
17183
|
+
{
|
|
17184
|
+
physicalLocation: {
|
|
17185
|
+
artifactLocation: { uri: f.location.path },
|
|
17186
|
+
...f.location.line ? { region: { startLine: f.location.line } } : {}
|
|
17187
|
+
}
|
|
17188
|
+
}
|
|
17189
|
+
],
|
|
17190
|
+
...f.flow.length ? {
|
|
17191
|
+
codeFlows: [
|
|
17192
|
+
{
|
|
17193
|
+
threadFlows: [
|
|
17194
|
+
{
|
|
17195
|
+
locations: f.flow.map((l) => ({
|
|
17196
|
+
location: {
|
|
17197
|
+
physicalLocation: {
|
|
17198
|
+
artifactLocation: { uri: l.path },
|
|
17199
|
+
region: { startLine: l.line ?? 1 }
|
|
17200
|
+
}
|
|
17201
|
+
}
|
|
17202
|
+
}))
|
|
17203
|
+
}
|
|
17204
|
+
]
|
|
17205
|
+
}
|
|
17206
|
+
]
|
|
17207
|
+
} : {}
|
|
17208
|
+
}))
|
|
17209
|
+
}
|
|
17210
|
+
]
|
|
17211
|
+
};
|
|
17212
|
+
}
|
|
17213
|
+
|
|
17214
|
+
// ../../packages/analysis/src/rules.ts
|
|
17215
|
+
var RULES_VERSION = "vibecheck-2026.1";
|
|
17216
|
+
var RULES = {
|
|
17217
|
+
rules: [
|
|
17218
|
+
{
|
|
17219
|
+
id: "vc.js.dynamic-eval",
|
|
17220
|
+
languages: ["javascript", "typescript"],
|
|
17221
|
+
severity: "ERROR",
|
|
17222
|
+
message: "Avoid evaluating dynamic code",
|
|
17223
|
+
pattern: "eval($X)"
|
|
17224
|
+
},
|
|
17225
|
+
{
|
|
17226
|
+
id: "vc.python.shell",
|
|
17227
|
+
languages: ["python"],
|
|
17228
|
+
severity: "ERROR",
|
|
17229
|
+
message: "Avoid shell execution",
|
|
17230
|
+
pattern: "subprocess.run(..., shell=True, ...)"
|
|
17231
|
+
},
|
|
17232
|
+
{
|
|
17233
|
+
id: "vc.go.insecure-tls",
|
|
17234
|
+
languages: ["go"],
|
|
17235
|
+
severity: "ERROR",
|
|
17236
|
+
message: "Verify TLS certificates",
|
|
17237
|
+
pattern: "tls.Config{..., InsecureSkipVerify: true, ...}"
|
|
17238
|
+
},
|
|
17239
|
+
{
|
|
17240
|
+
id: "vc.java.weak-hash",
|
|
17241
|
+
languages: ["java"],
|
|
17242
|
+
severity: "ERROR",
|
|
17243
|
+
message: "Replace weak digest",
|
|
17244
|
+
pattern: 'MessageDigest.getInstance("MD5")'
|
|
17245
|
+
},
|
|
17246
|
+
{
|
|
17247
|
+
id: "vc.csharp.weak-hash",
|
|
17248
|
+
languages: ["csharp"],
|
|
17249
|
+
severity: "ERROR",
|
|
17250
|
+
message: "Replace weak digest",
|
|
17251
|
+
pattern: "MD5.Create()"
|
|
17252
|
+
},
|
|
17253
|
+
{
|
|
17254
|
+
id: "vc.js.empty-catch",
|
|
17255
|
+
languages: ["javascript", "typescript"],
|
|
17256
|
+
severity: "WARNING",
|
|
17257
|
+
metadata: { category: "quality" },
|
|
17258
|
+
message: "Handle or explain the error",
|
|
17259
|
+
pattern: "try { ... } catch ($E) { }"
|
|
17260
|
+
}
|
|
17261
|
+
]
|
|
17262
|
+
};
|
|
17263
|
+
|
|
17264
|
+
// ../../packages/analysis/src/dependencies.ts
|
|
17265
|
+
import { dirname, join } from "path";
|
|
17266
|
+
import { readFileSync } from "fs";
|
|
17267
|
+
function classifyDependencies(findings, root) {
|
|
17268
|
+
const cache = /* @__PURE__ */ new Map();
|
|
17269
|
+
for (const finding2 of findings) {
|
|
17270
|
+
const pkg = finding2.package;
|
|
17271
|
+
if (!pkg) continue;
|
|
17272
|
+
const path = finding2.location.path;
|
|
17273
|
+
if (!path.endsWith("package-lock.json") && !path.endsWith("packages.lock.json"))
|
|
17274
|
+
continue;
|
|
17275
|
+
let lock = cache.get(path);
|
|
17276
|
+
if (!lock) {
|
|
17277
|
+
try {
|
|
17278
|
+
lock = record2(JSON.parse(readFileSync(join(root, path), "utf8")));
|
|
17279
|
+
} catch {
|
|
17280
|
+
continue;
|
|
17281
|
+
}
|
|
17282
|
+
cache.set(path, lock);
|
|
17283
|
+
}
|
|
17284
|
+
if (path.endsWith("packages.lock.json")) {
|
|
17285
|
+
const entries = Object.values(record2(lock.dependencies)).map((target) => record2(record2(target)[pkg.name])).filter((entry) => str(entry.resolved) === pkg.version);
|
|
17286
|
+
if (entries.some((entry) => entry.type === "Direct")) pkg.relationship = "direct";
|
|
17287
|
+
else if (entries.length && entries.every((entry) => entry.type === "Transitive"))
|
|
17288
|
+
pkg.relationship = "transitive";
|
|
17289
|
+
continue;
|
|
17290
|
+
}
|
|
17291
|
+
const packages = record2(lock.packages);
|
|
17292
|
+
if (!("" in packages)) continue;
|
|
17293
|
+
const manifests = Object.entries(packages).filter(
|
|
17294
|
+
([name]) => !name.includes("node_modules")
|
|
17295
|
+
);
|
|
17296
|
+
const direct = manifests.some(([workspace, raw]) => {
|
|
17297
|
+
const manifest = record2(raw);
|
|
17298
|
+
const declared = [
|
|
17299
|
+
"dependencies",
|
|
17300
|
+
"devDependencies",
|
|
17301
|
+
"optionalDependencies",
|
|
17302
|
+
"peerDependencies"
|
|
17303
|
+
].some((key) => pkg.name in record2(manifest[key]));
|
|
17304
|
+
if (!declared) return false;
|
|
17305
|
+
let directory = workspace;
|
|
17306
|
+
while (true) {
|
|
17307
|
+
const resolved = record2(
|
|
17308
|
+
packages[`${directory ? directory + "/" : ""}node_modules/${pkg.name}`]
|
|
17309
|
+
);
|
|
17310
|
+
if (str(resolved.version) === pkg.version) return true;
|
|
17311
|
+
if (!directory || directory === ".") return false;
|
|
17312
|
+
directory = dirname(directory);
|
|
17313
|
+
if (directory === ".") directory = "";
|
|
17314
|
+
}
|
|
17315
|
+
});
|
|
17316
|
+
pkg.relationship = direct ? "direct" : "transitive";
|
|
17317
|
+
}
|
|
17318
|
+
}
|
|
17319
|
+
|
|
17320
|
+
// ../../packages/analysis/src/index.ts
|
|
17321
|
+
var ENGINE_VERSIONS = {
|
|
17322
|
+
semgrep: "1.176.0",
|
|
17323
|
+
osv: "2.5.1",
|
|
17324
|
+
gitleaks: "8.30.1"
|
|
17325
|
+
};
|
|
17326
|
+
var SKIP = /* @__PURE__ */ new Set([
|
|
17327
|
+
".git",
|
|
17328
|
+
".next",
|
|
17329
|
+
".vibecheck",
|
|
17330
|
+
".tools",
|
|
17331
|
+
"__pycache__",
|
|
17332
|
+
"node_modules",
|
|
17333
|
+
"dist",
|
|
17334
|
+
"build",
|
|
17335
|
+
"coverage",
|
|
17336
|
+
".venv",
|
|
17337
|
+
"vendor",
|
|
17338
|
+
"bin",
|
|
17339
|
+
"obj"
|
|
17340
|
+
]);
|
|
17341
|
+
function inventory(root) {
|
|
17342
|
+
const out = [];
|
|
17343
|
+
function visit(dir) {
|
|
17344
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
17345
|
+
if (SKIP.has(e.name) || e.isSymbolicLink()) continue;
|
|
17346
|
+
const path = join2(dir, e.name);
|
|
17347
|
+
if (e.isDirectory()) visit(path);
|
|
17348
|
+
else if (e.isFile()) {
|
|
17349
|
+
if (out.length >= 1e5) throw new Error("Repository exceeds 100000 files");
|
|
17350
|
+
out.push(relative(root, path).split(sep).join("/"));
|
|
17351
|
+
}
|
|
17352
|
+
}
|
|
17353
|
+
}
|
|
17354
|
+
visit(root);
|
|
17355
|
+
return out.sort();
|
|
17356
|
+
}
|
|
17357
|
+
function localFile(root, path) {
|
|
17358
|
+
const full = resolve(root, path), rel = relative(root, full);
|
|
17359
|
+
if (rel.startsWith("..") || rel.startsWith(sep))
|
|
17360
|
+
throw new Error("Report is outside repository");
|
|
17361
|
+
let cursor = root;
|
|
17362
|
+
for (const part of rel.split(sep)) {
|
|
17363
|
+
cursor = join2(cursor, part);
|
|
17364
|
+
if (lstatSync(cursor).isSymbolicLink())
|
|
17365
|
+
throw new Error("Symlink report is not allowed");
|
|
17366
|
+
}
|
|
17367
|
+
if (lstatSync(full).size > 2e7) throw new Error("Report exceeds 20 MB");
|
|
17368
|
+
return readFileSync2(full, "utf8");
|
|
17369
|
+
}
|
|
17370
|
+
function parseDiff(diff) {
|
|
17371
|
+
const changes = [];
|
|
17372
|
+
let current;
|
|
17373
|
+
let previousPath;
|
|
17374
|
+
for (const line of diff.split("\n")) {
|
|
17375
|
+
if (line.startsWith("diff --git ")) {
|
|
17376
|
+
current = void 0;
|
|
17377
|
+
previousPath = void 0;
|
|
17378
|
+
}
|
|
17379
|
+
if (line.startsWith("rename from ")) previousPath = line.slice(12);
|
|
17380
|
+
if (line.startsWith("rename to ")) {
|
|
17381
|
+
current = { path: line.slice(10), previousPath, lines: [] };
|
|
17382
|
+
changes.push(current);
|
|
17383
|
+
}
|
|
17384
|
+
if (line.startsWith("+++ b/")) {
|
|
17385
|
+
if (current?.path !== line.slice(6)) {
|
|
17386
|
+
current = { path: line.slice(6), lines: [] };
|
|
17387
|
+
changes.push(current);
|
|
17388
|
+
}
|
|
17389
|
+
}
|
|
17390
|
+
const match = /^@@ .* \+(\d+)(?:,(\d+))? @@/.exec(line);
|
|
17391
|
+
if (match && current) {
|
|
17392
|
+
const start = Number(match[1]), count = match[2] === void 0 ? 1 : Number(match[2]);
|
|
17393
|
+
for (let i = 0; i < count; i++) current.lines.push(start + i);
|
|
17394
|
+
}
|
|
17395
|
+
}
|
|
17396
|
+
return changes;
|
|
17397
|
+
}
|
|
17398
|
+
async function analyze(options) {
|
|
17399
|
+
const root = resolve(options.root), files = inventory(root), reference = options.reference ?? "main", work = mkdtempSync(join2(tmpdir(), "vibecheck-analysis-"));
|
|
17400
|
+
const git = async (args) => execute(
|
|
17401
|
+
"git",
|
|
17402
|
+
["-c", "core.fsmonitor=false", "-c", `safe.directory=${root}`, ...args],
|
|
17403
|
+
root,
|
|
17404
|
+
1e4
|
|
17405
|
+
);
|
|
17406
|
+
const [head, branch, base] = await Promise.all([
|
|
17407
|
+
git(["rev-parse", "HEAD"]),
|
|
17408
|
+
git(["branch", "--show-current"]),
|
|
17409
|
+
git(["merge-base", "HEAD", reference])
|
|
17410
|
+
]);
|
|
17411
|
+
const sha = (s) => /^[a-f0-9]{40,64}$/.test(s.trim()) ? s.trim() : null;
|
|
17412
|
+
const mergeBase = sha(base.stdout);
|
|
17413
|
+
const diff = mergeBase ? await git([
|
|
17414
|
+
"-c",
|
|
17415
|
+
"core.quotePath=false",
|
|
17416
|
+
"diff",
|
|
17417
|
+
"--no-ext-diff",
|
|
17418
|
+
"--no-textconv",
|
|
17419
|
+
"--unified=0",
|
|
17420
|
+
"--find-renames",
|
|
17421
|
+
mergeBase,
|
|
17422
|
+
"--"
|
|
17423
|
+
]) : null;
|
|
17424
|
+
const report = {
|
|
17425
|
+
version: 2,
|
|
17426
|
+
projectId: options.projectId,
|
|
17427
|
+
runId: randomUUID(),
|
|
17428
|
+
provenance: "local-ci",
|
|
17429
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17430
|
+
commit: sha(head.stdout),
|
|
17431
|
+
branch: branch.stdout.trim() || "detached",
|
|
17432
|
+
reference,
|
|
17433
|
+
mergeBase,
|
|
17434
|
+
baselineRunId: options.baseline?.runId ?? null,
|
|
17435
|
+
scope: fingerprint([...SKIP].join(",")),
|
|
17436
|
+
engines: [],
|
|
17437
|
+
findings: [],
|
|
17438
|
+
coverage: { status: "missing", files: [] },
|
|
17439
|
+
tests: { status: "missing", total: 0, failed: 0, skipped: 0 },
|
|
17440
|
+
changes: diff?.code === 0 ? parseDiff(diff.stdout) : [],
|
|
17441
|
+
policy: DEFAULT_ANALYSIS_POLICY,
|
|
17442
|
+
conditions: [],
|
|
17443
|
+
status: "inconclusive"
|
|
17444
|
+
};
|
|
17445
|
+
if (diff && diff.code !== 0) report.mergeBase = null;
|
|
17446
|
+
const [untracked, dirty] = await Promise.all([
|
|
17447
|
+
git(["ls-files", "--others", "--exclude-standard", "-z"]),
|
|
17448
|
+
git(["status", "--porcelain", "-z"])
|
|
17449
|
+
]);
|
|
17450
|
+
if (dirty.stdout.length) report.commit = null;
|
|
17451
|
+
for (const path of untracked.stdout.split("\0").filter((p) => files.includes(p) && /\.(tsx?|jsx?|py|go|java|cs)$/.test(p))) {
|
|
17452
|
+
const count = localFile(root, path).split("\n").length;
|
|
17453
|
+
report.changes.push({ path, lines: Array.from({ length: count }, (_, i) => i + 1) });
|
|
17454
|
+
}
|
|
17455
|
+
try {
|
|
17456
|
+
const rules = join2(work, "rules.json");
|
|
17457
|
+
writeFileSync(rules, JSON.stringify(RULES));
|
|
17458
|
+
const snapshot = join2(work, "repository");
|
|
17459
|
+
mkdirSync(snapshot);
|
|
17460
|
+
const omitted = files.filter(
|
|
17461
|
+
(f) => lstatSync(join2(root, f)).size > 1e6 || /(^|\/)(\.gitleaks\.toml|\.gitleaksignore|\.semgrepignore)$/.test(f)
|
|
17462
|
+
);
|
|
17463
|
+
const selectedFiles = files.filter((f) => !omitted.includes(f));
|
|
17464
|
+
for (const path of selectedFiles) {
|
|
17465
|
+
const dest = join2(snapshot, path);
|
|
17466
|
+
mkdirSync(join2(dest, ".."), { recursive: true });
|
|
17467
|
+
copyFileSync(join2(root, path), dest);
|
|
17468
|
+
}
|
|
17469
|
+
const sourceFiles = selectedFiles.filter(
|
|
17470
|
+
(f) => /\.(tsx?|jsx?|py|go|java|cs)$/.test(f)
|
|
17471
|
+
);
|
|
17472
|
+
const locks = selectedFiles.filter(
|
|
17473
|
+
(f) => /(^|\/)(pnpm-lock.yaml|package-lock.json|yarn.lock|poetry.lock|uv.lock|requirements.txt|go.sum|pom.xml|gradle.lockfile|packages.lock.json)$/.test(
|
|
17474
|
+
f
|
|
17475
|
+
)
|
|
17476
|
+
);
|
|
17477
|
+
const outputs = [
|
|
17478
|
+
{
|
|
17479
|
+
id: "semgrep",
|
|
17480
|
+
binary: "semgrep",
|
|
17481
|
+
args: [
|
|
17482
|
+
"scan",
|
|
17483
|
+
"--config",
|
|
17484
|
+
rules,
|
|
17485
|
+
"--json",
|
|
17486
|
+
"--metrics=off",
|
|
17487
|
+
"--disable-version-check",
|
|
17488
|
+
"--disable-nosem",
|
|
17489
|
+
"--max-target-bytes",
|
|
17490
|
+
"1000000",
|
|
17491
|
+
...[...SKIP].flatMap((s) => ["--exclude", s]),
|
|
17492
|
+
"."
|
|
17493
|
+
],
|
|
17494
|
+
files: sourceFiles,
|
|
17495
|
+
parser: semgrep
|
|
17496
|
+
},
|
|
17497
|
+
{
|
|
17498
|
+
id: "osv",
|
|
17499
|
+
binary: "osv-scanner",
|
|
17500
|
+
args: ["scan", "source", "--format=json", ...locks.map((f) => "--lockfile=" + f)],
|
|
17501
|
+
files: locks,
|
|
17502
|
+
parser: osv
|
|
17503
|
+
},
|
|
17504
|
+
{
|
|
17505
|
+
id: "gitleaks",
|
|
17506
|
+
binary: "gitleaks",
|
|
17507
|
+
args: [
|
|
17508
|
+
"dir",
|
|
17509
|
+
".",
|
|
17510
|
+
"--no-banner",
|
|
17511
|
+
"--report-format",
|
|
17512
|
+
"json",
|
|
17513
|
+
"--report-path",
|
|
17514
|
+
join2(work, "gitleaks.json")
|
|
17515
|
+
],
|
|
17516
|
+
files: selectedFiles,
|
|
17517
|
+
parser: gitleaks
|
|
17518
|
+
}
|
|
17519
|
+
];
|
|
17520
|
+
for (const spec of outputs) {
|
|
17521
|
+
const engine = {
|
|
17522
|
+
id: spec.id,
|
|
17523
|
+
version: ENGINE_VERSIONS[spec.id],
|
|
17524
|
+
rules: spec.id === "semgrep" ? RULES_VERSION : "bundled",
|
|
17525
|
+
status: "not-applicable",
|
|
17526
|
+
durationMs: 0,
|
|
17527
|
+
files: [],
|
|
17528
|
+
excluded: omitted,
|
|
17529
|
+
message: ""
|
|
17530
|
+
};
|
|
17531
|
+
report.engines.push(engine);
|
|
17532
|
+
if (!spec.files.length) continue;
|
|
17533
|
+
const started = Date.now();
|
|
17534
|
+
const actual = await execute(
|
|
17535
|
+
spec.binary,
|
|
17536
|
+
[spec.id === "gitleaks" ? "version" : "--version"],
|
|
17537
|
+
root,
|
|
17538
|
+
1e4
|
|
17539
|
+
);
|
|
17540
|
+
if (actual.unavailable) {
|
|
17541
|
+
engine.status = "unavailable";
|
|
17542
|
+
engine.message = "Install the pinned engine version";
|
|
17543
|
+
continue;
|
|
17544
|
+
}
|
|
17545
|
+
if (actual.code !== 0 || !actual.stdout.split(/\s+/).includes(ENGINE_VERSIONS[spec.id])) {
|
|
17546
|
+
engine.status = "unavailable";
|
|
17547
|
+
engine.message = "Engine version differs from the pinned version";
|
|
17548
|
+
continue;
|
|
17549
|
+
}
|
|
17550
|
+
const result = await execute(spec.binary, spec.args, snapshot);
|
|
17551
|
+
engine.durationMs = Date.now() - started;
|
|
17552
|
+
if (![0, 1].includes(result.code)) {
|
|
17553
|
+
engine.status = "failed";
|
|
17554
|
+
engine.message = "Engine did not finish successfully";
|
|
17555
|
+
continue;
|
|
17556
|
+
}
|
|
17557
|
+
try {
|
|
17558
|
+
const raw = JSON.parse(
|
|
17559
|
+
spec.id === "gitleaks" ? readFileSync2(join2(work, "gitleaks.json"), "utf8") : result.stdout
|
|
17560
|
+
);
|
|
17561
|
+
if (spec.id === "osv")
|
|
17562
|
+
for (const item of list(record2(raw).results)) {
|
|
17563
|
+
const source = record2(record2(item).source);
|
|
17564
|
+
if (str(source.path).startsWith(snapshot + sep))
|
|
17565
|
+
source.path = relative(snapshot, str(source.path)).split(sep).join("/");
|
|
17566
|
+
}
|
|
17567
|
+
const imported = spec.parser(raw);
|
|
17568
|
+
if (spec.id === "osv") classifyDependencies(imported, snapshot);
|
|
17569
|
+
report.findings.push(...imported);
|
|
17570
|
+
engine.status = "ran";
|
|
17571
|
+
engine.files = spec.files;
|
|
17572
|
+
if (spec.id === "semgrep") {
|
|
17573
|
+
const paths = record2(record2(raw).paths);
|
|
17574
|
+
if (Array.isArray(paths.scanned))
|
|
17575
|
+
engine.files = paths.scanned.map((p) => str(p).replace(/^\.\//, ""));
|
|
17576
|
+
engine.excluded = [
|
|
17577
|
+
...omitted,
|
|
17578
|
+
...spec.files.filter((p) => !engine.files.includes(p))
|
|
17579
|
+
];
|
|
17580
|
+
}
|
|
17581
|
+
} catch {
|
|
17582
|
+
engine.status = "failed";
|
|
17583
|
+
engine.message = "Engine output was incomplete or invalid";
|
|
17584
|
+
}
|
|
17585
|
+
}
|
|
17586
|
+
const builtin = {
|
|
17587
|
+
id: "builtin",
|
|
17588
|
+
version: "2",
|
|
17589
|
+
rules: RULES_VERSION,
|
|
17590
|
+
status: "ran",
|
|
17591
|
+
durationMs: 0,
|
|
17592
|
+
files: [],
|
|
17593
|
+
excluded: [],
|
|
17594
|
+
message: ""
|
|
17595
|
+
};
|
|
17596
|
+
report.engines.push(builtin);
|
|
17597
|
+
try {
|
|
17598
|
+
const eligible = files.filter((f) => /\.(tsx?|jsx?|sql)$/.test(f));
|
|
17599
|
+
const selected = eligible.filter((f) => lstatSync(join2(root, f)).size <= 512e3);
|
|
17600
|
+
builtin.files = selected;
|
|
17601
|
+
builtin.excluded = eligible.filter((f) => !selected.includes(f));
|
|
17602
|
+
const tracked = await git(["ls-files", "-z"]);
|
|
17603
|
+
const names = new Set(tracked.code === 0 ? tracked.stdout.split("\0") : []);
|
|
17604
|
+
const found = runCodeChecks(
|
|
17605
|
+
{
|
|
17606
|
+
files: selected.map((path) => ({ path, content: localFile(root, path) })),
|
|
17607
|
+
tracked: (p) => names.has(p)
|
|
17608
|
+
},
|
|
17609
|
+
void 0,
|
|
17610
|
+
{ failOnError: true }
|
|
17611
|
+
);
|
|
17612
|
+
report.findings.push(
|
|
17613
|
+
...found.map((f) => {
|
|
17614
|
+
const r = finding("builtin", f.checkId, f.path, 0, f.checkId);
|
|
17615
|
+
r.severity = f.severity === "fatal" ? "critical" : f.severity;
|
|
17616
|
+
return r;
|
|
17617
|
+
})
|
|
17618
|
+
);
|
|
17619
|
+
} catch {
|
|
17620
|
+
builtin.status = "failed";
|
|
17621
|
+
builtin.message = "Built-in analysis failed";
|
|
17622
|
+
}
|
|
17623
|
+
for (const path of options.sarif ?? [])
|
|
17624
|
+
try {
|
|
17625
|
+
const imported = importSarif(JSON.parse(localFile(root, path)), path);
|
|
17626
|
+
report.findings.push(...imported.findings);
|
|
17627
|
+
report.engines.push(...imported.engines);
|
|
17628
|
+
} catch {
|
|
17629
|
+
report.engines.push({
|
|
17630
|
+
id: "sarif:" + fingerprint(path).slice(0, 24),
|
|
17631
|
+
version: "2.1.0",
|
|
17632
|
+
rules: "unknown",
|
|
17633
|
+
status: "failed",
|
|
17634
|
+
durationMs: 0,
|
|
17635
|
+
files: [],
|
|
17636
|
+
excluded: [],
|
|
17637
|
+
message: "Invalid SARIF"
|
|
17638
|
+
});
|
|
17639
|
+
}
|
|
17640
|
+
for (const item of options.coverage ?? [])
|
|
17641
|
+
try {
|
|
17642
|
+
const parsed = importCoverage(localFile(root, item.path), item.format);
|
|
17643
|
+
for (const file2 of parsed.files) {
|
|
17644
|
+
if (item.sourceRoot)
|
|
17645
|
+
file2.path = pathOf(item.sourceRoot.replace(/\/$/, "") + "/" + file2.path);
|
|
17646
|
+
const existing = report.coverage.files.find((f) => f.path === file2.path);
|
|
17647
|
+
if (!existing) report.coverage.files.push(file2);
|
|
17648
|
+
else {
|
|
17649
|
+
const lines = new Map(existing.lines.map((l) => [l.line, l.hits]));
|
|
17650
|
+
for (const line of file2.lines)
|
|
17651
|
+
lines.set(line.line, Math.max(lines.get(line.line) ?? 0, line.hits));
|
|
17652
|
+
existing.lines = [...lines].map(([line, hits]) => ({ line, hits }));
|
|
17653
|
+
}
|
|
17654
|
+
}
|
|
17655
|
+
if (report.coverage.status !== "failed") report.coverage.status = "ran";
|
|
17656
|
+
} catch {
|
|
17657
|
+
report.coverage.status = "failed";
|
|
17658
|
+
}
|
|
17659
|
+
for (const path of options.junit ?? [])
|
|
17660
|
+
try {
|
|
17661
|
+
const parsed = importJunit(localFile(root, path));
|
|
17662
|
+
if (report.tests.status !== "failed") report.tests.status = "ran";
|
|
17663
|
+
report.tests.total += parsed.total;
|
|
17664
|
+
report.tests.failed += parsed.failed;
|
|
17665
|
+
report.tests.skipped += parsed.skipped;
|
|
17666
|
+
if (parsed.failures?.length)
|
|
17667
|
+
report.tests.failures = [...report.tests.failures ?? [], ...parsed.failures];
|
|
17668
|
+
} catch {
|
|
17669
|
+
report.tests.status = "failed";
|
|
17670
|
+
}
|
|
17671
|
+
report.findings = [
|
|
17672
|
+
...new Map(report.findings.map((f) => [f.fingerprint, f])).values()
|
|
17673
|
+
];
|
|
17674
|
+
return analysisReportSchema.parse(evaluateAnalysis(report, options.baseline ?? null));
|
|
17675
|
+
} finally {
|
|
17676
|
+
rmSync(work, { recursive: true, force: true });
|
|
17677
|
+
}
|
|
17678
|
+
}
|
|
17679
|
+
|
|
17680
|
+
// src/analyze.ts
|
|
17681
|
+
var configSchema = external_exports.object({
|
|
17682
|
+
version: external_exports.literal(2).default(2),
|
|
17683
|
+
projectId: external_exports.string().uuid(),
|
|
17684
|
+
reference: external_exports.string().regex(/^[\w./-]{1,100}$/).default("main"),
|
|
17685
|
+
baseline: external_exports.string().optional(),
|
|
17686
|
+
coverage: external_exports.array(
|
|
17687
|
+
external_exports.object({
|
|
17688
|
+
path: external_exports.string(),
|
|
17689
|
+
format: external_exports.enum(["lcov", "cobertura", "jacoco"]),
|
|
17690
|
+
sourceRoot: external_exports.string().optional()
|
|
17691
|
+
})
|
|
17692
|
+
).default([]),
|
|
17693
|
+
junit: external_exports.array(external_exports.string()).default([]),
|
|
17694
|
+
sarif: external_exports.array(external_exports.string()).default([])
|
|
17695
|
+
}).strict();
|
|
17696
|
+
async function analyzeCommand(argv) {
|
|
17697
|
+
if (argv.includes("--help")) {
|
|
17698
|
+
console.warn(
|
|
17699
|
+
"vibecheck analyze [--dir <repo>] [--config vibecheck.config.json] [--publish]\n\n1. Create a project and copy its configuration from the web Settings.\n2. Install the pinned engines and configure your existing coverage/JUnit/SARIF reports.\n3. Run analyze locally; add --publish with VIBECHECK_PROJECT_TOKEN to upload results.\n\nSetup / Como usar: https://vibecheck.kinkai.cloud/projects/help\nWrites .vibecheck/analysis.{json,sarif}. No arbitrary test commands are executed.\nQuality gate exit: 0 pass, 1 regression, 2 incomplete, 3 operational error.\nPublish the reference branch first; missing comparable evidence means incomplete, not pass."
|
|
17700
|
+
);
|
|
17701
|
+
return 0;
|
|
17702
|
+
}
|
|
17703
|
+
const value = (key) => {
|
|
17704
|
+
const i = argv.indexOf(key);
|
|
17705
|
+
return i < 0 ? void 0 : argv[i + 1];
|
|
17706
|
+
};
|
|
17707
|
+
const root = resolve2(value("--dir") ?? process.cwd()), configPath = value("--config") ?? "vibecheck.config.json";
|
|
17708
|
+
if (!existsSync(join3(root, configPath)))
|
|
17709
|
+
throw new Error(
|
|
17710
|
+
"Create vibecheck.config.json with version: 2 and projectId from the web project."
|
|
17711
|
+
);
|
|
17712
|
+
const config2 = configSchema.parse(JSON.parse(localFile(root, configPath)));
|
|
17713
|
+
let baseline = config2.baseline ? analysisReportSchema.parse(JSON.parse(localFile(root, config2.baseline))) : null;
|
|
17714
|
+
let report = await analyze({ root, ...config2, baseline });
|
|
17715
|
+
const token = process.env["VIBECHECK_PROJECT_TOKEN"], base = process.env["VIBECHECK_URL"] ?? "https://vibecheck.kinkai.cloud";
|
|
17716
|
+
const url2 = new URL(base);
|
|
17717
|
+
if (url2.protocol !== "https:" && !["localhost", "127.0.0.1"].includes(url2.hostname))
|
|
17718
|
+
throw new Error("Use HTTPS for project uploads");
|
|
17719
|
+
const api = async (path, method = "GET", body) => {
|
|
17720
|
+
const result = await fetch(new URL("/api/projects/" + config2.projectId + path, url2), {
|
|
17721
|
+
method,
|
|
17722
|
+
redirect: "error",
|
|
17723
|
+
headers: { authorization: "Bearer " + token, "content-type": "application/json" },
|
|
17724
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
17725
|
+
signal: AbortSignal.timeout(3e4)
|
|
17726
|
+
});
|
|
17727
|
+
if (!result.ok) throw new Error("Project API rejected request: " + result.status);
|
|
17728
|
+
return result.json();
|
|
17729
|
+
};
|
|
17730
|
+
if (argv.includes("--publish")) {
|
|
17731
|
+
if (!token) throw new Error("VIBECHECK_PROJECT_TOKEN is required to publish");
|
|
17732
|
+
const project = external_exports.object({ policy: policySchema, reference: external_exports.string() }).parse(await api(""));
|
|
17733
|
+
if (project.reference !== config2.reference)
|
|
17734
|
+
throw new Error("Config reference must match the project reference branch");
|
|
17735
|
+
if (!baseline && report.mergeBase) {
|
|
17736
|
+
const runs = external_exports.array(external_exports.object({ id: external_exports.string().uuid() })).parse(await api("/runs?commit=" + report.mergeBase));
|
|
17737
|
+
if (runs[0]) baseline = analysisReportSchema.parse(await api("/runs/" + runs[0].id));
|
|
17738
|
+
}
|
|
17739
|
+
report = evaluateAnalysis(
|
|
17740
|
+
{ ...report, baselineRunId: baseline?.runId ?? null },
|
|
17741
|
+
baseline,
|
|
17742
|
+
project.policy
|
|
17743
|
+
);
|
|
17744
|
+
}
|
|
17745
|
+
const dir = join3(root, ".vibecheck");
|
|
17746
|
+
if (existsSync(dir) && lstatSync2(dir).isSymbolicLink())
|
|
17747
|
+
throw new Error("Unsafe artifact directory");
|
|
17748
|
+
mkdirSync2(dir, { recursive: true });
|
|
17749
|
+
const save = (r) => {
|
|
17750
|
+
for (const name of ["analysis.json", "analysis.sarif"]) {
|
|
17751
|
+
const target = join3(dir, name);
|
|
17752
|
+
if (existsSync(target) && (lstatSync2(target).isSymbolicLink() || !lstatSync2(target).isFile()))
|
|
17753
|
+
throw new Error("Unsafe artifact file");
|
|
17754
|
+
}
|
|
17755
|
+
writeFileSync2(join3(dir, "analysis.json"), JSON.stringify(r, null, 2) + "\n");
|
|
17756
|
+
writeFileSync2(
|
|
17757
|
+
join3(dir, "analysis.sarif"),
|
|
17758
|
+
JSON.stringify(toAnalysisSarif(r), null, 2) + "\n"
|
|
17759
|
+
);
|
|
17760
|
+
};
|
|
17761
|
+
save(report);
|
|
17762
|
+
if (argv.includes("--publish")) {
|
|
17763
|
+
const serialized = JSON.stringify(report), parts = serialized.match(/[\s\S]{1,100000}/g) ?? [];
|
|
17764
|
+
await api("/runs", "POST", { runId: report.runId, batches: parts.length });
|
|
17765
|
+
for (const [batch, payload] of parts.entries())
|
|
17766
|
+
await api("/runs/" + report.runId + "/batches", "PUT", { batch, payload });
|
|
17767
|
+
await api("/runs/" + report.runId + "/finalize", "POST", {});
|
|
17768
|
+
report = analysisReportSchema.parse(await api("/runs/" + report.runId));
|
|
17769
|
+
save(report);
|
|
17770
|
+
console.warn(new URL("/projects/" + config2.projectId, url2).href);
|
|
17771
|
+
}
|
|
17772
|
+
console.warn(
|
|
17773
|
+
`${report.status}: ${report.findings.filter((f) => f.state === "new").length} new findings`
|
|
17774
|
+
);
|
|
17775
|
+
for (const engine of report.engines)
|
|
17776
|
+
console.warn(`${engine.id} ${engine.version}: ${engine.status}`);
|
|
17777
|
+
return report.status === "passed" ? 0 : report.status === "failed" ? 1 : 2;
|
|
17778
|
+
}
|
|
17779
|
+
|
|
17780
|
+
// ../../packages/checks/src/types.ts
|
|
17781
|
+
var SEVERITIES = ["fatal", "critical", "high", "medium", "low"];
|
|
17782
|
+
var NODE_KINDS = [
|
|
17783
|
+
"browser",
|
|
17784
|
+
"app",
|
|
17785
|
+
"api",
|
|
17786
|
+
"db",
|
|
17787
|
+
"storage",
|
|
17788
|
+
"auth",
|
|
17789
|
+
"ai",
|
|
17790
|
+
"payments",
|
|
17791
|
+
"secrets"
|
|
17792
|
+
];
|
|
17793
|
+
var CATEGORIES = ["secrets", "data", "storage", "auth", "ai", "config"];
|
|
17794
|
+
|
|
17795
|
+
// ../../packages/graph/src/types.ts
|
|
17796
|
+
var EDGE_TYPES = ["imports", "calls", "contains"];
|
|
17797
|
+
var MAX_NODES = 400;
|
|
17798
|
+
var MAX_EDGES = 800;
|
|
17799
|
+
var MAX_COMMUNITIES = 9;
|
|
17800
|
+
var MAX_FILES_PER_NODE = 12;
|
|
17801
|
+
var MAX_COMMUNITY_FILES = 8;
|
|
17802
|
+
var MAX_BYTES = 256e3;
|
|
17803
|
+
var MAX_LABEL = 80;
|
|
17804
|
+
var MAX_ID = 64;
|
|
17805
|
+
var MAX_PATH = 180;
|
|
17806
|
+
var MAX_PACKAGES = 32;
|
|
17807
|
+
var MAX_HINT_ROUTES = 80;
|
|
17808
|
+
var MAX_HINT_TABLES = 40;
|
|
17809
|
+
var MAX_HINT_BUCKETS = 24;
|
|
17810
|
+
var MAX_HINT_COLLECTIONS = 24;
|
|
17811
|
+
var EMPTY_DATA = { tables: [], buckets: [], collections: [] };
|
|
17812
|
+
var pathSchema = external_exports.string().min(1).max(MAX_PATH);
|
|
17813
|
+
var nodeSchema = external_exports.object({
|
|
17814
|
+
id: external_exports.string().min(1).max(MAX_ID),
|
|
17815
|
+
kind: external_exports.enum(NODE_KINDS),
|
|
17816
|
+
label: external_exports.string().min(1).max(MAX_LABEL),
|
|
17817
|
+
files: external_exports.array(pathSchema).max(MAX_FILES_PER_NODE)
|
|
17818
|
+
});
|
|
17819
|
+
var edgeSchema = external_exports.object({
|
|
17820
|
+
from: external_exports.string().min(1).max(MAX_ID),
|
|
17821
|
+
to: external_exports.string().min(1).max(MAX_ID),
|
|
17822
|
+
type: external_exports.enum(EDGE_TYPES)
|
|
17823
|
+
});
|
|
17824
|
+
var communitySchema = external_exports.object({
|
|
17825
|
+
nodeKind: external_exports.enum(NODE_KINDS),
|
|
17826
|
+
label: external_exports.string().min(1).max(MAX_LABEL),
|
|
17827
|
+
files: external_exports.array(pathSchema).max(MAX_COMMUNITY_FILES)
|
|
17828
|
+
});
|
|
17829
|
+
var tableName = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/);
|
|
17830
|
+
var bucketName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/);
|
|
17831
|
+
var dataSchema = external_exports.object({
|
|
17832
|
+
tables: external_exports.array(tableName).max(MAX_HINT_TABLES),
|
|
17833
|
+
buckets: external_exports.array(bucketName).max(MAX_HINT_BUCKETS),
|
|
17834
|
+
collections: external_exports.array(tableName).max(MAX_HINT_COLLECTIONS)
|
|
17835
|
+
});
|
|
17836
|
+
var scanGraphSchema = external_exports.object({
|
|
17837
|
+
version: external_exports.literal(1),
|
|
17838
|
+
advanced: external_exports.boolean(),
|
|
17839
|
+
nodes: external_exports.array(nodeSchema).max(MAX_NODES),
|
|
17840
|
+
edges: external_exports.array(edgeSchema).max(MAX_EDGES),
|
|
17841
|
+
communities: external_exports.array(communitySchema).max(MAX_COMMUNITIES),
|
|
17842
|
+
packages: external_exports.array(external_exports.string().min(1).max(MAX_LABEL)).max(MAX_PACKAGES),
|
|
17843
|
+
data: dataSchema.optional()
|
|
17844
|
+
});
|
|
17845
|
+
var EMPTY_HINTS = {
|
|
17846
|
+
apiRoutes: [],
|
|
17847
|
+
tableNames: [],
|
|
17848
|
+
buckets: [],
|
|
17849
|
+
collections: []
|
|
17850
|
+
};
|
|
17851
|
+
|
|
17852
|
+
// ../../packages/graph/src/hints.ts
|
|
17853
|
+
function uniqueCapped(values, cap) {
|
|
17854
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17855
|
+
const out = [];
|
|
17856
|
+
for (const value of values) {
|
|
17857
|
+
if (out.length >= cap) break;
|
|
17858
|
+
if (seen.has(value)) continue;
|
|
17859
|
+
seen.add(value);
|
|
17860
|
+
out.push(value);
|
|
17861
|
+
}
|
|
17862
|
+
return out;
|
|
17863
|
+
}
|
|
17864
|
+
function routeFromFile(path) {
|
|
17865
|
+
const normal = path.replaceAll("\\", "/");
|
|
17866
|
+
const app = /(?:^|\/)app\/(.+)\/route\.(t|j)sx?$/i.exec(normal);
|
|
17867
|
+
if (app?.[1] !== void 0) {
|
|
17868
|
+
const segments = app[1].split("/").filter((s) => s.length > 0 && !s.startsWith("(") && !s.startsWith("@")).map((s) => s.replace(/^\[+\.\.\.(.+)\]+$/, "*").replace(/^\[+(.+)\]+$/, ":$1"));
|
|
17869
|
+
if (segments.length === 0) return "/api";
|
|
17870
|
+
return `/${segments.join("/")}`;
|
|
17871
|
+
}
|
|
17872
|
+
const pages = /(?:^|\/)pages\/api\/(.+)\.(t|j)sx?$/i.exec(normal);
|
|
17873
|
+
if (pages?.[1] !== void 0) {
|
|
17874
|
+
const withoutIndex = pages[1].replace(/\/index$/i, "");
|
|
17875
|
+
return `/api/${withoutIndex}`;
|
|
17876
|
+
}
|
|
17877
|
+
return null;
|
|
17878
|
+
}
|
|
17879
|
+
function filesFromGraph(graph) {
|
|
17880
|
+
return [
|
|
17881
|
+
...graph.nodes.flatMap((n) => n.files),
|
|
17882
|
+
...graph.communities.flatMap((c) => c.files)
|
|
17883
|
+
];
|
|
17884
|
+
}
|
|
17885
|
+
function hintsFromGraph(graph) {
|
|
17886
|
+
if (graph === null || graph === void 0 || !graph.advanced) return EMPTY_HINTS;
|
|
17887
|
+
const routes = [];
|
|
17888
|
+
for (const file2 of filesFromGraph(graph)) {
|
|
17889
|
+
const route = routeFromFile(file2);
|
|
17890
|
+
if (route !== null) routes.push(route);
|
|
17891
|
+
}
|
|
17892
|
+
routes.sort((a, b) => a.localeCompare(b));
|
|
17893
|
+
const data = graph.data ?? EMPTY_DATA;
|
|
17894
|
+
return {
|
|
17895
|
+
apiRoutes: uniqueCapped(routes, MAX_HINT_ROUTES),
|
|
17896
|
+
tableNames: uniqueCapped(data.tables, MAX_HINT_TABLES),
|
|
17897
|
+
buckets: uniqueCapped(data.buckets, MAX_HINT_BUCKETS),
|
|
17898
|
+
collections: uniqueCapped(data.collections, MAX_HINT_COLLECTIONS)
|
|
17899
|
+
};
|
|
17900
|
+
}
|
|
17901
|
+
|
|
17902
|
+
// ../../packages/graph/src/metrics.ts
|
|
17903
|
+
function importCycles(graph) {
|
|
17904
|
+
const adjacencia = /* @__PURE__ */ new Map();
|
|
17905
|
+
for (const aresta of graph.edges) {
|
|
17906
|
+
if (aresta.type !== "imports") continue;
|
|
17907
|
+
const vizinhos = adjacencia.get(aresta.from) ?? [];
|
|
17908
|
+
vizinhos.push(aresta.to);
|
|
17909
|
+
adjacencia.set(aresta.from, vizinhos);
|
|
17910
|
+
}
|
|
17911
|
+
const indice = /* @__PURE__ */ new Map();
|
|
17912
|
+
const lowlink = /* @__PURE__ */ new Map();
|
|
17913
|
+
const naPilha = /* @__PURE__ */ new Set();
|
|
17914
|
+
const pilha = [];
|
|
17915
|
+
let proximo = 0;
|
|
17916
|
+
let ciclos = 0;
|
|
17917
|
+
for (const raiz of graph.nodes.map((n) => n.id)) {
|
|
17918
|
+
if (indice.has(raiz)) continue;
|
|
17919
|
+
const quadros = [{ no: raiz, vizinho: 0 }];
|
|
17920
|
+
indice.set(raiz, proximo);
|
|
17921
|
+
lowlink.set(raiz, proximo);
|
|
17922
|
+
proximo += 1;
|
|
17923
|
+
pilha.push(raiz);
|
|
17924
|
+
naPilha.add(raiz);
|
|
17925
|
+
while (quadros.length > 0) {
|
|
17926
|
+
const quadro = quadros[quadros.length - 1];
|
|
17927
|
+
if (quadro === void 0) break;
|
|
17928
|
+
const vizinhos = adjacencia.get(quadro.no) ?? [];
|
|
17929
|
+
if (quadro.vizinho < vizinhos.length) {
|
|
17930
|
+
const vizinho = vizinhos[quadro.vizinho];
|
|
17931
|
+
quadro.vizinho += 1;
|
|
17932
|
+
if (!indice.has(vizinho)) {
|
|
17933
|
+
indice.set(vizinho, proximo);
|
|
17934
|
+
lowlink.set(vizinho, proximo);
|
|
17935
|
+
proximo += 1;
|
|
17936
|
+
pilha.push(vizinho);
|
|
17937
|
+
naPilha.add(vizinho);
|
|
17938
|
+
quadros.push({ no: vizinho, vizinho: 0 });
|
|
17939
|
+
} else if (naPilha.has(vizinho)) {
|
|
17940
|
+
lowlink.set(
|
|
17941
|
+
quadro.no,
|
|
17942
|
+
Math.min(lowlink.get(quadro.no) ?? 0, indice.get(vizinho) ?? 0)
|
|
17943
|
+
);
|
|
17944
|
+
}
|
|
17945
|
+
continue;
|
|
17946
|
+
}
|
|
17947
|
+
quadros.pop();
|
|
17948
|
+
const pai = quadros[quadros.length - 1];
|
|
17949
|
+
if (pai !== void 0) {
|
|
17950
|
+
lowlink.set(
|
|
17951
|
+
pai.no,
|
|
17952
|
+
Math.min(lowlink.get(pai.no) ?? 0, lowlink.get(quadro.no) ?? 0)
|
|
17953
|
+
);
|
|
17954
|
+
}
|
|
17955
|
+
if (lowlink.get(quadro.no) === indice.get(quadro.no)) {
|
|
17956
|
+
let tamanho = 0;
|
|
17957
|
+
let atual;
|
|
17958
|
+
do {
|
|
17959
|
+
atual = pilha.pop();
|
|
17960
|
+
if (atual === void 0) break;
|
|
17961
|
+
naPilha.delete(atual);
|
|
17962
|
+
tamanho += 1;
|
|
17963
|
+
} while (atual !== quadro.no);
|
|
17964
|
+
if (tamanho > 1) ciclos += 1;
|
|
17965
|
+
}
|
|
17966
|
+
}
|
|
17967
|
+
}
|
|
17968
|
+
return ciclos;
|
|
17969
|
+
}
|
|
17970
|
+
function graphMetrics(graph) {
|
|
17971
|
+
return {
|
|
17972
|
+
nodes: graph.nodes.length,
|
|
17973
|
+
edges: graph.edges.length,
|
|
17974
|
+
routes: hintsFromGraph(graph).apiRoutes.length,
|
|
17975
|
+
packages: graph.packages.length,
|
|
17976
|
+
cycles: importCycles(graph)
|
|
17977
|
+
};
|
|
17978
|
+
}
|
|
17979
|
+
|
|
17980
|
+
// ../../packages/graph/src/identifiers.ts
|
|
17981
|
+
var FROM_CALL = /([A-Za-z_$][\w$]*)\s*\.\s*from\(\s*["'`]([A-Za-z0-9._-]{1,63})["'`]\s*\)/g;
|
|
17982
|
+
var NOT_A_DATABASE = /* @__PURE__ */ new Set([
|
|
17983
|
+
"array",
|
|
17984
|
+
"buffer",
|
|
17985
|
+
"object",
|
|
17986
|
+
"string",
|
|
17987
|
+
"number",
|
|
17988
|
+
"date",
|
|
14731
17989
|
"map",
|
|
14732
17990
|
"set",
|
|
14733
17991
|
"promise",
|
|
@@ -14837,7 +18095,7 @@ var KIND_LABEL = {
|
|
|
14837
18095
|
payments: "payments",
|
|
14838
18096
|
secrets: "security"
|
|
14839
18097
|
};
|
|
14840
|
-
var
|
|
18098
|
+
var RULES2 = [
|
|
14841
18099
|
{
|
|
14842
18100
|
kind: "api",
|
|
14843
18101
|
test: /app\/.+\/route\.(t|j)sx?\b|pages\/api\//i
|
|
@@ -14854,16 +18112,16 @@ var RULES = [
|
|
|
14854
18112
|
{ kind: "browser", test: /\b(middleware\.(t|j)s|proxy\.(t|j)s)\b/i }
|
|
14855
18113
|
];
|
|
14856
18114
|
function mapKind(signal) {
|
|
14857
|
-
const
|
|
14858
|
-
for (const rule of
|
|
14859
|
-
if (rule.test.test(
|
|
18115
|
+
const text2 = `${signal.label} ${signal.files.join(" ")}`;
|
|
18116
|
+
for (const rule of RULES2) {
|
|
18117
|
+
if (rule.test.test(text2)) return rule.kind;
|
|
14860
18118
|
}
|
|
14861
18119
|
return "app";
|
|
14862
18120
|
}
|
|
14863
18121
|
|
|
14864
18122
|
// ../../packages/graph/src/packages.ts
|
|
14865
18123
|
var SECRETISH2 = /(sk_live_|sk_test_|rk_live_|pk_live_|pk_test_|sb_secret_|eyJ[A-Za-z0-9_-]{20,}|gsk_[A-Za-z0-9]|sk-ant-|sk-proj-|sk-or-v1-)/i;
|
|
14866
|
-
var
|
|
18124
|
+
var RULES3 = [
|
|
14867
18125
|
{ id: "next", test: /^next(?:\/|$)/ },
|
|
14868
18126
|
{ id: "react", test: /^react(?:-dom)?(?:\/|$)/ },
|
|
14869
18127
|
{ id: "vue", test: /^vue(?:\/|$)/ },
|
|
@@ -14903,7 +18161,7 @@ function packageId(spec) {
|
|
|
14903
18161
|
if (cleaned.length === 0 || cleaned.startsWith(".") || cleaned.startsWith("/"))
|
|
14904
18162
|
return null;
|
|
14905
18163
|
if (cleaned.startsWith("node:") || cleaned.startsWith("#")) return null;
|
|
14906
|
-
for (const rule of
|
|
18164
|
+
for (const rule of RULES3) {
|
|
14907
18165
|
if (rule.test.test(cleaned)) return rule.id;
|
|
14908
18166
|
}
|
|
14909
18167
|
return null;
|
|
@@ -15046,9 +18304,9 @@ function firstExport(value) {
|
|
|
15046
18304
|
}
|
|
15047
18305
|
function nodesFromDump(dump) {
|
|
15048
18306
|
const raw = dump["nodes"] ?? dump["nos"];
|
|
15049
|
-
const
|
|
18307
|
+
const list2 = Array.isArray(raw) ? raw : isRecord(raw) ? Object.entries(raw).map(([id, n]) => isRecord(n) ? { id, ...n } : { id }) : [];
|
|
15050
18308
|
const nodes = [];
|
|
15051
|
-
for (const [i, item] of
|
|
18309
|
+
for (const [i, item] of list2.entries()) {
|
|
15052
18310
|
if (!isRecord(item)) continue;
|
|
15053
18311
|
const files = filesFrom(
|
|
15054
18312
|
item["files"] ?? item["files"] ?? item["source_file"] ?? item["sourceFile"] ?? item["file"] ?? item["path"]
|
|
@@ -15070,10 +18328,10 @@ function nodesFromDump(dump) {
|
|
|
15070
18328
|
}
|
|
15071
18329
|
function edgesFromDump(dump, ids) {
|
|
15072
18330
|
const raw = dump["edges"] ?? dump["edges"];
|
|
15073
|
-
const
|
|
18331
|
+
const list2 = Array.isArray(raw) ? raw : [];
|
|
15074
18332
|
const out = [];
|
|
15075
18333
|
const seen = /* @__PURE__ */ new Set();
|
|
15076
|
-
for (const item of
|
|
18334
|
+
for (const item of list2) {
|
|
15077
18335
|
if (!isRecord(item)) continue;
|
|
15078
18336
|
const from = idFrom(item["from"] ?? item["de"] ?? item["source"], "");
|
|
15079
18337
|
const to = idFrom(item["to"] ?? item["para"] ?? item["target"], "");
|
|
@@ -15386,19 +18644,19 @@ var sequencedScanEventSchema = external_exports.intersection(
|
|
|
15386
18644
|
);
|
|
15387
18645
|
|
|
15388
18646
|
// src/consent.ts
|
|
15389
|
-
import { chmodSync as chmodSync2, mkdirSync as
|
|
18647
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
15390
18648
|
import { createInterface } from "readline/promises";
|
|
15391
|
-
import { join as
|
|
18649
|
+
import { join as join5, resolve as resolve3 } from "path";
|
|
15392
18650
|
|
|
15393
18651
|
// src/token.ts
|
|
15394
|
-
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
18652
|
+
import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
15395
18653
|
import { homedir } from "os";
|
|
15396
|
-
import { join } from "path";
|
|
18654
|
+
import { join as join4 } from "path";
|
|
15397
18655
|
function dirConfig() {
|
|
15398
|
-
return
|
|
18656
|
+
return join4(homedir(), ".config", "vibecheck");
|
|
15399
18657
|
}
|
|
15400
18658
|
function caminhoToken() {
|
|
15401
|
-
return
|
|
18659
|
+
return join4(dirConfig(), "token");
|
|
15402
18660
|
}
|
|
15403
18661
|
function lerToken() {
|
|
15404
18662
|
const env = process.env["VIBECHECK_TOKEN"];
|
|
@@ -15407,7 +18665,7 @@ function lerToken() {
|
|
|
15407
18665
|
return { token: env, host: hostEnv ?? "" };
|
|
15408
18666
|
}
|
|
15409
18667
|
try {
|
|
15410
|
-
const bruto =
|
|
18668
|
+
const bruto = readFileSync3(caminhoToken(), "utf8").trim();
|
|
15411
18669
|
const [token, host] = bruto.split("\n");
|
|
15412
18670
|
if (token === void 0 || token.length === 0) return null;
|
|
15413
18671
|
return { token, host: host ?? "" };
|
|
@@ -15417,9 +18675,9 @@ function lerToken() {
|
|
|
15417
18675
|
}
|
|
15418
18676
|
function gravarToken(token, host) {
|
|
15419
18677
|
const dir = dirConfig();
|
|
15420
|
-
|
|
18678
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
15421
18679
|
const destino = caminhoToken();
|
|
15422
|
-
|
|
18680
|
+
writeFileSync3(destino, `${token}
|
|
15423
18681
|
${host}
|
|
15424
18682
|
`, { encoding: "utf8", mode: 384 });
|
|
15425
18683
|
chmodSync(destino, 384);
|
|
@@ -15429,14 +18687,14 @@ ${host}
|
|
|
15429
18687
|
var ARQUIVO = "code-consent.json";
|
|
15430
18688
|
var ENV_CI_ALLOW = "VIBECHECK_ALLOW_CODE_SCAN";
|
|
15431
18689
|
function caminho(deps) {
|
|
15432
|
-
return
|
|
18690
|
+
return join5((deps.configDir ?? dirConfig)(), ARQUIVO);
|
|
15433
18691
|
}
|
|
15434
18692
|
function repoKey(dir) {
|
|
15435
|
-
return
|
|
18693
|
+
return resolve3(dir);
|
|
15436
18694
|
}
|
|
15437
18695
|
function listarConsentimentos(deps = {}) {
|
|
15438
18696
|
try {
|
|
15439
|
-
const bruto = JSON.parse(
|
|
18697
|
+
const bruto = JSON.parse(readFileSync4(caminho(deps), "utf8"));
|
|
15440
18698
|
if (!Array.isArray(bruto)) return [];
|
|
15441
18699
|
return bruto.filter(
|
|
15442
18700
|
(e) => e !== null && typeof e === "object" && typeof e.repo === "string" && typeof e.acceptedAt === "string"
|
|
@@ -15447,9 +18705,9 @@ function listarConsentimentos(deps = {}) {
|
|
|
15447
18705
|
}
|
|
15448
18706
|
function gravar(entradas, deps) {
|
|
15449
18707
|
const dir = (deps.configDir ?? dirConfig)();
|
|
15450
|
-
|
|
15451
|
-
const arquivo =
|
|
15452
|
-
|
|
18708
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
18709
|
+
const arquivo = join5(dir, ARQUIVO);
|
|
18710
|
+
writeFileSync4(arquivo, `${JSON.stringify(entradas, null, 2)}
|
|
15453
18711
|
`, { mode: 384 });
|
|
15454
18712
|
chmodSync2(arquivo, 384);
|
|
15455
18713
|
}
|
|
@@ -15547,227 +18805,26 @@ async function followScan(base, scanId, shareToken, options = {}) {
|
|
|
15547
18805
|
}
|
|
15548
18806
|
|
|
15549
18807
|
// src/gate.ts
|
|
15550
|
-
import { mkdirSync as
|
|
15551
|
-
import { join as
|
|
15552
|
-
|
|
15553
|
-
// ../../packages/checks/src/code/types.ts
|
|
15554
|
-
var API_ROUTE_FILE = /(^|\/)(app\/.*\/route\.(ts|js|tsx|jsx)|pages\/api\/.+\.(ts|js))$/;
|
|
15555
|
-
function isApiRoute(path) {
|
|
15556
|
-
return API_ROUTE_FILE.test(path);
|
|
15557
|
-
}
|
|
15558
|
-
|
|
15559
|
-
// ../../packages/checks/src/code/checks.ts
|
|
15560
|
-
var MIGRATION_FILE = /(^|\/)supabase\/migrations\/[^/]+\.sql$/;
|
|
15561
|
-
var CREATE_TABLE = /create\s+table\s+(?:if\s+not\s+exists\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?/gi;
|
|
15562
|
-
var ENABLE_RLS = /alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?(?:"?[a-z_][a-z0-9_]*"?\.)?"?([a-z_][a-z0-9_]*)"?\s+enable\s+row\s+level\s+security/gi;
|
|
15563
|
-
function all(pattern, text) {
|
|
15564
|
-
const out = [];
|
|
15565
|
-
for (const m of text.matchAll(pattern)) {
|
|
15566
|
-
const nome = m[1];
|
|
15567
|
-
if (nome !== void 0) out.push(nome.toLowerCase());
|
|
15568
|
-
}
|
|
15569
|
-
return out;
|
|
15570
|
-
}
|
|
15571
|
-
function isClientFile(file2) {
|
|
15572
|
-
if (/^\s*['"]use client['"]/.test(file2.content)) return true;
|
|
15573
|
-
if (/\.client\.(tsx|jsx|ts|js)$/.test(file2.path)) return true;
|
|
15574
|
-
return /(^|\/)pages\/(?!api\/).+\.(tsx|jsx|ts|js)$/.test(file2.path);
|
|
15575
|
-
}
|
|
15576
|
-
var TOUCHES_DB = /\b(supabase\.(?:from|rpc|storage)\b|prisma\.\w+\.\w+\(|drizzle\(|\bdb\.(?:select|insert|update|delete|query|execute|from)\(|\.from\(['"]|sql`|pool\.query\(|knex\(|mongoose\.|\.collection\(['"])/;
|
|
15577
|
-
var HAS_AUTH = /\b(getSession|getUser|auth\(\)|currentUser|requireAuth|verifyToken|verifyJwt|jwtVerify|authorization|Bearer|withAuth|clerk|next-auth|getServerSession|session)\b/i;
|
|
15578
|
-
var PUBLIC_ROUTE = /(^|\/)(webhooks?|health|healthz|ping|status|robots|sitemap|og|opengraph|cron)(\/|\.|$)/i;
|
|
15579
|
-
var AI_SDK = /from\s+['"](openai|@anthropic-ai\/sdk|ai|@ai-sdk\/[^'"]+|@google\/generative-ai|groq-sdk|cohere-ai|@mistralai\/mistralai)['"]/;
|
|
15580
|
-
var HAS_LIMITER = /\b(ratelimit|rateLimit|Ratelimit|upstash|limiter|throttle|slidingWindow|tokenBucket)\b/;
|
|
15581
|
-
var STRIPE_IMPORT = /from\s+['"]stripe['"]/;
|
|
15582
|
-
var WEBHOOK_PATH = /webhook/i;
|
|
15583
|
-
var SIGNATURE_CHECK = /constructEvent(?:Async)?\(|stripe-signature/;
|
|
15584
|
-
var SERVICE_ROLE_REF = /\b(SUPABASE_SERVICE_ROLE_KEY|SERVICE_ROLE_KEY|sb_secret_[A-Za-z0-9_]+|NEXT_PUBLIC_[A-Z0-9_]*(?:SECRET|SERVICE_ROLE|PRIVATE)[A-Z0-9_]*)\b/;
|
|
15585
|
-
var ENV_FILES = [
|
|
15586
|
-
".env",
|
|
15587
|
-
".env.local",
|
|
15588
|
-
".env.production",
|
|
15589
|
-
".env.development",
|
|
15590
|
-
".env.staging"
|
|
15591
|
-
];
|
|
15592
|
-
var codeRlsMissing = {
|
|
15593
|
-
id: "code-rls-missing",
|
|
15594
|
-
source: "code",
|
|
15595
|
-
category: "data",
|
|
15596
|
-
severity: "critical",
|
|
15597
|
-
title: "Table created without row level security",
|
|
15598
|
-
impact: "A migration creates a table and never turns on row level security for it. Once it has rows, anyone with the public key can read them \u2014 this is the hole the RLS check lights up from outside.",
|
|
15599
|
-
howToDetect: (ctx) => {
|
|
15600
|
-
const migrations = ctx.files.filter((f) => MIGRATION_FILE.test(f.path));
|
|
15601
|
-
if (migrations.length === 0) return [];
|
|
15602
|
-
const comRls = /* @__PURE__ */ new Set();
|
|
15603
|
-
for (const m of migrations) for (const t of all(ENABLE_RLS, m.content)) comRls.add(t);
|
|
15604
|
-
const out = [];
|
|
15605
|
-
const vistas = /* @__PURE__ */ new Set();
|
|
15606
|
-
for (const m of migrations) {
|
|
15607
|
-
for (const tabela of all(CREATE_TABLE, m.content)) {
|
|
15608
|
-
if (comRls.has(tabela) || vistas.has(tabela)) continue;
|
|
15609
|
-
vistas.add(tabela);
|
|
15610
|
-
out.push({
|
|
15611
|
-
path: m.path,
|
|
15612
|
-
reason: `table \`${tabela}\` is created without enable row level security`
|
|
15613
|
-
});
|
|
15614
|
-
}
|
|
15615
|
-
}
|
|
15616
|
-
return out;
|
|
15617
|
-
},
|
|
15618
|
-
fixPrompt: (a) => `In your code, ${a.reason} (${a.path}).
|
|
15619
|
-
|
|
15620
|
-
We do not know the columns or who should read what. We cannot write the policy from here.
|
|
15621
|
-
|
|
15622
|
-
Good practice: every table starts with row level security on, in the same migration that creates it. Then one policy per real access: a person reads their own rows; a public catalog, if it exists, is a deliberate read policy; writes always go through a session. Do this in a new migration, never by editing one that already ran.
|
|
15623
|
-
|
|
15624
|
-
After, deploy and read the table with the public key and no session \u2014 it must answer empty or denied.`
|
|
15625
|
-
};
|
|
15626
|
-
var codeServiceRoleInClient = {
|
|
15627
|
-
id: "code-service-role-in-client",
|
|
15628
|
-
source: "code",
|
|
15629
|
-
category: "secrets",
|
|
15630
|
-
severity: "fatal",
|
|
15631
|
-
title: "Service key referenced in client code",
|
|
15632
|
-
impact: "A file that runs in the browser mentions the Supabase service key or a NEXT_PUBLIC_ secret. Whatever the bundler inlines there ships to every visitor \u2014 this is the fatal finding from outside, one build away.",
|
|
15633
|
-
howToDetect: (ctx) => ctx.files.filter(isClientFile).filter((f) => SERVICE_ROLE_REF.test(f.content)).map((f) => ({
|
|
15634
|
-
path: f.path,
|
|
15635
|
-
reason: "client-side file references a service or secret key"
|
|
15636
|
-
})),
|
|
15637
|
-
fixPrompt: (a) => `In your code, a client-side file references a service or secret key (${a.path}).
|
|
15638
|
-
|
|
15639
|
-
We do not have the value and we do not want it. The problem is the place, not the string.
|
|
15640
|
-
|
|
15641
|
-
Good practice: the service key lives only in server code \u2014 a route handler, a server action, an edge function \u2014 read from the environment without the NEXT_PUBLIC_ prefix. The browser talks to your API; your API talks to the database. Remove the reference from the client file and, if the key was ever bundled, rotate it in the dashboard.
|
|
15642
|
-
|
|
15643
|
-
After, search the built bundle for the key prefix and confirm it is gone.`
|
|
15644
|
-
};
|
|
15645
|
-
var codeRouteWithoutAuth = {
|
|
15646
|
-
id: "code-route-without-auth",
|
|
15647
|
-
source: "code",
|
|
15648
|
-
category: "auth",
|
|
15649
|
-
severity: "high",
|
|
15650
|
-
title: "API route touches data without a session check",
|
|
15651
|
-
impact: "An API route reads or writes the database and nothing in it checks who is asking. If the middleware does not cover it, anyone can call it \u2014 this is what api-route-noauth lights up from outside.",
|
|
15652
|
-
howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && !PUBLIC_ROUTE.test(f.path)).filter((f) => TOUCHES_DB.test(f.content) && !HAS_AUTH.test(f.content)).map((f) => ({
|
|
15653
|
-
path: f.path,
|
|
15654
|
-
reason: "route reads or writes data with no session check in the file"
|
|
15655
|
-
})),
|
|
15656
|
-
fixPrompt: (a) => `In your code, a route reads or writes data with no session check in the file (${a.path}).
|
|
15657
|
-
|
|
15658
|
-
We cannot see your middleware. If it already protects this path, mark the finding as known.
|
|
15659
|
-
|
|
15660
|
-
Good practice: the route resolves the session first and answers 401 before touching data; what it reads or writes is scoped to that user. A public route is public on purpose, with a comment saying so, and never exposes rows that belong to someone.
|
|
15661
|
-
|
|
15662
|
-
After, call the route from a logged-out client and confirm it answers 401 or 403.`
|
|
15663
|
-
};
|
|
15664
|
-
var codeEnvCommitted = {
|
|
15665
|
-
id: "code-env-committed",
|
|
15666
|
-
source: "code",
|
|
15667
|
-
category: "secrets",
|
|
15668
|
-
severity: "critical",
|
|
15669
|
-
title: ".env file tracked by git",
|
|
15670
|
-
impact: "An environment file is committed to the repository. Every clone, fork, CI log and AI tool that reads the repo has the secrets in it. Even after deleting it, the history keeps the values.",
|
|
15671
|
-
// Só o nome. O conteúdo nunca é lido — é o único check que não recebe
|
|
15672
|
-
// `content`, e é assim de propósito.
|
|
15673
|
-
howToDetect: (ctx) => ENV_FILES.filter((nome) => ctx.tracked(nome)).map((nome) => ({
|
|
15674
|
-
path: nome,
|
|
15675
|
-
reason: `${nome} is tracked by git`
|
|
15676
|
-
})),
|
|
15677
|
-
fixPrompt: (a) => `In your code, ${a.reason}.
|
|
15678
|
-
|
|
15679
|
-
We did not read it. We only saw that git tracks it.
|
|
15680
|
-
|
|
15681
|
-
Good practice: environment files stay out of version control \u2014 add them to .gitignore, remove them from the index, and keep a committed .env.example with the variable names and no values. Every secret that was ever in the file is compromised: rotate each one in its provider.
|
|
15682
|
-
|
|
15683
|
-
After, run git ls-files and confirm no .env file is listed.`
|
|
15684
|
-
};
|
|
15685
|
-
var codeAiRouteWithoutLimit = {
|
|
15686
|
-
id: "code-ai-route-without-limit",
|
|
15687
|
-
source: "code",
|
|
15688
|
-
category: "ai",
|
|
15689
|
-
severity: "high",
|
|
15690
|
-
title: "AI route with no rate limit",
|
|
15691
|
-
impact: "A route calls an LLM provider and nothing in it limits how often. A loop from one client turns your provider account into an open bill \u2014 this is ai-proxy-open, seen from inside.",
|
|
15692
|
-
howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path)).filter((f) => AI_SDK.test(f.content) && !HAS_LIMITER.test(f.content)).map((f) => ({
|
|
15693
|
-
path: f.path,
|
|
15694
|
-
reason: "route calls an AI provider with no rate limit in the file"
|
|
15695
|
-
})),
|
|
15696
|
-
fixPrompt: (a) => `In your code, a route calls an AI provider with no rate limit in the file (${a.path}).
|
|
15697
|
-
|
|
15698
|
-
We cannot see limits applied elsewhere. If your gateway already throttles this path, mark the finding as known.
|
|
15699
|
-
|
|
15700
|
-
Good practice: the route requires a session, then applies a per-user limit (so many calls per minute, so many tokens per day) before calling the provider, and answers 429 when it is hit. Cost caps at the provider are the second net, not the first.
|
|
15701
|
-
|
|
15702
|
-
After, call the route in a loop from one client and confirm it starts answering 429.`
|
|
15703
|
-
};
|
|
15704
|
-
var codeWebhookUnsigned = {
|
|
15705
|
-
id: "code-webhook-unsigned",
|
|
15706
|
-
source: "code",
|
|
15707
|
-
category: "config",
|
|
15708
|
-
severity: "high",
|
|
15709
|
-
title: "Stripe webhook without signature verification",
|
|
15710
|
-
impact: 'A webhook route imports Stripe and never verifies the event signature. Anyone can POST a fake "payment succeeded" and your app will believe it \u2014 the one check we refuse to run from outside, because proving it would mean doing exactly that.',
|
|
15711
|
-
howToDetect: (ctx) => ctx.files.filter((f) => isApiRoute(f.path) && WEBHOOK_PATH.test(f.path)).filter((f) => STRIPE_IMPORT.test(f.content) && !SIGNATURE_CHECK.test(f.content)).map((f) => ({
|
|
15712
|
-
path: f.path,
|
|
15713
|
-
reason: "Stripe webhook route never verifies the event signature"
|
|
15714
|
-
})),
|
|
15715
|
-
fixPrompt: (a) => `In your code, a Stripe webhook route never verifies the event signature (${a.path}).
|
|
15716
|
-
|
|
15717
|
-
We cannot see the secret and do not want it.
|
|
15718
|
-
|
|
15719
|
-
Good practice: the route reads the raw body and the stripe-signature header, builds the event with the webhook secret from the environment, and rejects anything that fails. Only the verified event drives state. Each event id is processed once.
|
|
15720
|
-
|
|
15721
|
-
After, POST a fabricated event to the route and confirm it answers 400.`
|
|
15722
|
-
};
|
|
15723
|
-
var CODE_CHECKS = [
|
|
15724
|
-
codeRlsMissing,
|
|
15725
|
-
codeServiceRoleInClient,
|
|
15726
|
-
codeRouteWithoutAuth,
|
|
15727
|
-
codeEnvCommitted,
|
|
15728
|
-
codeAiRouteWithoutLimit,
|
|
15729
|
-
codeWebhookUnsigned
|
|
15730
|
-
];
|
|
15731
|
-
function runCodeChecks(ctx, catalog = CODE_CHECKS) {
|
|
15732
|
-
const out = [];
|
|
15733
|
-
for (const check2 of catalog) {
|
|
15734
|
-
let advisories = [];
|
|
15735
|
-
try {
|
|
15736
|
-
advisories = check2.howToDetect(ctx);
|
|
15737
|
-
} catch {
|
|
15738
|
-
advisories = [];
|
|
15739
|
-
}
|
|
15740
|
-
for (const a of advisories) {
|
|
15741
|
-
out.push({
|
|
15742
|
-
checkId: check2.id,
|
|
15743
|
-
severity: check2.severity,
|
|
15744
|
-
path: a.path,
|
|
15745
|
-
reason: a.reason
|
|
15746
|
-
});
|
|
15747
|
-
}
|
|
15748
|
-
}
|
|
15749
|
-
return out;
|
|
15750
|
-
}
|
|
15751
|
-
|
|
15752
|
-
// src/gate.ts
|
|
18808
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
18809
|
+
import { join as join6 } from "path";
|
|
15753
18810
|
var GATE_DIR = ".vibecheck";
|
|
15754
18811
|
var GATE_JSON = "quality-gate.json";
|
|
15755
18812
|
var GATE_SARIF = "quality-gate.sarif";
|
|
15756
|
-
function escreverArtefatos(dir, report,
|
|
18813
|
+
function escreverArtefatos(dir, report, sarif2, deps) {
|
|
15757
18814
|
deps.writeArtifact(
|
|
15758
|
-
|
|
18815
|
+
join6(dir, GATE_DIR, GATE_JSON),
|
|
15759
18816
|
`${JSON.stringify(report, null, 2)}
|
|
15760
18817
|
`
|
|
15761
18818
|
);
|
|
15762
18819
|
deps.writeArtifact(
|
|
15763
|
-
|
|
15764
|
-
`${JSON.stringify(
|
|
18820
|
+
join6(dir, GATE_DIR, GATE_SARIF),
|
|
18821
|
+
`${JSON.stringify(sarif2, null, 2)}
|
|
15765
18822
|
`
|
|
15766
18823
|
);
|
|
15767
18824
|
}
|
|
15768
18825
|
function gravarNoDisco(caminho2, conteudo) {
|
|
15769
|
-
|
|
15770
|
-
|
|
18826
|
+
mkdirSync5(join6(caminho2, ".."), { recursive: true });
|
|
18827
|
+
writeFileSync5(caminho2, conteudo);
|
|
15771
18828
|
}
|
|
15772
18829
|
async function rodarHttp(input, grafo, deps) {
|
|
15773
18830
|
const headers = {
|
|
@@ -15845,7 +18902,9 @@ async function runGate(input, deps) {
|
|
|
15845
18902
|
const grafo = await deps.extractGraph(input.dir);
|
|
15846
18903
|
const metrics = grafo === null ? null : graphMetrics(grafo);
|
|
15847
18904
|
const repo = deps.readRepo(input.dir);
|
|
15848
|
-
const codeIssues = runCodeChecks(repo
|
|
18905
|
+
const codeIssues = runCodeChecks(repo, void 0, {
|
|
18906
|
+
failOnError: true
|
|
18907
|
+
}).map((i) => ({
|
|
15849
18908
|
checkId: i.checkId,
|
|
15850
18909
|
severity: i.severity,
|
|
15851
18910
|
path: i.path,
|
|
@@ -15897,31 +18956,31 @@ async function enviarGate(scanId, report, input, deps) {
|
|
|
15897
18956
|
}
|
|
15898
18957
|
|
|
15899
18958
|
// src/graphify.ts
|
|
15900
|
-
import { spawn } from "child_process";
|
|
18959
|
+
import { spawn as spawn2 } from "child_process";
|
|
15901
18960
|
import { readFile } from "fs/promises";
|
|
15902
|
-
import { join as
|
|
18961
|
+
import { join as join7 } from "path";
|
|
15903
18962
|
async function tentarGraphify(raiz) {
|
|
15904
|
-
const json2 =
|
|
18963
|
+
const json2 = join7(raiz, "graphify-out", "graph.json");
|
|
15905
18964
|
try {
|
|
15906
18965
|
return JSON.parse(await readFile(json2, "utf8"));
|
|
15907
18966
|
} catch {
|
|
15908
18967
|
}
|
|
15909
|
-
const ok = await new Promise((
|
|
15910
|
-
const child =
|
|
18968
|
+
const ok = await new Promise((resolve4) => {
|
|
18969
|
+
const child = spawn2("graphify", [".", "--no-viz"], {
|
|
15911
18970
|
cwd: raiz,
|
|
15912
18971
|
stdio: "ignore"
|
|
15913
18972
|
});
|
|
15914
18973
|
const timer = setTimeout(() => {
|
|
15915
18974
|
child.kill();
|
|
15916
|
-
|
|
18975
|
+
resolve4(false);
|
|
15917
18976
|
}, 2e4);
|
|
15918
18977
|
child.on("exit", (code) => {
|
|
15919
18978
|
clearTimeout(timer);
|
|
15920
|
-
|
|
18979
|
+
resolve4(code === 0);
|
|
15921
18980
|
});
|
|
15922
18981
|
child.on("error", () => {
|
|
15923
18982
|
clearTimeout(timer);
|
|
15924
|
-
|
|
18983
|
+
resolve4(false);
|
|
15925
18984
|
});
|
|
15926
18985
|
});
|
|
15927
18986
|
if (!ok) return null;
|
|
@@ -15933,7 +18992,7 @@ async function tentarGraphify(raiz) {
|
|
|
15933
18992
|
}
|
|
15934
18993
|
|
|
15935
18994
|
// src/login.ts
|
|
15936
|
-
import { spawn as
|
|
18995
|
+
import { spawn as spawn3 } from "child_process";
|
|
15937
18996
|
function urlBase() {
|
|
15938
18997
|
return (process.env["VIBECHECK_URL"] ?? "https://vibecheck.kinkai.cloud").replace(
|
|
15939
18998
|
/\/$/,
|
|
@@ -15953,12 +19012,12 @@ function comandosAbrirBrowser(url2, platform = process.platform, env = process.e
|
|
|
15953
19012
|
return [["xdg-open", [url2]]];
|
|
15954
19013
|
}
|
|
15955
19014
|
function tentarSpawn(cmd, args) {
|
|
15956
|
-
return new Promise((
|
|
15957
|
-
const child =
|
|
15958
|
-
child.once("error", () =>
|
|
19015
|
+
return new Promise((resolve4) => {
|
|
19016
|
+
const child = spawn3(cmd, args, { stdio: "ignore", detached: true });
|
|
19017
|
+
child.once("error", () => resolve4(false));
|
|
15959
19018
|
child.once("spawn", () => {
|
|
15960
19019
|
child.unref();
|
|
15961
|
-
|
|
19020
|
+
resolve4(true);
|
|
15962
19021
|
});
|
|
15963
19022
|
});
|
|
15964
19023
|
}
|
|
@@ -16018,8 +19077,8 @@ async function loginDevice(ci, options = {}) {
|
|
|
16018
19077
|
|
|
16019
19078
|
// src/repo.ts
|
|
16020
19079
|
import { execFileSync } from "child_process";
|
|
16021
|
-
import { lstatSync, readdirSync, readFileSync as
|
|
16022
|
-
import { join as
|
|
19080
|
+
import { lstatSync as lstatSync3, readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
19081
|
+
import { join as join8, relative as relative2 } from "path";
|
|
16023
19082
|
var MAX_FILES = 800;
|
|
16024
19083
|
var MAX_BYTES2 = 64e3;
|
|
16025
19084
|
var MAX_SQL_BYTES = 512e3;
|
|
@@ -16053,23 +19112,23 @@ function walk(dir, raiz, out) {
|
|
|
16053
19112
|
if (out.length >= MAX_FILES) return;
|
|
16054
19113
|
let entradas;
|
|
16055
19114
|
try {
|
|
16056
|
-
entradas =
|
|
19115
|
+
entradas = readdirSync2(dir);
|
|
16057
19116
|
} catch {
|
|
16058
19117
|
return;
|
|
16059
19118
|
}
|
|
16060
19119
|
for (const nome of entradas) {
|
|
16061
19120
|
if (out.length >= MAX_FILES) return;
|
|
16062
19121
|
if (SKIP_DIR.has(nome) || nome.startsWith(".") && nome !== ".env") continue;
|
|
16063
|
-
const caminho2 =
|
|
19122
|
+
const caminho2 = join8(dir, nome);
|
|
16064
19123
|
let st;
|
|
16065
19124
|
try {
|
|
16066
|
-
st =
|
|
19125
|
+
st = lstatSync3(caminho2);
|
|
16067
19126
|
} catch {
|
|
16068
19127
|
continue;
|
|
16069
19128
|
}
|
|
16070
19129
|
if (st.isSymbolicLink()) continue;
|
|
16071
19130
|
if (st.isDirectory()) walk(caminho2, raiz, out);
|
|
16072
|
-
else if (SOURCE.test(nome)) out.push(
|
|
19131
|
+
else if (SOURCE.test(nome)) out.push(relative2(raiz, caminho2).split("\\").join("/"));
|
|
16073
19132
|
}
|
|
16074
19133
|
}
|
|
16075
19134
|
function readCodeContext(dir) {
|
|
@@ -16079,9 +19138,9 @@ function readCodeContext(dir) {
|
|
|
16079
19138
|
const files = [];
|
|
16080
19139
|
for (const path of caminhos) {
|
|
16081
19140
|
try {
|
|
16082
|
-
const st =
|
|
19141
|
+
const st = lstatSync3(join8(dir, path));
|
|
16083
19142
|
if (st.size > (path.endsWith(".sql") ? MAX_SQL_BYTES : MAX_BYTES2)) continue;
|
|
16084
|
-
files.push({ path, content:
|
|
19143
|
+
files.push({ path, content: readFileSync5(join8(dir, path), "utf8") });
|
|
16085
19144
|
} catch {
|
|
16086
19145
|
}
|
|
16087
19146
|
}
|
|
@@ -16107,7 +19166,7 @@ function regras(issues) {
|
|
|
16107
19166
|
for (const issue2 of issues) {
|
|
16108
19167
|
if (!vistos.has(issue2.checkId)) vistos.set(issue2.checkId, issue2.reason);
|
|
16109
19168
|
}
|
|
16110
|
-
return [...vistos].map(([id,
|
|
19169
|
+
return [...vistos].map(([id, text2]) => ({ id, shortDescription: { text: text2 } }));
|
|
16111
19170
|
}
|
|
16112
19171
|
function toSarif(report, version2) {
|
|
16113
19172
|
return {
|
|
@@ -16135,8 +19194,8 @@ function toSarif(report, version2) {
|
|
|
16135
19194
|
}
|
|
16136
19195
|
|
|
16137
19196
|
// src/walker.ts
|
|
16138
|
-
import { lstatSync as
|
|
16139
|
-
import { join as
|
|
19197
|
+
import { lstatSync as lstatSync4, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
19198
|
+
import { join as join9, relative as relative3 } from "path";
|
|
16140
19199
|
var IGNORAR = /* @__PURE__ */ new Set([
|
|
16141
19200
|
"node_modules",
|
|
16142
19201
|
".git",
|
|
@@ -16171,16 +19230,16 @@ var HANDLERS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE",
|
|
|
16171
19230
|
function listar(raiz, dir, acc) {
|
|
16172
19231
|
let nomes;
|
|
16173
19232
|
try {
|
|
16174
|
-
nomes =
|
|
19233
|
+
nomes = readdirSync3(dir);
|
|
16175
19234
|
} catch {
|
|
16176
19235
|
return;
|
|
16177
19236
|
}
|
|
16178
19237
|
for (const nome of nomes) {
|
|
16179
19238
|
if (IGNORAR.has(nome) || nome.startsWith(".")) continue;
|
|
16180
|
-
const cheio =
|
|
19239
|
+
const cheio = join9(dir, nome);
|
|
16181
19240
|
let stat;
|
|
16182
19241
|
try {
|
|
16183
|
-
stat =
|
|
19242
|
+
stat = lstatSync4(cheio);
|
|
16184
19243
|
} catch {
|
|
16185
19244
|
continue;
|
|
16186
19245
|
}
|
|
@@ -16194,7 +19253,7 @@ function listar(raiz, dir, acc) {
|
|
|
16194
19253
|
}
|
|
16195
19254
|
}
|
|
16196
19255
|
function posixRel(raiz, cheio) {
|
|
16197
|
-
return
|
|
19256
|
+
return relative3(raiz, cheio).replaceAll("\\", "/");
|
|
16198
19257
|
}
|
|
16199
19258
|
function dirnamePosix(caminho2) {
|
|
16200
19259
|
const i = caminho2.lastIndexOf("/");
|
|
@@ -16266,12 +19325,12 @@ function resolverSpec(de, spec, indice) {
|
|
|
16266
19325
|
}
|
|
16267
19326
|
return null;
|
|
16268
19327
|
}
|
|
16269
|
-
function rotuloDe(rel,
|
|
19328
|
+
function rotuloDe(rel, exports2) {
|
|
16270
19329
|
const rota = routeFromFile(rel);
|
|
16271
|
-
const handlers =
|
|
19330
|
+
const handlers = exports2.filter((e) => HANDLERS.has(e));
|
|
16272
19331
|
if (rota !== null && handlers.length > 0) return `${handlers[0]} ${rota}`;
|
|
16273
19332
|
if (rota !== null) return rota;
|
|
16274
|
-
const simbolo =
|
|
19333
|
+
const simbolo = exports2.find((e) => !HANDLERS.has(e));
|
|
16275
19334
|
if (simbolo !== void 0) return simbolo;
|
|
16276
19335
|
const base = rel.split("/").at(-1)?.replace(/\.(m?[jt]sx?)$/i, "");
|
|
16277
19336
|
return base !== void 0 && base.length > 0 && base !== "index" ? base : rel;
|
|
@@ -16280,16 +19339,16 @@ function lerArquivo(raiz, cheio) {
|
|
|
16280
19339
|
const rel = posixRel(raiz, cheio);
|
|
16281
19340
|
let conteudo = "";
|
|
16282
19341
|
try {
|
|
16283
|
-
conteudo =
|
|
19342
|
+
conteudo = readFileSync6(cheio, "utf8").slice(0, MAX_BYTES_ARQUIVO);
|
|
16284
19343
|
} catch {
|
|
16285
19344
|
return null;
|
|
16286
19345
|
}
|
|
16287
|
-
const
|
|
19346
|
+
const exports2 = exportsDe(conteudo);
|
|
16288
19347
|
const specs = specsDe(conteudo);
|
|
16289
19348
|
return {
|
|
16290
19349
|
rel,
|
|
16291
|
-
label: rotuloDe(rel,
|
|
16292
|
-
exports,
|
|
19350
|
+
label: rotuloDe(rel, exports2),
|
|
19351
|
+
exports: exports2,
|
|
16293
19352
|
importsRel: specs.filter(
|
|
16294
19353
|
(s) => s.startsWith(".") || s.startsWith("@/") || s.startsWith("~/")
|
|
16295
19354
|
),
|
|
@@ -16402,11 +19461,14 @@ function isAdvancedScan(grafo) {
|
|
|
16402
19461
|
}
|
|
16403
19462
|
|
|
16404
19463
|
// src/cli.ts
|
|
16405
|
-
var VERSION = "0.
|
|
19464
|
+
var VERSION = "0.3.0";
|
|
16406
19465
|
var AJUDA = `vibecheck \u2014 security check for AI-built apps
|
|
16407
19466
|
|
|
16408
19467
|
npx @kinkai.cloud/vibecheck [options]
|
|
16409
19468
|
|
|
19469
|
+
Commands
|
|
19470
|
+
analyze project quality and security analysis (analyze --help)
|
|
19471
|
+
|
|
16410
19472
|
Options
|
|
16411
19473
|
--url <url> target; defaults to https://<verified host of the token>
|
|
16412
19474
|
--dir <path> repository root (default: current directory)
|
|
@@ -16421,6 +19483,7 @@ Options
|
|
|
16421
19483
|
|
|
16422
19484
|
Environment
|
|
16423
19485
|
VIBECHECK_TOKEN, VIBECHECK_HOST, VIBECHECK_URL
|
|
19486
|
+
VIBECHECK_PROJECT_TOKEN project token for analyze --publish
|
|
16424
19487
|
VIBECHECK_ALLOW_CODE_SCAN=1 required by --gate in CI
|
|
16425
19488
|
`;
|
|
16426
19489
|
function arg(nome, argv) {
|
|
@@ -16476,6 +19539,7 @@ async function comandoConsentimento(acao, raiz) {
|
|
|
16476
19539
|
return GATE_EXIT.operational;
|
|
16477
19540
|
}
|
|
16478
19541
|
async function main(argv) {
|
|
19542
|
+
if (argv[0] === "analyze") return analyzeCommand(argv.slice(1));
|
|
16479
19543
|
if (temFlag("--help", argv) || temFlag("-h", argv)) {
|
|
16480
19544
|
console.warn(AJUDA);
|
|
16481
19545
|
return 0;
|