@kinkai.cloud/vibecheck 0.2.0 → 0.3.1
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 +3580 -479
- package/engines/engines.json +7 -0
- package/engines/install.py +20 -0
- package/engines/requirements.txt +66 -0
- package/package.json +5 -2
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, readFileSync as readFileSync3 } 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,3506 @@ 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
|
+
function loadLocalEnv(root) {
|
|
17697
|
+
const file2 = join3(root, ".env");
|
|
17698
|
+
if (!existsSync(file2) || lstatSync2(file2).isSymbolicLink() || !lstatSync2(file2).isFile())
|
|
17699
|
+
return;
|
|
17700
|
+
for (const line of readFileSync3(file2, "utf8").split(/\r?\n/)) {
|
|
17701
|
+
const match = line.match(/^\s*(?:export\s+)?([A-Z_][A-Z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
17702
|
+
const key = match?.[1];
|
|
17703
|
+
if (!match || !key || process.env[key] !== void 0) continue;
|
|
17704
|
+
let value = match[2];
|
|
17705
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))
|
|
17706
|
+
value = value.slice(1, -1);
|
|
17707
|
+
process.env[key] = value;
|
|
17708
|
+
}
|
|
17709
|
+
}
|
|
17710
|
+
async function analyzeCommand(argv) {
|
|
17711
|
+
if (argv.includes("--help")) {
|
|
17712
|
+
console.warn(
|
|
17713
|
+
"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."
|
|
17714
|
+
);
|
|
17715
|
+
return 0;
|
|
17716
|
+
}
|
|
17717
|
+
const value = (key) => {
|
|
17718
|
+
const i = argv.indexOf(key);
|
|
17719
|
+
return i < 0 ? void 0 : argv[i + 1];
|
|
17720
|
+
};
|
|
17721
|
+
const root = resolve2(value("--dir") ?? process.cwd()), configPath = value("--config") ?? "vibecheck.config.json";
|
|
17722
|
+
loadLocalEnv(root);
|
|
17723
|
+
const token = process.env["VIBECHECK_PROJECT_TOKEN"], base = process.env["VIBECHECK_URL"] ?? "https://vibecheck.kinkai.cloud";
|
|
17724
|
+
let config2;
|
|
17725
|
+
if (existsSync(join3(root, configPath))) {
|
|
17726
|
+
config2 = configSchema.parse(JSON.parse(localFile(root, configPath)));
|
|
17727
|
+
} else if (argv.includes("--publish") && token) {
|
|
17728
|
+
const response = await fetch(new URL("/api/projects", base), {
|
|
17729
|
+
headers: { authorization: "Bearer " + token },
|
|
17730
|
+
signal: AbortSignal.timeout(3e4)
|
|
17731
|
+
});
|
|
17732
|
+
if (!response.ok) throw new Error("Project token rejected while discovering project");
|
|
17733
|
+
const project = external_exports.object({ id: external_exports.string().uuid(), reference: external_exports.string() }).parse(await response.json());
|
|
17734
|
+
config2 = {
|
|
17735
|
+
version: 2,
|
|
17736
|
+
projectId: project.id,
|
|
17737
|
+
reference: project.reference,
|
|
17738
|
+
baseline: void 0,
|
|
17739
|
+
coverage: [],
|
|
17740
|
+
junit: [],
|
|
17741
|
+
sarif: []
|
|
17742
|
+
};
|
|
17743
|
+
console.warn(
|
|
17744
|
+
"No vibecheck.config.json found; using project settings discovered from the token."
|
|
17745
|
+
);
|
|
17746
|
+
} else {
|
|
17747
|
+
throw new Error(
|
|
17748
|
+
"Create vibecheck.config.json, or set VIBECHECK_PROJECT_TOKEN and use --publish to discover the project automatically."
|
|
17749
|
+
);
|
|
17750
|
+
}
|
|
17751
|
+
let baseline = config2.baseline ? analysisReportSchema.parse(JSON.parse(localFile(root, config2.baseline))) : null;
|
|
17752
|
+
let report = await analyze({ root, ...config2, baseline });
|
|
17753
|
+
const url2 = new URL(base);
|
|
17754
|
+
if (url2.protocol !== "https:" && !["localhost", "127.0.0.1"].includes(url2.hostname))
|
|
17755
|
+
throw new Error("Use HTTPS for project uploads");
|
|
17756
|
+
const api = async (path, method = "GET", body) => {
|
|
17757
|
+
const result = await fetch(new URL("/api/projects/" + config2.projectId + path, url2), {
|
|
17758
|
+
method,
|
|
17759
|
+
redirect: "error",
|
|
17760
|
+
headers: { authorization: "Bearer " + token, "content-type": "application/json" },
|
|
17761
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
17762
|
+
signal: AbortSignal.timeout(3e4)
|
|
17763
|
+
});
|
|
17764
|
+
if (!result.ok) throw new Error("Project API rejected request: " + result.status);
|
|
17765
|
+
return result.json();
|
|
17766
|
+
};
|
|
17767
|
+
if (argv.includes("--publish")) {
|
|
17768
|
+
if (!token) throw new Error("VIBECHECK_PROJECT_TOKEN is required to publish");
|
|
17769
|
+
const project = external_exports.object({ policy: policySchema, reference: external_exports.string() }).parse(await api(""));
|
|
17770
|
+
if (project.reference !== config2.reference)
|
|
17771
|
+
throw new Error("Config reference must match the project reference branch");
|
|
17772
|
+
if (!baseline && report.mergeBase) {
|
|
17773
|
+
const runs = external_exports.array(external_exports.object({ id: external_exports.string().uuid() })).parse(await api("/runs?commit=" + report.mergeBase));
|
|
17774
|
+
if (runs[0]) baseline = analysisReportSchema.parse(await api("/runs/" + runs[0].id));
|
|
17775
|
+
}
|
|
17776
|
+
report = evaluateAnalysis(
|
|
17777
|
+
{ ...report, baselineRunId: baseline?.runId ?? null },
|
|
17778
|
+
baseline,
|
|
17779
|
+
project.policy
|
|
17780
|
+
);
|
|
17781
|
+
}
|
|
17782
|
+
const dir = join3(root, ".vibecheck");
|
|
17783
|
+
if (existsSync(dir) && lstatSync2(dir).isSymbolicLink())
|
|
17784
|
+
throw new Error("Unsafe artifact directory");
|
|
17785
|
+
mkdirSync2(dir, { recursive: true });
|
|
17786
|
+
const save = (r) => {
|
|
17787
|
+
for (const name of ["analysis.json", "analysis.sarif"]) {
|
|
17788
|
+
const target = join3(dir, name);
|
|
17789
|
+
if (existsSync(target) && (lstatSync2(target).isSymbolicLink() || !lstatSync2(target).isFile()))
|
|
17790
|
+
throw new Error("Unsafe artifact file");
|
|
17791
|
+
}
|
|
17792
|
+
writeFileSync2(join3(dir, "analysis.json"), JSON.stringify(r, null, 2) + "\n");
|
|
17793
|
+
writeFileSync2(
|
|
17794
|
+
join3(dir, "analysis.sarif"),
|
|
17795
|
+
JSON.stringify(toAnalysisSarif(r), null, 2) + "\n"
|
|
17796
|
+
);
|
|
17797
|
+
};
|
|
17798
|
+
save(report);
|
|
17799
|
+
if (argv.includes("--publish")) {
|
|
17800
|
+
const serialized = JSON.stringify(report), parts = serialized.match(/[\s\S]{1,100000}/g) ?? [];
|
|
17801
|
+
await api("/runs", "POST", { runId: report.runId, batches: parts.length });
|
|
17802
|
+
for (const [batch, payload] of parts.entries())
|
|
17803
|
+
await api("/runs/" + report.runId + "/batches", "PUT", { batch, payload });
|
|
17804
|
+
await api("/runs/" + report.runId + "/finalize", "POST", {});
|
|
17805
|
+
report = analysisReportSchema.parse(await api("/runs/" + report.runId));
|
|
17806
|
+
save(report);
|
|
17807
|
+
console.warn(new URL("/projects/" + config2.projectId, url2).href);
|
|
17808
|
+
}
|
|
17809
|
+
console.warn(
|
|
17810
|
+
`${report.status}: ${report.findings.filter((f) => f.state === "new").length} new findings`
|
|
17811
|
+
);
|
|
17812
|
+
for (const engine of report.engines)
|
|
17813
|
+
console.warn(`${engine.id} ${engine.version}: ${engine.status}`);
|
|
17814
|
+
return report.status === "passed" ? 0 : report.status === "failed" ? 1 : 2;
|
|
17815
|
+
}
|
|
17816
|
+
|
|
17817
|
+
// ../../packages/checks/src/types.ts
|
|
17818
|
+
var SEVERITIES = ["fatal", "critical", "high", "medium", "low"];
|
|
17819
|
+
var NODE_KINDS = [
|
|
17820
|
+
"browser",
|
|
17821
|
+
"app",
|
|
17822
|
+
"api",
|
|
17823
|
+
"db",
|
|
17824
|
+
"storage",
|
|
17825
|
+
"auth",
|
|
17826
|
+
"ai",
|
|
17827
|
+
"payments",
|
|
17828
|
+
"secrets"
|
|
17829
|
+
];
|
|
17830
|
+
var CATEGORIES = ["secrets", "data", "storage", "auth", "ai", "config"];
|
|
17831
|
+
|
|
17832
|
+
// ../../packages/graph/src/types.ts
|
|
17833
|
+
var EDGE_TYPES = ["imports", "calls", "contains"];
|
|
17834
|
+
var MAX_NODES = 400;
|
|
17835
|
+
var MAX_EDGES = 800;
|
|
17836
|
+
var MAX_COMMUNITIES = 9;
|
|
17837
|
+
var MAX_FILES_PER_NODE = 12;
|
|
17838
|
+
var MAX_COMMUNITY_FILES = 8;
|
|
17839
|
+
var MAX_BYTES = 256e3;
|
|
17840
|
+
var MAX_LABEL = 80;
|
|
17841
|
+
var MAX_ID = 64;
|
|
17842
|
+
var MAX_PATH = 180;
|
|
17843
|
+
var MAX_PACKAGES = 32;
|
|
17844
|
+
var MAX_HINT_ROUTES = 80;
|
|
17845
|
+
var MAX_HINT_TABLES = 40;
|
|
17846
|
+
var MAX_HINT_BUCKETS = 24;
|
|
17847
|
+
var MAX_HINT_COLLECTIONS = 24;
|
|
17848
|
+
var EMPTY_DATA = { tables: [], buckets: [], collections: [] };
|
|
17849
|
+
var pathSchema = external_exports.string().min(1).max(MAX_PATH);
|
|
17850
|
+
var nodeSchema = external_exports.object({
|
|
17851
|
+
id: external_exports.string().min(1).max(MAX_ID),
|
|
17852
|
+
kind: external_exports.enum(NODE_KINDS),
|
|
17853
|
+
label: external_exports.string().min(1).max(MAX_LABEL),
|
|
17854
|
+
files: external_exports.array(pathSchema).max(MAX_FILES_PER_NODE)
|
|
17855
|
+
});
|
|
17856
|
+
var edgeSchema = external_exports.object({
|
|
17857
|
+
from: external_exports.string().min(1).max(MAX_ID),
|
|
17858
|
+
to: external_exports.string().min(1).max(MAX_ID),
|
|
17859
|
+
type: external_exports.enum(EDGE_TYPES)
|
|
17860
|
+
});
|
|
17861
|
+
var communitySchema = external_exports.object({
|
|
17862
|
+
nodeKind: external_exports.enum(NODE_KINDS),
|
|
17863
|
+
label: external_exports.string().min(1).max(MAX_LABEL),
|
|
17864
|
+
files: external_exports.array(pathSchema).max(MAX_COMMUNITY_FILES)
|
|
17865
|
+
});
|
|
17866
|
+
var tableName = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/);
|
|
17867
|
+
var bucketName = external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$/);
|
|
17868
|
+
var dataSchema = external_exports.object({
|
|
17869
|
+
tables: external_exports.array(tableName).max(MAX_HINT_TABLES),
|
|
17870
|
+
buckets: external_exports.array(bucketName).max(MAX_HINT_BUCKETS),
|
|
17871
|
+
collections: external_exports.array(tableName).max(MAX_HINT_COLLECTIONS)
|
|
17872
|
+
});
|
|
17873
|
+
var scanGraphSchema = external_exports.object({
|
|
17874
|
+
version: external_exports.literal(1),
|
|
17875
|
+
advanced: external_exports.boolean(),
|
|
17876
|
+
nodes: external_exports.array(nodeSchema).max(MAX_NODES),
|
|
17877
|
+
edges: external_exports.array(edgeSchema).max(MAX_EDGES),
|
|
17878
|
+
communities: external_exports.array(communitySchema).max(MAX_COMMUNITIES),
|
|
17879
|
+
packages: external_exports.array(external_exports.string().min(1).max(MAX_LABEL)).max(MAX_PACKAGES),
|
|
17880
|
+
data: dataSchema.optional()
|
|
17881
|
+
});
|
|
17882
|
+
var EMPTY_HINTS = {
|
|
17883
|
+
apiRoutes: [],
|
|
17884
|
+
tableNames: [],
|
|
17885
|
+
buckets: [],
|
|
17886
|
+
collections: []
|
|
17887
|
+
};
|
|
17888
|
+
|
|
17889
|
+
// ../../packages/graph/src/hints.ts
|
|
17890
|
+
function uniqueCapped(values, cap) {
|
|
17891
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17892
|
+
const out = [];
|
|
17893
|
+
for (const value of values) {
|
|
17894
|
+
if (out.length >= cap) break;
|
|
17895
|
+
if (seen.has(value)) continue;
|
|
17896
|
+
seen.add(value);
|
|
17897
|
+
out.push(value);
|
|
17898
|
+
}
|
|
17899
|
+
return out;
|
|
17900
|
+
}
|
|
17901
|
+
function routeFromFile(path) {
|
|
17902
|
+
const normal = path.replaceAll("\\", "/");
|
|
17903
|
+
const app = /(?:^|\/)app\/(.+)\/route\.(t|j)sx?$/i.exec(normal);
|
|
17904
|
+
if (app?.[1] !== void 0) {
|
|
17905
|
+
const segments = app[1].split("/").filter((s) => s.length > 0 && !s.startsWith("(") && !s.startsWith("@")).map((s) => s.replace(/^\[+\.\.\.(.+)\]+$/, "*").replace(/^\[+(.+)\]+$/, ":$1"));
|
|
17906
|
+
if (segments.length === 0) return "/api";
|
|
17907
|
+
return `/${segments.join("/")}`;
|
|
17908
|
+
}
|
|
17909
|
+
const pages = /(?:^|\/)pages\/api\/(.+)\.(t|j)sx?$/i.exec(normal);
|
|
17910
|
+
if (pages?.[1] !== void 0) {
|
|
17911
|
+
const withoutIndex = pages[1].replace(/\/index$/i, "");
|
|
17912
|
+
return `/api/${withoutIndex}`;
|
|
17913
|
+
}
|
|
17914
|
+
return null;
|
|
17915
|
+
}
|
|
17916
|
+
function filesFromGraph(graph) {
|
|
17917
|
+
return [
|
|
17918
|
+
...graph.nodes.flatMap((n) => n.files),
|
|
17919
|
+
...graph.communities.flatMap((c) => c.files)
|
|
17920
|
+
];
|
|
17921
|
+
}
|
|
17922
|
+
function hintsFromGraph(graph) {
|
|
17923
|
+
if (graph === null || graph === void 0 || !graph.advanced) return EMPTY_HINTS;
|
|
17924
|
+
const routes = [];
|
|
17925
|
+
for (const file2 of filesFromGraph(graph)) {
|
|
17926
|
+
const route = routeFromFile(file2);
|
|
17927
|
+
if (route !== null) routes.push(route);
|
|
17928
|
+
}
|
|
17929
|
+
routes.sort((a, b) => a.localeCompare(b));
|
|
17930
|
+
const data = graph.data ?? EMPTY_DATA;
|
|
17931
|
+
return {
|
|
17932
|
+
apiRoutes: uniqueCapped(routes, MAX_HINT_ROUTES),
|
|
17933
|
+
tableNames: uniqueCapped(data.tables, MAX_HINT_TABLES),
|
|
17934
|
+
buckets: uniqueCapped(data.buckets, MAX_HINT_BUCKETS),
|
|
17935
|
+
collections: uniqueCapped(data.collections, MAX_HINT_COLLECTIONS)
|
|
17936
|
+
};
|
|
17937
|
+
}
|
|
17938
|
+
|
|
17939
|
+
// ../../packages/graph/src/metrics.ts
|
|
17940
|
+
function importCycles(graph) {
|
|
17941
|
+
const adjacencia = /* @__PURE__ */ new Map();
|
|
17942
|
+
for (const aresta of graph.edges) {
|
|
17943
|
+
if (aresta.type !== "imports") continue;
|
|
17944
|
+
const vizinhos = adjacencia.get(aresta.from) ?? [];
|
|
17945
|
+
vizinhos.push(aresta.to);
|
|
17946
|
+
adjacencia.set(aresta.from, vizinhos);
|
|
17947
|
+
}
|
|
17948
|
+
const indice = /* @__PURE__ */ new Map();
|
|
17949
|
+
const lowlink = /* @__PURE__ */ new Map();
|
|
17950
|
+
const naPilha = /* @__PURE__ */ new Set();
|
|
17951
|
+
const pilha = [];
|
|
17952
|
+
let proximo = 0;
|
|
17953
|
+
let ciclos = 0;
|
|
17954
|
+
for (const raiz of graph.nodes.map((n) => n.id)) {
|
|
17955
|
+
if (indice.has(raiz)) continue;
|
|
17956
|
+
const quadros = [{ no: raiz, vizinho: 0 }];
|
|
17957
|
+
indice.set(raiz, proximo);
|
|
17958
|
+
lowlink.set(raiz, proximo);
|
|
17959
|
+
proximo += 1;
|
|
17960
|
+
pilha.push(raiz);
|
|
17961
|
+
naPilha.add(raiz);
|
|
17962
|
+
while (quadros.length > 0) {
|
|
17963
|
+
const quadro = quadros[quadros.length - 1];
|
|
17964
|
+
if (quadro === void 0) break;
|
|
17965
|
+
const vizinhos = adjacencia.get(quadro.no) ?? [];
|
|
17966
|
+
if (quadro.vizinho < vizinhos.length) {
|
|
17967
|
+
const vizinho = vizinhos[quadro.vizinho];
|
|
17968
|
+
quadro.vizinho += 1;
|
|
17969
|
+
if (!indice.has(vizinho)) {
|
|
17970
|
+
indice.set(vizinho, proximo);
|
|
17971
|
+
lowlink.set(vizinho, proximo);
|
|
17972
|
+
proximo += 1;
|
|
17973
|
+
pilha.push(vizinho);
|
|
17974
|
+
naPilha.add(vizinho);
|
|
17975
|
+
quadros.push({ no: vizinho, vizinho: 0 });
|
|
17976
|
+
} else if (naPilha.has(vizinho)) {
|
|
17977
|
+
lowlink.set(
|
|
17978
|
+
quadro.no,
|
|
17979
|
+
Math.min(lowlink.get(quadro.no) ?? 0, indice.get(vizinho) ?? 0)
|
|
17980
|
+
);
|
|
17981
|
+
}
|
|
17982
|
+
continue;
|
|
17983
|
+
}
|
|
17984
|
+
quadros.pop();
|
|
17985
|
+
const pai = quadros[quadros.length - 1];
|
|
17986
|
+
if (pai !== void 0) {
|
|
17987
|
+
lowlink.set(
|
|
17988
|
+
pai.no,
|
|
17989
|
+
Math.min(lowlink.get(pai.no) ?? 0, lowlink.get(quadro.no) ?? 0)
|
|
17990
|
+
);
|
|
17991
|
+
}
|
|
17992
|
+
if (lowlink.get(quadro.no) === indice.get(quadro.no)) {
|
|
17993
|
+
let tamanho = 0;
|
|
17994
|
+
let atual;
|
|
17995
|
+
do {
|
|
17996
|
+
atual = pilha.pop();
|
|
17997
|
+
if (atual === void 0) break;
|
|
17998
|
+
naPilha.delete(atual);
|
|
17999
|
+
tamanho += 1;
|
|
18000
|
+
} while (atual !== quadro.no);
|
|
18001
|
+
if (tamanho > 1) ciclos += 1;
|
|
18002
|
+
}
|
|
18003
|
+
}
|
|
18004
|
+
}
|
|
18005
|
+
return ciclos;
|
|
18006
|
+
}
|
|
18007
|
+
function graphMetrics(graph) {
|
|
18008
|
+
return {
|
|
18009
|
+
nodes: graph.nodes.length,
|
|
18010
|
+
edges: graph.edges.length,
|
|
18011
|
+
routes: hintsFromGraph(graph).apiRoutes.length,
|
|
18012
|
+
packages: graph.packages.length,
|
|
18013
|
+
cycles: importCycles(graph)
|
|
18014
|
+
};
|
|
18015
|
+
}
|
|
18016
|
+
|
|
18017
|
+
// ../../packages/graph/src/identifiers.ts
|
|
18018
|
+
var FROM_CALL = /([A-Za-z_$][\w$]*)\s*\.\s*from\(\s*["'`]([A-Za-z0-9._-]{1,63})["'`]\s*\)/g;
|
|
18019
|
+
var NOT_A_DATABASE = /* @__PURE__ */ new Set([
|
|
18020
|
+
"array",
|
|
18021
|
+
"buffer",
|
|
18022
|
+
"object",
|
|
18023
|
+
"string",
|
|
18024
|
+
"number",
|
|
18025
|
+
"date",
|
|
14731
18026
|
"map",
|
|
14732
18027
|
"set",
|
|
14733
18028
|
"promise",
|
|
@@ -14837,7 +18132,7 @@ var KIND_LABEL = {
|
|
|
14837
18132
|
payments: "payments",
|
|
14838
18133
|
secrets: "security"
|
|
14839
18134
|
};
|
|
14840
|
-
var
|
|
18135
|
+
var RULES2 = [
|
|
14841
18136
|
{
|
|
14842
18137
|
kind: "api",
|
|
14843
18138
|
test: /app\/.+\/route\.(t|j)sx?\b|pages\/api\//i
|
|
@@ -14854,16 +18149,16 @@ var RULES = [
|
|
|
14854
18149
|
{ kind: "browser", test: /\b(middleware\.(t|j)s|proxy\.(t|j)s)\b/i }
|
|
14855
18150
|
];
|
|
14856
18151
|
function mapKind(signal) {
|
|
14857
|
-
const
|
|
14858
|
-
for (const rule of
|
|
14859
|
-
if (rule.test.test(
|
|
18152
|
+
const text2 = `${signal.label} ${signal.files.join(" ")}`;
|
|
18153
|
+
for (const rule of RULES2) {
|
|
18154
|
+
if (rule.test.test(text2)) return rule.kind;
|
|
14860
18155
|
}
|
|
14861
18156
|
return "app";
|
|
14862
18157
|
}
|
|
14863
18158
|
|
|
14864
18159
|
// ../../packages/graph/src/packages.ts
|
|
14865
18160
|
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
|
|
18161
|
+
var RULES3 = [
|
|
14867
18162
|
{ id: "next", test: /^next(?:\/|$)/ },
|
|
14868
18163
|
{ id: "react", test: /^react(?:-dom)?(?:\/|$)/ },
|
|
14869
18164
|
{ id: "vue", test: /^vue(?:\/|$)/ },
|
|
@@ -14903,7 +18198,7 @@ function packageId(spec) {
|
|
|
14903
18198
|
if (cleaned.length === 0 || cleaned.startsWith(".") || cleaned.startsWith("/"))
|
|
14904
18199
|
return null;
|
|
14905
18200
|
if (cleaned.startsWith("node:") || cleaned.startsWith("#")) return null;
|
|
14906
|
-
for (const rule of
|
|
18201
|
+
for (const rule of RULES3) {
|
|
14907
18202
|
if (rule.test.test(cleaned)) return rule.id;
|
|
14908
18203
|
}
|
|
14909
18204
|
return null;
|
|
@@ -15046,9 +18341,9 @@ function firstExport(value) {
|
|
|
15046
18341
|
}
|
|
15047
18342
|
function nodesFromDump(dump) {
|
|
15048
18343
|
const raw = dump["nodes"] ?? dump["nos"];
|
|
15049
|
-
const
|
|
18344
|
+
const list2 = Array.isArray(raw) ? raw : isRecord(raw) ? Object.entries(raw).map(([id, n]) => isRecord(n) ? { id, ...n } : { id }) : [];
|
|
15050
18345
|
const nodes = [];
|
|
15051
|
-
for (const [i, item] of
|
|
18346
|
+
for (const [i, item] of list2.entries()) {
|
|
15052
18347
|
if (!isRecord(item)) continue;
|
|
15053
18348
|
const files = filesFrom(
|
|
15054
18349
|
item["files"] ?? item["files"] ?? item["source_file"] ?? item["sourceFile"] ?? item["file"] ?? item["path"]
|
|
@@ -15070,10 +18365,10 @@ function nodesFromDump(dump) {
|
|
|
15070
18365
|
}
|
|
15071
18366
|
function edgesFromDump(dump, ids) {
|
|
15072
18367
|
const raw = dump["edges"] ?? dump["edges"];
|
|
15073
|
-
const
|
|
18368
|
+
const list2 = Array.isArray(raw) ? raw : [];
|
|
15074
18369
|
const out = [];
|
|
15075
18370
|
const seen = /* @__PURE__ */ new Set();
|
|
15076
|
-
for (const item of
|
|
18371
|
+
for (const item of list2) {
|
|
15077
18372
|
if (!isRecord(item)) continue;
|
|
15078
18373
|
const from = idFrom(item["from"] ?? item["de"] ?? item["source"], "");
|
|
15079
18374
|
const to = idFrom(item["to"] ?? item["para"] ?? item["target"], "");
|
|
@@ -15386,19 +18681,19 @@ var sequencedScanEventSchema = external_exports.intersection(
|
|
|
15386
18681
|
);
|
|
15387
18682
|
|
|
15388
18683
|
// src/consent.ts
|
|
15389
|
-
import { chmodSync as chmodSync2, mkdirSync as
|
|
18684
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
15390
18685
|
import { createInterface } from "readline/promises";
|
|
15391
|
-
import { join as
|
|
18686
|
+
import { join as join5, resolve as resolve3 } from "path";
|
|
15392
18687
|
|
|
15393
18688
|
// src/token.ts
|
|
15394
|
-
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
18689
|
+
import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
15395
18690
|
import { homedir } from "os";
|
|
15396
|
-
import { join } from "path";
|
|
18691
|
+
import { join as join4 } from "path";
|
|
15397
18692
|
function dirConfig() {
|
|
15398
|
-
return
|
|
18693
|
+
return join4(homedir(), ".config", "vibecheck");
|
|
15399
18694
|
}
|
|
15400
18695
|
function caminhoToken() {
|
|
15401
|
-
return
|
|
18696
|
+
return join4(dirConfig(), "token");
|
|
15402
18697
|
}
|
|
15403
18698
|
function lerToken() {
|
|
15404
18699
|
const env = process.env["VIBECHECK_TOKEN"];
|
|
@@ -15407,7 +18702,7 @@ function lerToken() {
|
|
|
15407
18702
|
return { token: env, host: hostEnv ?? "" };
|
|
15408
18703
|
}
|
|
15409
18704
|
try {
|
|
15410
|
-
const bruto =
|
|
18705
|
+
const bruto = readFileSync4(caminhoToken(), "utf8").trim();
|
|
15411
18706
|
const [token, host] = bruto.split("\n");
|
|
15412
18707
|
if (token === void 0 || token.length === 0) return null;
|
|
15413
18708
|
return { token, host: host ?? "" };
|
|
@@ -15417,9 +18712,9 @@ function lerToken() {
|
|
|
15417
18712
|
}
|
|
15418
18713
|
function gravarToken(token, host) {
|
|
15419
18714
|
const dir = dirConfig();
|
|
15420
|
-
|
|
18715
|
+
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
15421
18716
|
const destino = caminhoToken();
|
|
15422
|
-
|
|
18717
|
+
writeFileSync3(destino, `${token}
|
|
15423
18718
|
${host}
|
|
15424
18719
|
`, { encoding: "utf8", mode: 384 });
|
|
15425
18720
|
chmodSync(destino, 384);
|
|
@@ -15429,14 +18724,14 @@ ${host}
|
|
|
15429
18724
|
var ARQUIVO = "code-consent.json";
|
|
15430
18725
|
var ENV_CI_ALLOW = "VIBECHECK_ALLOW_CODE_SCAN";
|
|
15431
18726
|
function caminho(deps) {
|
|
15432
|
-
return
|
|
18727
|
+
return join5((deps.configDir ?? dirConfig)(), ARQUIVO);
|
|
15433
18728
|
}
|
|
15434
18729
|
function repoKey(dir) {
|
|
15435
|
-
return
|
|
18730
|
+
return resolve3(dir);
|
|
15436
18731
|
}
|
|
15437
18732
|
function listarConsentimentos(deps = {}) {
|
|
15438
18733
|
try {
|
|
15439
|
-
const bruto = JSON.parse(
|
|
18734
|
+
const bruto = JSON.parse(readFileSync5(caminho(deps), "utf8"));
|
|
15440
18735
|
if (!Array.isArray(bruto)) return [];
|
|
15441
18736
|
return bruto.filter(
|
|
15442
18737
|
(e) => e !== null && typeof e === "object" && typeof e.repo === "string" && typeof e.acceptedAt === "string"
|
|
@@ -15447,9 +18742,9 @@ function listarConsentimentos(deps = {}) {
|
|
|
15447
18742
|
}
|
|
15448
18743
|
function gravar(entradas, deps) {
|
|
15449
18744
|
const dir = (deps.configDir ?? dirConfig)();
|
|
15450
|
-
|
|
15451
|
-
const arquivo =
|
|
15452
|
-
|
|
18745
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
18746
|
+
const arquivo = join5(dir, ARQUIVO);
|
|
18747
|
+
writeFileSync4(arquivo, `${JSON.stringify(entradas, null, 2)}
|
|
15453
18748
|
`, { mode: 384 });
|
|
15454
18749
|
chmodSync2(arquivo, 384);
|
|
15455
18750
|
}
|
|
@@ -15547,227 +18842,26 @@ async function followScan(base, scanId, shareToken, options = {}) {
|
|
|
15547
18842
|
}
|
|
15548
18843
|
|
|
15549
18844
|
// 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
|
|
18845
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
18846
|
+
import { join as join6 } from "path";
|
|
15753
18847
|
var GATE_DIR = ".vibecheck";
|
|
15754
18848
|
var GATE_JSON = "quality-gate.json";
|
|
15755
18849
|
var GATE_SARIF = "quality-gate.sarif";
|
|
15756
|
-
function escreverArtefatos(dir, report,
|
|
18850
|
+
function escreverArtefatos(dir, report, sarif2, deps) {
|
|
15757
18851
|
deps.writeArtifact(
|
|
15758
|
-
|
|
18852
|
+
join6(dir, GATE_DIR, GATE_JSON),
|
|
15759
18853
|
`${JSON.stringify(report, null, 2)}
|
|
15760
18854
|
`
|
|
15761
18855
|
);
|
|
15762
18856
|
deps.writeArtifact(
|
|
15763
|
-
|
|
15764
|
-
`${JSON.stringify(
|
|
18857
|
+
join6(dir, GATE_DIR, GATE_SARIF),
|
|
18858
|
+
`${JSON.stringify(sarif2, null, 2)}
|
|
15765
18859
|
`
|
|
15766
18860
|
);
|
|
15767
18861
|
}
|
|
15768
18862
|
function gravarNoDisco(caminho2, conteudo) {
|
|
15769
|
-
|
|
15770
|
-
|
|
18863
|
+
mkdirSync5(join6(caminho2, ".."), { recursive: true });
|
|
18864
|
+
writeFileSync5(caminho2, conteudo);
|
|
15771
18865
|
}
|
|
15772
18866
|
async function rodarHttp(input, grafo, deps) {
|
|
15773
18867
|
const headers = {
|
|
@@ -15845,7 +18939,9 @@ async function runGate(input, deps) {
|
|
|
15845
18939
|
const grafo = await deps.extractGraph(input.dir);
|
|
15846
18940
|
const metrics = grafo === null ? null : graphMetrics(grafo);
|
|
15847
18941
|
const repo = deps.readRepo(input.dir);
|
|
15848
|
-
const codeIssues = runCodeChecks(repo
|
|
18942
|
+
const codeIssues = runCodeChecks(repo, void 0, {
|
|
18943
|
+
failOnError: true
|
|
18944
|
+
}).map((i) => ({
|
|
15849
18945
|
checkId: i.checkId,
|
|
15850
18946
|
severity: i.severity,
|
|
15851
18947
|
path: i.path,
|
|
@@ -15897,31 +18993,31 @@ async function enviarGate(scanId, report, input, deps) {
|
|
|
15897
18993
|
}
|
|
15898
18994
|
|
|
15899
18995
|
// src/graphify.ts
|
|
15900
|
-
import { spawn } from "child_process";
|
|
18996
|
+
import { spawn as spawn2 } from "child_process";
|
|
15901
18997
|
import { readFile } from "fs/promises";
|
|
15902
|
-
import { join as
|
|
18998
|
+
import { join as join7 } from "path";
|
|
15903
18999
|
async function tentarGraphify(raiz) {
|
|
15904
|
-
const json2 =
|
|
19000
|
+
const json2 = join7(raiz, "graphify-out", "graph.json");
|
|
15905
19001
|
try {
|
|
15906
19002
|
return JSON.parse(await readFile(json2, "utf8"));
|
|
15907
19003
|
} catch {
|
|
15908
19004
|
}
|
|
15909
|
-
const ok = await new Promise((
|
|
15910
|
-
const child =
|
|
19005
|
+
const ok = await new Promise((resolve4) => {
|
|
19006
|
+
const child = spawn2("graphify", [".", "--no-viz"], {
|
|
15911
19007
|
cwd: raiz,
|
|
15912
19008
|
stdio: "ignore"
|
|
15913
19009
|
});
|
|
15914
19010
|
const timer = setTimeout(() => {
|
|
15915
19011
|
child.kill();
|
|
15916
|
-
|
|
19012
|
+
resolve4(false);
|
|
15917
19013
|
}, 2e4);
|
|
15918
19014
|
child.on("exit", (code) => {
|
|
15919
19015
|
clearTimeout(timer);
|
|
15920
|
-
|
|
19016
|
+
resolve4(code === 0);
|
|
15921
19017
|
});
|
|
15922
19018
|
child.on("error", () => {
|
|
15923
19019
|
clearTimeout(timer);
|
|
15924
|
-
|
|
19020
|
+
resolve4(false);
|
|
15925
19021
|
});
|
|
15926
19022
|
});
|
|
15927
19023
|
if (!ok) return null;
|
|
@@ -15933,7 +19029,7 @@ async function tentarGraphify(raiz) {
|
|
|
15933
19029
|
}
|
|
15934
19030
|
|
|
15935
19031
|
// src/login.ts
|
|
15936
|
-
import { spawn as
|
|
19032
|
+
import { spawn as spawn3 } from "child_process";
|
|
15937
19033
|
function urlBase() {
|
|
15938
19034
|
return (process.env["VIBECHECK_URL"] ?? "https://vibecheck.kinkai.cloud").replace(
|
|
15939
19035
|
/\/$/,
|
|
@@ -15953,12 +19049,12 @@ function comandosAbrirBrowser(url2, platform = process.platform, env = process.e
|
|
|
15953
19049
|
return [["xdg-open", [url2]]];
|
|
15954
19050
|
}
|
|
15955
19051
|
function tentarSpawn(cmd, args) {
|
|
15956
|
-
return new Promise((
|
|
15957
|
-
const child =
|
|
15958
|
-
child.once("error", () =>
|
|
19052
|
+
return new Promise((resolve4) => {
|
|
19053
|
+
const child = spawn3(cmd, args, { stdio: "ignore", detached: true });
|
|
19054
|
+
child.once("error", () => resolve4(false));
|
|
15959
19055
|
child.once("spawn", () => {
|
|
15960
19056
|
child.unref();
|
|
15961
|
-
|
|
19057
|
+
resolve4(true);
|
|
15962
19058
|
});
|
|
15963
19059
|
});
|
|
15964
19060
|
}
|
|
@@ -16018,8 +19114,8 @@ async function loginDevice(ci, options = {}) {
|
|
|
16018
19114
|
|
|
16019
19115
|
// src/repo.ts
|
|
16020
19116
|
import { execFileSync } from "child_process";
|
|
16021
|
-
import { lstatSync, readdirSync, readFileSync as
|
|
16022
|
-
import { join as
|
|
19117
|
+
import { lstatSync as lstatSync3, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
19118
|
+
import { join as join8, relative as relative2 } from "path";
|
|
16023
19119
|
var MAX_FILES = 800;
|
|
16024
19120
|
var MAX_BYTES2 = 64e3;
|
|
16025
19121
|
var MAX_SQL_BYTES = 512e3;
|
|
@@ -16053,23 +19149,23 @@ function walk(dir, raiz, out) {
|
|
|
16053
19149
|
if (out.length >= MAX_FILES) return;
|
|
16054
19150
|
let entradas;
|
|
16055
19151
|
try {
|
|
16056
|
-
entradas =
|
|
19152
|
+
entradas = readdirSync2(dir);
|
|
16057
19153
|
} catch {
|
|
16058
19154
|
return;
|
|
16059
19155
|
}
|
|
16060
19156
|
for (const nome of entradas) {
|
|
16061
19157
|
if (out.length >= MAX_FILES) return;
|
|
16062
19158
|
if (SKIP_DIR.has(nome) || nome.startsWith(".") && nome !== ".env") continue;
|
|
16063
|
-
const caminho2 =
|
|
19159
|
+
const caminho2 = join8(dir, nome);
|
|
16064
19160
|
let st;
|
|
16065
19161
|
try {
|
|
16066
|
-
st =
|
|
19162
|
+
st = lstatSync3(caminho2);
|
|
16067
19163
|
} catch {
|
|
16068
19164
|
continue;
|
|
16069
19165
|
}
|
|
16070
19166
|
if (st.isSymbolicLink()) continue;
|
|
16071
19167
|
if (st.isDirectory()) walk(caminho2, raiz, out);
|
|
16072
|
-
else if (SOURCE.test(nome)) out.push(
|
|
19168
|
+
else if (SOURCE.test(nome)) out.push(relative2(raiz, caminho2).split("\\").join("/"));
|
|
16073
19169
|
}
|
|
16074
19170
|
}
|
|
16075
19171
|
function readCodeContext(dir) {
|
|
@@ -16079,9 +19175,9 @@ function readCodeContext(dir) {
|
|
|
16079
19175
|
const files = [];
|
|
16080
19176
|
for (const path of caminhos) {
|
|
16081
19177
|
try {
|
|
16082
|
-
const st =
|
|
19178
|
+
const st = lstatSync3(join8(dir, path));
|
|
16083
19179
|
if (st.size > (path.endsWith(".sql") ? MAX_SQL_BYTES : MAX_BYTES2)) continue;
|
|
16084
|
-
files.push({ path, content:
|
|
19180
|
+
files.push({ path, content: readFileSync6(join8(dir, path), "utf8") });
|
|
16085
19181
|
} catch {
|
|
16086
19182
|
}
|
|
16087
19183
|
}
|
|
@@ -16107,7 +19203,7 @@ function regras(issues) {
|
|
|
16107
19203
|
for (const issue2 of issues) {
|
|
16108
19204
|
if (!vistos.has(issue2.checkId)) vistos.set(issue2.checkId, issue2.reason);
|
|
16109
19205
|
}
|
|
16110
|
-
return [...vistos].map(([id,
|
|
19206
|
+
return [...vistos].map(([id, text2]) => ({ id, shortDescription: { text: text2 } }));
|
|
16111
19207
|
}
|
|
16112
19208
|
function toSarif(report, version2) {
|
|
16113
19209
|
return {
|
|
@@ -16135,8 +19231,8 @@ function toSarif(report, version2) {
|
|
|
16135
19231
|
}
|
|
16136
19232
|
|
|
16137
19233
|
// src/walker.ts
|
|
16138
|
-
import { lstatSync as
|
|
16139
|
-
import { join as
|
|
19234
|
+
import { lstatSync as lstatSync4, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
|
|
19235
|
+
import { join as join9, relative as relative3 } from "path";
|
|
16140
19236
|
var IGNORAR = /* @__PURE__ */ new Set([
|
|
16141
19237
|
"node_modules",
|
|
16142
19238
|
".git",
|
|
@@ -16171,16 +19267,16 @@ var HANDLERS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE",
|
|
|
16171
19267
|
function listar(raiz, dir, acc) {
|
|
16172
19268
|
let nomes;
|
|
16173
19269
|
try {
|
|
16174
|
-
nomes =
|
|
19270
|
+
nomes = readdirSync3(dir);
|
|
16175
19271
|
} catch {
|
|
16176
19272
|
return;
|
|
16177
19273
|
}
|
|
16178
19274
|
for (const nome of nomes) {
|
|
16179
19275
|
if (IGNORAR.has(nome) || nome.startsWith(".")) continue;
|
|
16180
|
-
const cheio =
|
|
19276
|
+
const cheio = join9(dir, nome);
|
|
16181
19277
|
let stat;
|
|
16182
19278
|
try {
|
|
16183
|
-
stat =
|
|
19279
|
+
stat = lstatSync4(cheio);
|
|
16184
19280
|
} catch {
|
|
16185
19281
|
continue;
|
|
16186
19282
|
}
|
|
@@ -16194,7 +19290,7 @@ function listar(raiz, dir, acc) {
|
|
|
16194
19290
|
}
|
|
16195
19291
|
}
|
|
16196
19292
|
function posixRel(raiz, cheio) {
|
|
16197
|
-
return
|
|
19293
|
+
return relative3(raiz, cheio).replaceAll("\\", "/");
|
|
16198
19294
|
}
|
|
16199
19295
|
function dirnamePosix(caminho2) {
|
|
16200
19296
|
const i = caminho2.lastIndexOf("/");
|
|
@@ -16266,12 +19362,12 @@ function resolverSpec(de, spec, indice) {
|
|
|
16266
19362
|
}
|
|
16267
19363
|
return null;
|
|
16268
19364
|
}
|
|
16269
|
-
function rotuloDe(rel,
|
|
19365
|
+
function rotuloDe(rel, exports2) {
|
|
16270
19366
|
const rota = routeFromFile(rel);
|
|
16271
|
-
const handlers =
|
|
19367
|
+
const handlers = exports2.filter((e) => HANDLERS.has(e));
|
|
16272
19368
|
if (rota !== null && handlers.length > 0) return `${handlers[0]} ${rota}`;
|
|
16273
19369
|
if (rota !== null) return rota;
|
|
16274
|
-
const simbolo =
|
|
19370
|
+
const simbolo = exports2.find((e) => !HANDLERS.has(e));
|
|
16275
19371
|
if (simbolo !== void 0) return simbolo;
|
|
16276
19372
|
const base = rel.split("/").at(-1)?.replace(/\.(m?[jt]sx?)$/i, "");
|
|
16277
19373
|
return base !== void 0 && base.length > 0 && base !== "index" ? base : rel;
|
|
@@ -16280,16 +19376,16 @@ function lerArquivo(raiz, cheio) {
|
|
|
16280
19376
|
const rel = posixRel(raiz, cheio);
|
|
16281
19377
|
let conteudo = "";
|
|
16282
19378
|
try {
|
|
16283
|
-
conteudo =
|
|
19379
|
+
conteudo = readFileSync7(cheio, "utf8").slice(0, MAX_BYTES_ARQUIVO);
|
|
16284
19380
|
} catch {
|
|
16285
19381
|
return null;
|
|
16286
19382
|
}
|
|
16287
|
-
const
|
|
19383
|
+
const exports2 = exportsDe(conteudo);
|
|
16288
19384
|
const specs = specsDe(conteudo);
|
|
16289
19385
|
return {
|
|
16290
19386
|
rel,
|
|
16291
|
-
label: rotuloDe(rel,
|
|
16292
|
-
exports,
|
|
19387
|
+
label: rotuloDe(rel, exports2),
|
|
19388
|
+
exports: exports2,
|
|
16293
19389
|
importsRel: specs.filter(
|
|
16294
19390
|
(s) => s.startsWith(".") || s.startsWith("@/") || s.startsWith("~/")
|
|
16295
19391
|
),
|
|
@@ -16402,11 +19498,14 @@ function isAdvancedScan(grafo) {
|
|
|
16402
19498
|
}
|
|
16403
19499
|
|
|
16404
19500
|
// src/cli.ts
|
|
16405
|
-
var VERSION = "0.
|
|
19501
|
+
var VERSION = "0.3.1";
|
|
16406
19502
|
var AJUDA = `vibecheck \u2014 security check for AI-built apps
|
|
16407
19503
|
|
|
16408
19504
|
npx @kinkai.cloud/vibecheck [options]
|
|
16409
19505
|
|
|
19506
|
+
Commands
|
|
19507
|
+
analyze project quality and security analysis (analyze --help)
|
|
19508
|
+
|
|
16410
19509
|
Options
|
|
16411
19510
|
--url <url> target; defaults to https://<verified host of the token>
|
|
16412
19511
|
--dir <path> repository root (default: current directory)
|
|
@@ -16421,6 +19520,7 @@ Options
|
|
|
16421
19520
|
|
|
16422
19521
|
Environment
|
|
16423
19522
|
VIBECHECK_TOKEN, VIBECHECK_HOST, VIBECHECK_URL
|
|
19523
|
+
VIBECHECK_PROJECT_TOKEN project token for analyze --publish
|
|
16424
19524
|
VIBECHECK_ALLOW_CODE_SCAN=1 required by --gate in CI
|
|
16425
19525
|
`;
|
|
16426
19526
|
function arg(nome, argv) {
|
|
@@ -16476,6 +19576,7 @@ async function comandoConsentimento(acao, raiz) {
|
|
|
16476
19576
|
return GATE_EXIT.operational;
|
|
16477
19577
|
}
|
|
16478
19578
|
async function main(argv) {
|
|
19579
|
+
if (argv[0] === "analyze") return analyzeCommand(argv.slice(1));
|
|
16479
19580
|
if (temFlag("--help", argv) || temFlag("-h", argv)) {
|
|
16480
19581
|
console.warn(AJUDA);
|
|
16481
19582
|
return 0;
|