canship 0.7.0 → 0.7.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-zh-CN.md +9 -5
- package/README.md +9 -5
- package/dist/cli.js +275 -180
- package/dist/index.js +274 -179
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3180,9 +3180,32 @@ function synchronousLocal(file, expr) {
|
|
|
3180
3180
|
const code = noiseMaskedOf(file);
|
|
3181
3181
|
const escaped = name.replace(/\$/g, "\\$");
|
|
3182
3182
|
const declared = new RegExp(`(?:^|[^\\w$])(async\\s+)?function\\s*\\*?\\s*${escaped}\\s*\\(`).exec(code);
|
|
3183
|
-
if (declared) return
|
|
3183
|
+
if (declared?.[1]) return false;
|
|
3184
3184
|
const assigned = new RegExp(`\\b(?:const|let|var)\\s+${escaped}\\s*(?::[^=;]{0,200})?=\\s*(async\\b)?\\s*(?:function\\b|\\([^()]*\\)\\s*(?::[^=;{]{0,200})?=>|[A-Za-z_$][\\w$]*\\s*=>)`).exec(code);
|
|
3185
|
-
|
|
3185
|
+
if (!declared && (!assigned || assigned[1])) return false;
|
|
3186
|
+
const declaration = declared ?? assigned;
|
|
3187
|
+
const pairedEnd = (open, left, right) => {
|
|
3188
|
+
let depth = 0;
|
|
3189
|
+
for (let i = open; i < Math.min(code.length, open + 65536); i++) {
|
|
3190
|
+
if (code[i] === left) depth++;
|
|
3191
|
+
else if (code[i] === right && --depth === 0) return i;
|
|
3192
|
+
}
|
|
3193
|
+
return -1;
|
|
3194
|
+
};
|
|
3195
|
+
let start = declaration.index + declaration[0].length;
|
|
3196
|
+
if (declared) start = pairedEnd(start - 1, "(", ")") + 1;
|
|
3197
|
+
else if (/function\b$/.test(declaration[0])) {
|
|
3198
|
+
const open = code.indexOf("(", start);
|
|
3199
|
+
start = open < 0 ? 0 : pairedEnd(open, "(", ")") + 1;
|
|
3200
|
+
}
|
|
3201
|
+
if (start === 0) return false;
|
|
3202
|
+
start += /^\s*(?::[\w\s.<>,[\]|?]+)?\s*/.exec(code.slice(start))[0].length;
|
|
3203
|
+
const close = code[start] === "{" ? pairedEnd(start, "{", "}") : -1;
|
|
3204
|
+
if (code[start] === "{" && close < 0) return false;
|
|
3205
|
+
const body = close >= 0 ? code.slice(start, close + 1) : `return ${code.slice(start).split(/[;\n]/)[0] ?? ""}`;
|
|
3206
|
+
if (/\b(?:Promise|then|await|new|function|async)\b|=>|\.\.\./.test(body)) return false;
|
|
3207
|
+
if ([...body.matchAll(/([\w$]+)\s*\(/g)].some((m) => !/^(?:if|switch)$/.test(m[1]))) return false;
|
|
3208
|
+
return /\breturn\s+(?:null|false|true|\{|[\w$]+\s*\?)/.test(body);
|
|
3186
3209
|
}
|
|
3187
3210
|
var limited = /* @__PURE__ */ new WeakSet();
|
|
3188
3211
|
var cache2 = /* @__PURE__ */ new WeakMap();
|
|
@@ -3618,11 +3641,13 @@ function usePrefixes(a, arg) {
|
|
|
3618
3641
|
if (arg.text.startsWith("[")) {
|
|
3619
3642
|
const close = a.pairs.get(arg.at);
|
|
3620
3643
|
const items = close === void 0 ? [] : argsOf(a, arg.at, close);
|
|
3621
|
-
if (items.length === 0 || !items.
|
|
3644
|
+
if (items.length === 0 || !items.some((item) => /^['"`]/.test(item.source))) return null;
|
|
3622
3645
|
return items.map((item) => pathOf(a, item)).filter((path) => path !== null);
|
|
3623
3646
|
}
|
|
3624
3647
|
const prefix = pathOf(a, arg);
|
|
3625
|
-
|
|
3648
|
+
if (prefix !== null) return [prefix];
|
|
3649
|
+
if (/^['"`/]|^new\s+RegExp\b|^(?:process\s*\.\s*env|import\s*\.\s*meta\s*\.\s*env|(?:[\w$]+\s*\.\s*)?env)\s*[.[]/.test(arg.source)) return [];
|
|
3650
|
+
return null;
|
|
3626
3651
|
}
|
|
3627
3652
|
function pathOf(a, arg) {
|
|
3628
3653
|
if (arg.text.startsWith("[")) return null;
|
|
@@ -4088,7 +4113,7 @@ function opsOf(site) {
|
|
|
4088
4113
|
return result;
|
|
4089
4114
|
}
|
|
4090
4115
|
function generalMiddleware(site, before, files) {
|
|
4091
|
-
const args = opsOf(site).uses.filter((use) => !use.mount && use.at < before && (use.prefix === null || use.prefix === "/" || /^\/?\*$/.test(use.prefix))).flatMap((use) => use.args).filter((arg) => site.framework !== "express" || mountedFile(site.a, arg, files) === null);
|
|
4116
|
+
const args = opsOf(site).uses.filter((use) => !use.mount && use.at < before && (use.prefix === null || site.framework !== "hono" && use.prefix === "/" || /^\/?\*$/.test(use.prefix))).flatMap((use) => use.args).filter((arg) => site.framework !== "express" || mountedFile(site.a, arg, files) === null);
|
|
4092
4117
|
return toRefs(site.a, args);
|
|
4093
4118
|
}
|
|
4094
4119
|
function passedSites(site, files) {
|
|
@@ -4276,8 +4301,7 @@ function serverIndex(files) {
|
|
|
4276
4301
|
};
|
|
4277
4302
|
add2(handler, callbacks);
|
|
4278
4303
|
callbacks.forEach((callback, index2) => {
|
|
4279
|
-
|
|
4280
|
-
const range = functionAt(a, callback.at, callback.at + callback.text.length);
|
|
4304
|
+
const range = handlerOf(a, callback, files);
|
|
4281
4305
|
if (range) add2(range, callbacks.slice(0, index2));
|
|
4282
4306
|
});
|
|
4283
4307
|
}
|
|
@@ -4330,25 +4354,37 @@ function serverRoutesOf(file, files) {
|
|
|
4330
4354
|
const actions = serverActionRoutes(file);
|
|
4331
4355
|
return actions.length ? actions : nodeRoutesFor(file, files);
|
|
4332
4356
|
}
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4357
|
+
function decoratorOf(ref, receiver, name, files) {
|
|
4358
|
+
const a = analyse(ref.file);
|
|
4359
|
+
const owner = (at) => a.bodies.filter((b) => b.start < at && at < b.end).sort((x, y) => y.start - x.start)[0];
|
|
4360
|
+
const pattern = new RegExp(`(?<![\\w$.])${receiver.replace(/\$/g, "\\$")}\\s*\\.\\s*decorate\\s*\\(\\s*['"]${name.replace(/\$/g, "\\$")}['"]\\s*,`, "g");
|
|
4361
|
+
let result = null;
|
|
4362
|
+
for (const m of a.source.matchAll(pattern)) {
|
|
4363
|
+
if (m.index >= ref.at || owner(m.index) !== owner(ref.at)) continue;
|
|
4364
|
+
const open = a.source.indexOf("(", m.index);
|
|
4365
|
+
const close = a.pairs.get(open);
|
|
4366
|
+
const value = close === void 0 ? void 0 : argsOf(a, open, close)[1];
|
|
4367
|
+
result = value ? handlerOf(a, value, files) : null;
|
|
4368
|
+
}
|
|
4369
|
+
return result;
|
|
4370
|
+
}
|
|
4371
|
+
var authPluginCache = /* @__PURE__ */ new WeakMap();
|
|
4372
|
+
function registeredAuthPlugin(ref, pkg) {
|
|
4373
|
+
const a = analyse(ref.file);
|
|
4374
|
+
const receiver = /^([A-Za-z_$][\w$]*)\s*\./.exec(ref.text)?.[1];
|
|
4375
|
+
if (!receiver) return false;
|
|
4376
|
+
let cache3 = authPluginCache.get(ref.file);
|
|
4336
4377
|
if (!cache3) {
|
|
4337
4378
|
cache3 = /* @__PURE__ */ new Map();
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
const close = a.pairs.get(open);
|
|
4345
|
-
const value = close === void 0 ? void 0 : argsOf(a, open, close)[1];
|
|
4346
|
-
cache3.set(m[1], value ? handlerOf(a, value, files) : null);
|
|
4347
|
-
}
|
|
4348
|
-
}
|
|
4349
|
-
decoratorCache.set(files, cache3);
|
|
4379
|
+
authPluginCache.set(ref.file, cache3);
|
|
4380
|
+
}
|
|
4381
|
+
let registers = cache3.get(receiver);
|
|
4382
|
+
if (!registers) {
|
|
4383
|
+
registers = fastifyOps(a, receiver).registers;
|
|
4384
|
+
cache3.set(receiver, registers);
|
|
4350
4385
|
}
|
|
4351
|
-
|
|
4386
|
+
const owner = (at) => a.bodies.filter((b) => b.start < at && at < b.end).sort((x, y) => y.start - x.start)[0];
|
|
4387
|
+
return registers.some((reg) => reg.at < ref.at && owner(reg.at) === owner(ref.at) && /^[A-Za-z_$][\w$]*$/.test(reg.target.text) && packageOf(a, reg.target.text) === pkg);
|
|
4352
4388
|
}
|
|
4353
4389
|
function authCompositionOf(ref) {
|
|
4354
4390
|
const a = analyse(ref.file);
|
|
@@ -4382,7 +4418,7 @@ function middlewareDefinition(ref, files) {
|
|
|
4382
4418
|
const callee = member ?? root;
|
|
4383
4419
|
const spec = packageOf(a, root);
|
|
4384
4420
|
if (member && (spec === null || spec === "fastify")) {
|
|
4385
|
-
const decorated = decoratorOf(member, files);
|
|
4421
|
+
const decorated = decoratorOf(ref, root, member, files);
|
|
4386
4422
|
if (decorated) return { name: callee, range: decorated, spec: null, inline };
|
|
4387
4423
|
}
|
|
4388
4424
|
if (spec) return { name: callee, range: null, spec, inline };
|
|
@@ -4399,6 +4435,181 @@ function propertyValue(a, name) {
|
|
|
4399
4435
|
return { file: a.file, start: from, end: expressionEnd(a, from) - 1 };
|
|
4400
4436
|
}
|
|
4401
4437
|
|
|
4438
|
+
// src/rules/verification.ts
|
|
4439
|
+
var LocalVerification = class {
|
|
4440
|
+
constructor(file, responseExits = false) {
|
|
4441
|
+
this.responseExits = responseExits;
|
|
4442
|
+
this.code = noiseMaskedOf(file);
|
|
4443
|
+
this.source = commentsMaskedOf(file);
|
|
4444
|
+
this.pairs = delimiterPairs(this.code);
|
|
4445
|
+
this.bodies = functionBodies(this.code, this.pairs);
|
|
4446
|
+
}
|
|
4447
|
+
responseExits;
|
|
4448
|
+
code;
|
|
4449
|
+
source;
|
|
4450
|
+
pairs;
|
|
4451
|
+
bodies;
|
|
4452
|
+
owner(at) {
|
|
4453
|
+
return this.bodies.filter((b) => b.start < at && at < b.end).sort((a, b) => b.start - a.start)[0];
|
|
4454
|
+
}
|
|
4455
|
+
skipSpace(at) {
|
|
4456
|
+
while (/\s/.test(this.code[at] ?? "")) at++;
|
|
4457
|
+
return at;
|
|
4458
|
+
}
|
|
4459
|
+
endOf(at, limit = this.code.length) {
|
|
4460
|
+
if (this.code[at] === "{") return (this.pairs.get(at) ?? limit - 1) + 1;
|
|
4461
|
+
for (let i = at; i < limit; i++) {
|
|
4462
|
+
if (/[;\n}]/.test(this.code[i])) return i;
|
|
4463
|
+
const end = this.pairs.get(i);
|
|
4464
|
+
if (end !== void 0) i = end;
|
|
4465
|
+
}
|
|
4466
|
+
return limit;
|
|
4467
|
+
}
|
|
4468
|
+
/**
|
|
4469
|
+
* 块的顶层有无条件退出即算退出,前面可以有记录日志等语句;
|
|
4470
|
+
* 嵌套分支、函数中的 return 以及无花括号 if/else/循环控制的 return 不算。
|
|
4471
|
+
*/
|
|
4472
|
+
exits(from, to) {
|
|
4473
|
+
let at = this.skipSpace(from);
|
|
4474
|
+
if (this.code[at] !== "{") return /^(?:return|throw)\b|^(?:redirect|notFound)\s*\(/.test(this.code.slice(at, to));
|
|
4475
|
+
const end = Math.min(to, this.pairs.get(at) ?? to);
|
|
4476
|
+
at = this.skipSpace(at + 1);
|
|
4477
|
+
while (at < end) {
|
|
4478
|
+
const statement = this.code.slice(at, this.statementEnd(at, end));
|
|
4479
|
+
if (/^return\b/.test(statement) && this.responseExits) {
|
|
4480
|
+
if (/^return\s+(?:(?:await\s+)?(?:reply|res|response)\s*\.[\s\S]*\b(?:send|end|json|sendStatus)\s*\(|(?:c|ctx|context)\s*\.\s*(?:json|text|body|html|redirect|newResponse)\s*\(|new\s+Response\s*\(|next\s*\(\s*[^)\s])/.test(statement)) return true;
|
|
4481
|
+
return false;
|
|
4482
|
+
}
|
|
4483
|
+
if (/^(?:return|throw)\b|^(?:redirect|notFound)\s*\(/.test(statement)) return true;
|
|
4484
|
+
if (this.responseExits && /^reply\s*\.\s*(?:(?:code|status)\s*\([^)]*\)\s*\.\s*)?send\s*\(/.test(this.code.slice(at, end))) return true;
|
|
4485
|
+
const next = this.statementEnd(at, end);
|
|
4486
|
+
if (next <= at) return false;
|
|
4487
|
+
at = this.skipSpace(next);
|
|
4488
|
+
}
|
|
4489
|
+
return false;
|
|
4490
|
+
}
|
|
4491
|
+
/** 跳过完整语句;分支、循环和函数内的退出不提升为外层退出。 */
|
|
4492
|
+
statementEnd(at, limit, depth = 0) {
|
|
4493
|
+
if (depth >= 64) return limit;
|
|
4494
|
+
if (this.code[at] === "{") return (this.pairs.get(at) ?? limit - 1) + 1;
|
|
4495
|
+
const control = /^(if|for(?:\s+await)?|while|with|switch)\s*\(/.exec(this.code.slice(at, at + 80));
|
|
4496
|
+
if (control) {
|
|
4497
|
+
const close = this.pairs.get(at + control[0].length - 1);
|
|
4498
|
+
if (close === void 0) return limit;
|
|
4499
|
+
let end = this.statementEnd(this.skipSpace(close + 1), limit, depth + 1);
|
|
4500
|
+
const next = this.skipSpace(end);
|
|
4501
|
+
if (control[1] === "if" && /^else\b/.test(this.code.slice(next, next + 5))) {
|
|
4502
|
+
end = this.statementEnd(this.skipSpace(next + 4), limit, depth + 1);
|
|
4503
|
+
}
|
|
4504
|
+
return end;
|
|
4505
|
+
}
|
|
4506
|
+
if (/^(?:try|do)\b/.test(this.code.slice(at, at + 8))) return limit;
|
|
4507
|
+
for (let i = at; i < limit; i++) {
|
|
4508
|
+
if (this.code[i] === ";") return i + 1;
|
|
4509
|
+
const close = this.pairs.get(i);
|
|
4510
|
+
if (close !== void 0) {
|
|
4511
|
+
i = close;
|
|
4512
|
+
continue;
|
|
4513
|
+
}
|
|
4514
|
+
if (this.code[i] !== "\n") continue;
|
|
4515
|
+
const before = this.code.slice(at, i).trimEnd();
|
|
4516
|
+
const after = this.code.slice(i + 1, limit).trimStart();
|
|
4517
|
+
if (!before || /[=+\-*/%&|^!?:,.([{<>]$/.test(before)) continue;
|
|
4518
|
+
if (/^[.?+\-*/%&|^,:<>=([{`]/.test(after)) continue;
|
|
4519
|
+
if (/\b(?:const|let|var|function|async|await|new|yield)$/.test(before)) continue;
|
|
4520
|
+
return i + 1;
|
|
4521
|
+
}
|
|
4522
|
+
return limit;
|
|
4523
|
+
}
|
|
4524
|
+
/** 验证与使用须在同一函数,且不能依赖可选分支或被吞掉的异常。 */
|
|
4525
|
+
enforcedBefore(from, to) {
|
|
4526
|
+
const owner = this.owner(from);
|
|
4527
|
+
if (from >= to || owner !== this.owner(to)) return false;
|
|
4528
|
+
const start = owner ? owner.start + 1 : 0;
|
|
4529
|
+
for (let i = start; i < from; i++) {
|
|
4530
|
+
const conditional = /^(?:if|for|while|switch)\s*\(/.exec(this.code.slice(i, i + 32));
|
|
4531
|
+
if (conditional && !/[\w$]/.test(this.code[i - 1] ?? "")) {
|
|
4532
|
+
const close = this.pairs.get(i + conditional[0].length - 1);
|
|
4533
|
+
if (close !== void 0) {
|
|
4534
|
+
const body = this.skipSpace(close + 1);
|
|
4535
|
+
const end2 = this.endOf(body);
|
|
4536
|
+
if (from < end2 && to >= end2) return false;
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
const end = this.pairs.get(i);
|
|
4540
|
+
if (end === void 0) continue;
|
|
4541
|
+
if (end < from) {
|
|
4542
|
+
i = end;
|
|
4543
|
+
continue;
|
|
4544
|
+
}
|
|
4545
|
+
if (end >= to) continue;
|
|
4546
|
+
if (this.code[i] === "(" && !/(?:\?\.|\b(?:if|for|while|switch|catch))\s*$/.test(this.code.slice(Math.max(start, i - 20), i))) continue;
|
|
4547
|
+
if (this.code[i] !== "{" || !/\btry\s*$/.test(this.code.slice(Math.max(start, i - 16), i))) return false;
|
|
4548
|
+
let next = this.skipSpace(end + 1);
|
|
4549
|
+
if (/^catch\b/.test(this.code.slice(next))) {
|
|
4550
|
+
next = this.skipSpace(next + 5);
|
|
4551
|
+
if (this.code[next] === "(") next = this.skipSpace((this.pairs.get(next) ?? next) + 1);
|
|
4552
|
+
const caughtEnd = this.endOf(next);
|
|
4553
|
+
if (!this.exits(next, caughtEnd)) return false;
|
|
4554
|
+
next = this.skipSpace(caughtEnd);
|
|
4555
|
+
}
|
|
4556
|
+
if (/^finally\b/.test(this.code.slice(next))) return false;
|
|
4557
|
+
}
|
|
4558
|
+
let boundary = start;
|
|
4559
|
+
for (const [open, close] of this.pairs) {
|
|
4560
|
+
if (this.code[open] === "{" && open < from && close > from) boundary = Math.max(boundary, open + 1);
|
|
4561
|
+
}
|
|
4562
|
+
while (boundary < from) {
|
|
4563
|
+
const next = this.statementEnd(this.skipSpace(boundary), this.code.length);
|
|
4564
|
+
if (next <= boundary || next > from) break;
|
|
4565
|
+
boundary = next;
|
|
4566
|
+
}
|
|
4567
|
+
return !/[?]|&&|\|\||=>/.test(this.code.slice(boundary, from));
|
|
4568
|
+
}
|
|
4569
|
+
/** 找出当前函数中最后一次赋值;未知或条件覆盖同样会使旧证明失效。 */
|
|
4570
|
+
assignment(name, at) {
|
|
4571
|
+
const escaped = name.replace(/\$/g, "\\$");
|
|
4572
|
+
const pattern = new RegExp(`(?<![\\w$.])(?:const\\s+|let\\s+|var\\s+)?${escaped}((?:\\s*\\.[\\w$]+|\\s*\\[[^\\]]*\\])*)\\s*(?::[^=;{}\\n]{0,100})?=(?![=>])`, "g");
|
|
4573
|
+
let result = null;
|
|
4574
|
+
for (const m of this.code.slice(0, at).matchAll(pattern)) {
|
|
4575
|
+
if (this.owner(m.index) !== this.owner(at)) continue;
|
|
4576
|
+
const from = this.skipSpace(m.index + m[0].length);
|
|
4577
|
+
const end = this.endOf(from);
|
|
4578
|
+
if (end <= at) result = { at: m.index, from, end, expression: this.code.slice(from, end).trim(), member: m[1].length > 0 };
|
|
4579
|
+
}
|
|
4580
|
+
return result;
|
|
4581
|
+
}
|
|
4582
|
+
};
|
|
4583
|
+
function enforcedVerification(source, pattern, synchronous = /(?!)/) {
|
|
4584
|
+
const fragment = new LocalVerification({ path: "middleware.ts", content: source, lines: source.split("\n"), isExampleContext: false });
|
|
4585
|
+
const first = fragment.code.search(/\S/);
|
|
4586
|
+
let body = fragment.code[first] === "{" ? { start: first, end: fragment.pairs.get(first) ?? fragment.code.length - 1 } : fragment.bodies.find((b) => b.declaration >= first);
|
|
4587
|
+
if (body) {
|
|
4588
|
+
const returned = fragment.bodies.find((b) => b.declaration > body.start && b.end < body.end && /^\s*return\s+(?:createMiddleware\s*\(\s*)?(?:async\s*)?(?:\([^)]*\)\s*)?$/.test(fragment.code.slice(body.start + 1, b.declaration)));
|
|
4589
|
+
if (returned) body = returned;
|
|
4590
|
+
}
|
|
4591
|
+
const inner = body ? source.slice(body.start + 1, body.end) : source;
|
|
4592
|
+
const content = `async function __guard__(){
|
|
4593
|
+
${inner}
|
|
4594
|
+
__continuation__();
|
|
4595
|
+
}`;
|
|
4596
|
+
const context = new LocalVerification({ path: "middleware.ts", content, lines: content.split("\n"), isExampleContext: false }, true);
|
|
4597
|
+
const owner = context.bodies[0];
|
|
4598
|
+
let end = content.lastIndexOf("__continuation__");
|
|
4599
|
+
for (const m of context.code.matchAll(/(?<![\w$.])next\s*\(\s*\)/g)) {
|
|
4600
|
+
if (context.owner(m.index) === owner) end = Math.min(end, m.index);
|
|
4601
|
+
}
|
|
4602
|
+
const calls = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
4603
|
+
for (const m of context.code.matchAll(calls)) {
|
|
4604
|
+
if (context.owner(m.index) !== owner || !context.enforcedBefore(m.index, end)) continue;
|
|
4605
|
+
const close = context.pairs.get(m.index + m[0].length - 1);
|
|
4606
|
+
if (close === void 0 || /^[.[]|^\?\./.test(context.code.slice(context.skipSpace(close + 1)))) continue;
|
|
4607
|
+
const before = context.code.slice(Math.max(owner?.start ?? 0, m.index - 300), m.index).replace(/(?:[A-Za-z_$][\w$]*(?:\s*\([^()]*\))?\s*\??\.\s*)*$/, "").trimEnd();
|
|
4608
|
+
if (/(?:^|[^\w$.])(?:await|return)$/.test(before) || synchronous.test(m[0])) return true;
|
|
4609
|
+
}
|
|
4610
|
+
return false;
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4402
4613
|
// src/rules/apiauth.ts
|
|
4403
4614
|
var APP_ROUTER = /(?:^|\/)app\/api\/(?:.+\/)?route\.[mc]?[jt]sx?$/;
|
|
4404
4615
|
var PAGES_ROUTER = /(?:^|\/)pages\/api\/.+\.[mc]?[jt]sx?$/;
|
|
@@ -6018,13 +6229,6 @@ function passesOnlyAuthenticated(code) {
|
|
|
6018
6229
|
}
|
|
6019
6230
|
return false;
|
|
6020
6231
|
}
|
|
6021
|
-
function awaitedCall(code, pattern) {
|
|
6022
|
-
for (const m of code.matchAll(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`))) {
|
|
6023
|
-
const before = code.slice(Math.max(0, m.index - 300), m.index).replace(/(?:[A-Za-z_$][\w$]*(?:\s*\([^()]*\))?\s*\??\.\s*)*$/, "").trimEnd();
|
|
6024
|
-
if (/(?:^|[^\w$.])(?:await|return)$/.test(before)) return true;
|
|
6025
|
-
}
|
|
6026
|
-
return false;
|
|
6027
|
-
}
|
|
6028
6232
|
function maskUnusedFunctions(code) {
|
|
6029
6233
|
let out = code;
|
|
6030
6234
|
const definitions = /\b(?:(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::[^=;]{0,200})?=\s*(?:async\b\s*)?(?:function\b[^(]{0,80}\([^()]*\)|\([^()]*\)\s*(?::[^=;{]{0,200})?=>|[A-Za-z_$][\w$]*\s*=>)|(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)\s*\([^()]*\))\s*/g;
|
|
@@ -6052,7 +6256,7 @@ function maskUnusedFunctions(code) {
|
|
|
6052
6256
|
return out;
|
|
6053
6257
|
}
|
|
6054
6258
|
function jwtVerifyRejects(code) {
|
|
6055
|
-
if (!
|
|
6259
|
+
if (!enforcedVerification(code, /\bjwtVerify\s*\(/)) return false;
|
|
6056
6260
|
const handlers = [...code.matchAll(/\bcatch\s*(?:\([^()]*\))?\s*\{/g)].map((m) => {
|
|
6057
6261
|
const open = m.index + m[0].length - 1;
|
|
6058
6262
|
return code.slice(open, closingDelimiter(code, open, "{", "}") ?? open);
|
|
@@ -6079,7 +6283,8 @@ function catchDenies(code) {
|
|
|
6079
6283
|
const tryOpen = m.index + m[0].length - 1;
|
|
6080
6284
|
const tryClose = closingDelimiter(code, tryOpen, "{", "}");
|
|
6081
6285
|
if (tryClose === null) continue;
|
|
6082
|
-
|
|
6286
|
+
const calls = /\b\w*(?:auth|valid|verify|session|token|user|client|key|secret|permission|access)\w*\s*\(/i;
|
|
6287
|
+
if (!enforcedVerification(code, calls, /^(?:assertAuth(?:enticated)?|constructEvent)\b/i)) continue;
|
|
6083
6288
|
const handler = /^\s*catch\s*(?:\([^()]*\))?\s*\{/.exec(code.slice(tryClose + 1));
|
|
6084
6289
|
if (!handler) continue;
|
|
6085
6290
|
const open = tryClose + handler[0].length;
|
|
@@ -6115,8 +6320,8 @@ function rejectsMissingCredential(code) {
|
|
|
6115
6320
|
}
|
|
6116
6321
|
function middlewareRejects(source) {
|
|
6117
6322
|
const code = maskUnusedFunctions(source);
|
|
6118
|
-
if (hasConditionalAuthGuard(code) || rejectsMissingCredential(code) || passesOnlyAuthenticated(code) || rejectsSecretMismatch(code) || catchDenies(code) || AUTH_ENFORCING_CALL
|
|
6119
|
-
return
|
|
6323
|
+
if (hasConditionalAuthGuard(code) || rejectsMissingCredential(code) || passesOnlyAuthenticated(code) || rejectsSecretMismatch(code) || catchDenies(code) || enforcedVerification(code, AUTH_ENFORCING_CALL, /^(?:assertAuth(?:enticated)?|constructEvent)\b/i) || jwtVerifyRejects(code)) return true;
|
|
6324
|
+
return false;
|
|
6120
6325
|
}
|
|
6121
6326
|
function middlewareStatus(ref, files, depth = 0) {
|
|
6122
6327
|
const composition = depth < 3 ? authCompositionOf(ref) : null;
|
|
@@ -6142,7 +6347,7 @@ function middlewareStatus(ref, files, depth = 0) {
|
|
|
6142
6347
|
if (inner.status === "guard") return { status: "guard", name };
|
|
6143
6348
|
}
|
|
6144
6349
|
} else if (/^[A-Za-z_$][\w$]*\s*\.\s*[A-Za-z_$][\w$]*$/.test(ref.text)) {
|
|
6145
|
-
const plugin = FASTIFY_AUTH_DECORATORS.find(([pkg, decorator]) => decorator === name &&
|
|
6350
|
+
const plugin = FASTIFY_AUTH_DECORATORS.find(([pkg, decorator]) => decorator === name && registeredAuthPlugin(ref, pkg));
|
|
6146
6351
|
if (plugin) return { status: "guard", name };
|
|
6147
6352
|
}
|
|
6148
6353
|
return { status: AUTH_HELPER_NAME.test(name) ? "unconfirmed" : "none", name };
|
|
@@ -6989,12 +7194,13 @@ function fixedTablesOf(code, source, pairs) {
|
|
|
6989
7194
|
return result;
|
|
6990
7195
|
}
|
|
6991
7196
|
var InputFlow = class {
|
|
6992
|
-
constructor(code, source, span, pairs, roles, fixedTables) {
|
|
7197
|
+
constructor(code, source, span, pairs, roles, fixedTables, definition) {
|
|
6993
7198
|
this.code = code;
|
|
6994
7199
|
this.source = source;
|
|
6995
7200
|
this.span = span;
|
|
6996
7201
|
this.pairs = pairs;
|
|
6997
7202
|
this.fixedTables = fixedTables;
|
|
7203
|
+
this.definition = definition;
|
|
6998
7204
|
for (const [name, at] of roles.values) this.parameters.set(name, { level: "direct", origin: at, names: /* @__PURE__ */ new Set([name]), ownUrl: false });
|
|
6999
7205
|
this.sourcePattern = sourceRegex(roles);
|
|
7000
7206
|
this.contextRead = roles.contexts.size > 0 ? new RegExp(String.raw`(?<![\w$.])(?:${namePattern(roles.contexts.keys())})\s*\??\.\s*req(?:\s*\??\.\s*raw)?\s*\??\.\s*(${HONO_READ_CALLS})\s*\(`) : null;
|
|
@@ -7039,6 +7245,7 @@ var InputFlow = class {
|
|
|
7039
7245
|
span;
|
|
7040
7246
|
pairs;
|
|
7041
7247
|
fixedTables;
|
|
7248
|
+
definition;
|
|
7042
7249
|
parameters = /* @__PURE__ */ new Map();
|
|
7043
7250
|
limited = false;
|
|
7044
7251
|
assignmentList = /* @__PURE__ */ new Map();
|
|
@@ -7148,7 +7355,8 @@ var InputFlow = class {
|
|
|
7148
7355
|
if (!new RegExp(`^(?:(?:await|new)\\s+)*${escaped}\\s*\\(`).test(expression)) return false;
|
|
7149
7356
|
const cached = this.mirrors.get(callee);
|
|
7150
7357
|
if (cached !== void 0) return cached;
|
|
7151
|
-
|
|
7358
|
+
let code = this.code;
|
|
7359
|
+
let pairs = this.pairs;
|
|
7152
7360
|
let params = "";
|
|
7153
7361
|
let after = -1;
|
|
7154
7362
|
const declared = new RegExp(`(?:^|[^\\w$.])(?:async\\s+)?function\\s*\\*?\\s*${escaped}\\s*\\(`).exec(code);
|
|
@@ -7159,21 +7367,31 @@ var InputFlow = class {
|
|
|
7159
7367
|
after = match.index + match[0].length;
|
|
7160
7368
|
} else if (match) {
|
|
7161
7369
|
const open = match.index + match[0].length - 1;
|
|
7162
|
-
const close =
|
|
7370
|
+
const close = pairs.get(open);
|
|
7163
7371
|
if (close !== void 0) {
|
|
7164
7372
|
params = code.slice(open + 1, close);
|
|
7165
7373
|
after = close + 1;
|
|
7166
7374
|
}
|
|
7167
7375
|
}
|
|
7376
|
+
if (!match) {
|
|
7377
|
+
const target = this.definition?.(callee);
|
|
7378
|
+
if (target) {
|
|
7379
|
+
code = noiseMaskedOf(target.file);
|
|
7380
|
+
pairs = delimiterPairs(code);
|
|
7381
|
+
params = functionParams(target).join(",");
|
|
7382
|
+
after = code[target.start] === "{" ? target.start : code.indexOf("=>", target.start) + 2;
|
|
7383
|
+
if (after < target.start || after > target.end) after = -1;
|
|
7384
|
+
}
|
|
7385
|
+
}
|
|
7168
7386
|
let result = false;
|
|
7169
7387
|
if (after !== -1) {
|
|
7170
7388
|
const names = params.split(",").flatMap((part) => part.replace(/[:=][\s\S]*$/, "").match(/[A-Za-z_$][\w$]*/g) ?? []);
|
|
7171
7389
|
const head = /^\s*(?::[^={]{0,200})?(?:=>)?\s*/.exec(code.slice(after, after + 260));
|
|
7172
7390
|
const start = after + head[0].length;
|
|
7173
|
-
const end = (from, limit) => expressionEnd2(code, from, limit,
|
|
7391
|
+
const end = (from, limit) => expressionEnd2(code, from, limit, pairs, () => {
|
|
7174
7392
|
this.limited = true;
|
|
7175
7393
|
});
|
|
7176
|
-
const bodyEnd =
|
|
7394
|
+
const bodyEnd = pairs.get(start) ?? start;
|
|
7177
7395
|
const returns = code[start] === "{" ? [...code.slice(start, bodyEnd).matchAll(/\breturn\b/g)].map((m) => {
|
|
7178
7396
|
const from = start + m.index + m[0].length;
|
|
7179
7397
|
return code.slice(from, end(from, bodyEnd));
|
|
@@ -7454,12 +7672,13 @@ function lowerBound(values, value) {
|
|
|
7454
7672
|
return low;
|
|
7455
7673
|
}
|
|
7456
7674
|
var HandlerFile = class {
|
|
7457
|
-
constructor(code, source, pairs, lineStarts, candidates) {
|
|
7675
|
+
constructor(code, source, pairs, lineStarts, candidates, definition) {
|
|
7458
7676
|
this.code = code;
|
|
7459
7677
|
this.source = source;
|
|
7460
7678
|
this.pairs = pairs;
|
|
7461
7679
|
this.lineStarts = lineStarts;
|
|
7462
7680
|
this.candidates = candidates;
|
|
7681
|
+
this.definition = definition;
|
|
7463
7682
|
this.fixedTables = fixedTablesOf(code, source, pairs);
|
|
7464
7683
|
}
|
|
7465
7684
|
code;
|
|
@@ -7467,6 +7686,7 @@ var HandlerFile = class {
|
|
|
7467
7686
|
pairs;
|
|
7468
7687
|
lineStarts;
|
|
7469
7688
|
candidates;
|
|
7689
|
+
definition;
|
|
7470
7690
|
flows = /* @__PURE__ */ new Map();
|
|
7471
7691
|
fixedTables;
|
|
7472
7692
|
get limited() {
|
|
@@ -7483,7 +7703,7 @@ var HandlerFile = class {
|
|
|
7483
7703
|
if (first >= positions.length || positions[first] >= body.end) continue;
|
|
7484
7704
|
let flow = this.flows.get(candidate);
|
|
7485
7705
|
if (!flow) {
|
|
7486
|
-
flow = new InputFlow(this.code, this.source, { start: body.start, end: body.end }, this.pairs, candidate.roles, this.fixedTables);
|
|
7706
|
+
flow = new InputFlow(this.code, this.source, { start: body.start, end: body.end }, this.pairs, candidate.roles, this.fixedTables, this.definition);
|
|
7487
7707
|
this.flows.set(candidate, flow);
|
|
7488
7708
|
}
|
|
7489
7709
|
result.push({ route: candidate.route, body, flow });
|
|
@@ -7528,7 +7748,14 @@ function handlerFileOf(file, files) {
|
|
|
7528
7748
|
if (hasInput(roles)) candidates.push({ route: r, body, roles });
|
|
7529
7749
|
}
|
|
7530
7750
|
}
|
|
7531
|
-
if (candidates.length > 0) result = new HandlerFile(
|
|
7751
|
+
if (candidates.length > 0) result = new HandlerFile(
|
|
7752
|
+
code,
|
|
7753
|
+
source,
|
|
7754
|
+
pairs,
|
|
7755
|
+
lineStartsOf(file.content),
|
|
7756
|
+
candidates,
|
|
7757
|
+
(name) => calleeDefinition(file, name, null, files)
|
|
7758
|
+
);
|
|
7532
7759
|
}
|
|
7533
7760
|
}
|
|
7534
7761
|
handlerCache.set(file, result);
|
|
@@ -8050,140 +8277,6 @@ function check(ctx, kind) {
|
|
|
8050
8277
|
var ssrfRule = { id: "ssrf/request-url", severity: "P1", check: (ctx) => check(ctx, "ssrf") };
|
|
8051
8278
|
var redirectRule = { id: "redirect/open", severity: "P2", check: (ctx) => check(ctx, "redirect") };
|
|
8052
8279
|
|
|
8053
|
-
// src/rules/verification.ts
|
|
8054
|
-
var LocalVerification = class {
|
|
8055
|
-
code;
|
|
8056
|
-
source;
|
|
8057
|
-
pairs;
|
|
8058
|
-
bodies;
|
|
8059
|
-
constructor(file) {
|
|
8060
|
-
this.code = noiseMaskedOf(file);
|
|
8061
|
-
this.source = commentsMaskedOf(file);
|
|
8062
|
-
this.pairs = delimiterPairs(this.code);
|
|
8063
|
-
this.bodies = functionBodies(this.code, this.pairs);
|
|
8064
|
-
}
|
|
8065
|
-
owner(at) {
|
|
8066
|
-
return this.bodies.filter((b) => b.start < at && at < b.end).sort((a, b) => b.start - a.start)[0];
|
|
8067
|
-
}
|
|
8068
|
-
skipSpace(at) {
|
|
8069
|
-
while (/\s/.test(this.code[at] ?? "")) at++;
|
|
8070
|
-
return at;
|
|
8071
|
-
}
|
|
8072
|
-
endOf(at, limit = this.code.length) {
|
|
8073
|
-
if (this.code[at] === "{") return (this.pairs.get(at) ?? limit - 1) + 1;
|
|
8074
|
-
for (let i = at; i < limit; i++) {
|
|
8075
|
-
if (/[;\n}]/.test(this.code[i])) return i;
|
|
8076
|
-
const end = this.pairs.get(i);
|
|
8077
|
-
if (end !== void 0) i = end;
|
|
8078
|
-
}
|
|
8079
|
-
return limit;
|
|
8080
|
-
}
|
|
8081
|
-
/**
|
|
8082
|
-
* 块的顶层有无条件退出即算退出,前面可以有记录日志等语句;
|
|
8083
|
-
* 嵌套分支、函数中的 return 以及无花括号 if/else/循环控制的 return 不算。
|
|
8084
|
-
*/
|
|
8085
|
-
exits(from, to) {
|
|
8086
|
-
let at = this.skipSpace(from);
|
|
8087
|
-
if (this.code[at] !== "{") return /^(?:return|throw)\b|^(?:redirect|notFound)\s*\(/.test(this.code.slice(at, to));
|
|
8088
|
-
const end = Math.min(to, this.pairs.get(at) ?? to);
|
|
8089
|
-
at = this.skipSpace(at + 1);
|
|
8090
|
-
while (at < end) {
|
|
8091
|
-
if (/^(?:return|throw)\b|^(?:redirect|notFound)\s*\(/.test(this.code.slice(at, at + 32))) return true;
|
|
8092
|
-
const next = this.statementEnd(at, end);
|
|
8093
|
-
if (next <= at) return false;
|
|
8094
|
-
at = this.skipSpace(next);
|
|
8095
|
-
}
|
|
8096
|
-
return false;
|
|
8097
|
-
}
|
|
8098
|
-
/** 跳过完整语句;分支、循环和函数内的退出不提升为外层退出。 */
|
|
8099
|
-
statementEnd(at, limit, depth = 0) {
|
|
8100
|
-
if (depth >= 64) return limit;
|
|
8101
|
-
if (this.code[at] === "{") return (this.pairs.get(at) ?? limit - 1) + 1;
|
|
8102
|
-
const control = /^(if|for(?:\s+await)?|while|with|switch)\s*\(/.exec(this.code.slice(at, at + 80));
|
|
8103
|
-
if (control) {
|
|
8104
|
-
const close = this.pairs.get(at + control[0].length - 1);
|
|
8105
|
-
if (close === void 0) return limit;
|
|
8106
|
-
let end = this.statementEnd(this.skipSpace(close + 1), limit, depth + 1);
|
|
8107
|
-
const next = this.skipSpace(end);
|
|
8108
|
-
if (control[1] === "if" && /^else\b/.test(this.code.slice(next, next + 5))) {
|
|
8109
|
-
end = this.statementEnd(this.skipSpace(next + 4), limit, depth + 1);
|
|
8110
|
-
}
|
|
8111
|
-
return end;
|
|
8112
|
-
}
|
|
8113
|
-
if (/^(?:try|do)\b/.test(this.code.slice(at, at + 8))) return limit;
|
|
8114
|
-
for (let i = at; i < limit; i++) {
|
|
8115
|
-
if (this.code[i] === ";") return i + 1;
|
|
8116
|
-
const close = this.pairs.get(i);
|
|
8117
|
-
if (close !== void 0) {
|
|
8118
|
-
i = close;
|
|
8119
|
-
continue;
|
|
8120
|
-
}
|
|
8121
|
-
if (this.code[i] !== "\n") continue;
|
|
8122
|
-
const before = this.code.slice(at, i).trimEnd();
|
|
8123
|
-
const after = this.code.slice(i + 1, limit).trimStart();
|
|
8124
|
-
if (!before || /[=+\-*/%&|^!?:,.([{<>]$/.test(before)) continue;
|
|
8125
|
-
if (/^[.?+\-*/%&|^,:<>=([{`]/.test(after)) continue;
|
|
8126
|
-
if (/\b(?:const|let|var|function|async|await|new|yield)$/.test(before)) continue;
|
|
8127
|
-
return i + 1;
|
|
8128
|
-
}
|
|
8129
|
-
return limit;
|
|
8130
|
-
}
|
|
8131
|
-
/** 验证与使用须在同一函数,且不能依赖可选分支或被吞掉的异常。 */
|
|
8132
|
-
enforcedBefore(from, to) {
|
|
8133
|
-
const owner = this.owner(from);
|
|
8134
|
-
if (from >= to || owner !== this.owner(to)) return false;
|
|
8135
|
-
const start = owner ? owner.start + 1 : 0;
|
|
8136
|
-
for (let i = start; i < from; i++) {
|
|
8137
|
-
const conditional = /^(?:if|for|while|switch)\s*\(/.exec(this.code.slice(i, i + 32));
|
|
8138
|
-
if (conditional && !/[\w$]/.test(this.code[i - 1] ?? "")) {
|
|
8139
|
-
const close = this.pairs.get(i + conditional[0].length - 1);
|
|
8140
|
-
if (close !== void 0) {
|
|
8141
|
-
const body = this.skipSpace(close + 1);
|
|
8142
|
-
const end2 = this.endOf(body);
|
|
8143
|
-
if (from < end2 && to >= end2) return false;
|
|
8144
|
-
}
|
|
8145
|
-
}
|
|
8146
|
-
const end = this.pairs.get(i);
|
|
8147
|
-
if (end === void 0) continue;
|
|
8148
|
-
if (end < from) {
|
|
8149
|
-
i = end;
|
|
8150
|
-
continue;
|
|
8151
|
-
}
|
|
8152
|
-
if (end >= to) continue;
|
|
8153
|
-
if (this.code[i] !== "{" || !/\btry\s*$/.test(this.code.slice(Math.max(start, i - 16), i))) return false;
|
|
8154
|
-
let next = this.skipSpace(end + 1);
|
|
8155
|
-
if (/^catch\b/.test(this.code.slice(next))) {
|
|
8156
|
-
next = this.skipSpace(next + 5);
|
|
8157
|
-
if (this.code[next] === "(") next = this.skipSpace((this.pairs.get(next) ?? next) + 1);
|
|
8158
|
-
const caughtEnd = this.endOf(next);
|
|
8159
|
-
if (!this.exits(next, caughtEnd)) return false;
|
|
8160
|
-
next = this.skipSpace(caughtEnd);
|
|
8161
|
-
}
|
|
8162
|
-
if (/^finally\b/.test(this.code.slice(next))) return false;
|
|
8163
|
-
}
|
|
8164
|
-
const boundary = Math.max(
|
|
8165
|
-
start,
|
|
8166
|
-
this.code.lastIndexOf(";", from - 1) + 1,
|
|
8167
|
-
this.code.lastIndexOf("\n", from - 1) + 1,
|
|
8168
|
-
this.code.lastIndexOf("{", from - 1) + 1
|
|
8169
|
-
);
|
|
8170
|
-
return !/[?]|&&|\|\|/.test(this.code.slice(boundary, from));
|
|
8171
|
-
}
|
|
8172
|
-
/** 找出当前函数中最后一次赋值;未知或条件覆盖同样会使旧证明失效。 */
|
|
8173
|
-
assignment(name, at) {
|
|
8174
|
-
const escaped = name.replace(/\$/g, "\\$");
|
|
8175
|
-
const pattern = new RegExp(`(?<![\\w$.])(?:const\\s+|let\\s+|var\\s+)?${escaped}((?:\\s*\\.[\\w$]+|\\s*\\[[^\\]]*\\])*)\\s*(?::[^=;{}\\n]{0,100})?=(?![=>])`, "g");
|
|
8176
|
-
let result = null;
|
|
8177
|
-
for (const m of this.code.slice(0, at).matchAll(pattern)) {
|
|
8178
|
-
if (this.owner(m.index) !== this.owner(at)) continue;
|
|
8179
|
-
const from = this.skipSpace(m.index + m[0].length);
|
|
8180
|
-
const end = this.endOf(from);
|
|
8181
|
-
if (end <= at) result = { at: m.index, from, end, expression: this.code.slice(from, end).trim(), member: m[1].length > 0 };
|
|
8182
|
-
}
|
|
8183
|
-
return result;
|
|
8184
|
-
}
|
|
8185
|
-
};
|
|
8186
|
-
|
|
8187
8280
|
// src/rules/session.ts
|
|
8188
8281
|
var GET_SESSION = /\.auth\s*\.\s*getSession\s*\(/g;
|
|
8189
8282
|
function verifiedSession(file, context, sessionAt, names) {
|
|
@@ -8321,7 +8414,7 @@ var READS_BODY = /(?<![\w$])(?:req|request)\s*(?:\.\s*raw\s*)?\.\s*(?:json|text|
|
|
|
8321
8414
|
var SIGNATURE_HEADER = /['"]stripe-signature['"]/i;
|
|
8322
8415
|
function middlewareVerifies(code) {
|
|
8323
8416
|
const masked = maskUnusedFunctions(code);
|
|
8324
|
-
const verifies = /\bconstructEvent
|
|
8417
|
+
const verifies = enforcedVerification(masked, /\bconstructEvent(?:Async)?\s*\(/, /^constructEvent\s*\(/);
|
|
8325
8418
|
if (!verifies) return false;
|
|
8326
8419
|
for (const m of masked.matchAll(/\bcatch\s*(?:\([^()]*\))?\s*\{/g)) {
|
|
8327
8420
|
const open = m.index + m[0].length - 1;
|
|
@@ -8347,6 +8440,8 @@ function verifiedByMiddleware(route, files) {
|
|
|
8347
8440
|
const masked = maskUnusedFunctions(code);
|
|
8348
8441
|
const calls = [...masked.matchAll(/(?<![\w$.])([A-Za-z_$][\w$]*)\s*(?:\.\s*([A-Za-z_$][\w$]*)\s*)?\(/g)].slice(0, 16);
|
|
8349
8442
|
return calls.some((m) => {
|
|
8443
|
+
const call = new RegExp(`(?<![\\w$.])${m[1].replace(/\$/g, "\\$")}${m[2] ? `\\s*\\.\\s*${m[2].replace(/\$/g, "\\$")}` : ""}\\s*\\(`);
|
|
8444
|
+
if (!enforcedVerification(masked, call)) return false;
|
|
8350
8445
|
const definition = calleeDefinition(file, m[1], m[2] ?? null, files);
|
|
8351
8446
|
return definition !== null && middlewareVerifies(commentsMaskedOf(definition.file).slice(definition.start, definition.end + 1));
|
|
8352
8447
|
});
|
|
@@ -10250,7 +10345,7 @@ function openReport(path, onError, options = {}) {
|
|
|
10250
10345
|
}
|
|
10251
10346
|
|
|
10252
10347
|
// src/cli.ts
|
|
10253
|
-
var VERSION = true ? "0.7.
|
|
10348
|
+
var VERSION = true ? "0.7.1" : "0.0.0-dev";
|
|
10254
10349
|
function finish(code) {
|
|
10255
10350
|
process.exitCode = code;
|
|
10256
10351
|
}
|