@massu/core 1.5.8 → 1.6.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/dist/adapter.d.ts +76 -0
- package/dist/adapter.js +431 -0
- package/dist/cli.js +5 -739
- package/dist/detect/adapters/.bundle-shasums.json +12 -0
- package/dist/detect/adapters/aspnet.js +577 -0
- package/dist/detect/adapters/go-chi.js +561 -0
- package/dist/detect/adapters/parse-guard.d.ts +69 -0
- package/dist/detect/adapters/parse-guard.js +54 -0
- package/dist/detect/adapters/phoenix.js +556 -0
- package/dist/detect/adapters/query-helpers.d.ts +60 -0
- package/dist/detect/adapters/query-helpers.js +85 -0
- package/dist/detect/adapters/rails.js +567 -0
- package/dist/detect/adapters/spring.js +582 -0
- package/dist/detect/adapters/tree-sitter-loader.d.ts +102 -0
- package/dist/detect/adapters/tree-sitter-loader.js +317 -0
- package/dist/detect/adapters/types.d.ts +151 -0
- package/dist/detect/adapters/types.js +0 -0
- package/dist/hooks/session-start.js +552 -5211
- package/docs/AUTHORING-ADAPTERS.md +41 -0
- package/docs/SECURITY.md +39 -0
- package/package.json +17 -5
- package/src/adapter.ts +31 -0
- package/src/detect/adapters/aspnet.ts +4 -293
- package/src/detect/adapters/go-chi.ts +4 -261
- package/src/detect/adapters/phoenix.ts +4 -277
- package/src/detect/adapters/rails.ts +4 -279
- package/src/detect/adapters/spring.ts +4 -284
- package/src/security/registry-pubkey.generated.ts +1 -1
package/dist/cli.js
CHANGED
|
@@ -11160,776 +11160,42 @@ var init_python_flask = __esm({
|
|
|
11160
11160
|
});
|
|
11161
11161
|
|
|
11162
11162
|
// src/detect/adapters/go-chi.ts
|
|
11163
|
-
import {
|
|
11164
|
-
function extractPrefixBase4(prefix3) {
|
|
11165
|
-
if (!prefix3.startsWith("/")) return null;
|
|
11166
|
-
const stripped = prefix3.replace(/^\/+/, "");
|
|
11167
|
-
const firstSeg = stripped.split("/")[0];
|
|
11168
|
-
if (!firstSeg) return null;
|
|
11169
|
-
return "/" + firstSeg;
|
|
11170
|
-
}
|
|
11171
|
-
var ROUTE_METHOD_QUERY, MOUNT_PREFIX_QUERY, MIDDLEWARE_USE_QUERY, goChiAdapter;
|
|
11163
|
+
import { goChiAdapter } from "@massu/adapter-go-chi";
|
|
11172
11164
|
var init_go_chi = __esm({
|
|
11173
11165
|
"src/detect/adapters/go-chi.ts"() {
|
|
11174
11166
|
"use strict";
|
|
11175
|
-
init_query_helpers();
|
|
11176
|
-
init_tree_sitter_loader();
|
|
11177
|
-
init_parse_guard();
|
|
11178
|
-
ROUTE_METHOD_QUERY = `
|
|
11179
|
-
(call_expression
|
|
11180
|
-
function: (selector_expression
|
|
11181
|
-
field: (field_identifier) @method (#match? @method "^(Get|Post|Put|Delete|Patch|Head|Options|Connect|Trace)$"))
|
|
11182
|
-
arguments: (argument_list
|
|
11183
|
-
.
|
|
11184
|
-
(interpreted_string_literal) @route_path))
|
|
11185
|
-
`;
|
|
11186
|
-
MOUNT_PREFIX_QUERY = `
|
|
11187
|
-
(call_expression
|
|
11188
|
-
function: (selector_expression
|
|
11189
|
-
field: (field_identifier) @_field (#eq? @_field "Mount"))
|
|
11190
|
-
arguments: (argument_list
|
|
11191
|
-
.
|
|
11192
|
-
(interpreted_string_literal) @mount_path))
|
|
11193
|
-
`;
|
|
11194
|
-
MIDDLEWARE_USE_QUERY = `
|
|
11195
|
-
(call_expression
|
|
11196
|
-
function: (selector_expression
|
|
11197
|
-
field: (field_identifier) @_use (#eq? @_use "Use"))
|
|
11198
|
-
arguments: (argument_list
|
|
11199
|
-
.
|
|
11200
|
-
(selector_expression
|
|
11201
|
-
operand: (identifier) @_pkg (#eq? @_pkg "middleware")
|
|
11202
|
-
field: (field_identifier) @middleware_name)))
|
|
11203
|
-
`;
|
|
11204
|
-
goChiAdapter = {
|
|
11205
|
-
id: "go-chi",
|
|
11206
|
-
languages: ["go"],
|
|
11207
|
-
matches(signals) {
|
|
11208
|
-
if (!signals.goMod) return false;
|
|
11209
|
-
if (/github\.com\/go-chi\/chi/i.test(signals.goMod)) return true;
|
|
11210
|
-
return false;
|
|
11211
|
-
},
|
|
11212
|
-
async introspect(files, _rootDir) {
|
|
11213
|
-
if (files.length === 0) {
|
|
11214
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11215
|
-
}
|
|
11216
|
-
let language;
|
|
11217
|
-
try {
|
|
11218
|
-
language = await loadGrammar("go");
|
|
11219
|
-
} catch (e2) {
|
|
11220
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11221
|
-
}
|
|
11222
|
-
const parser = new Parser7();
|
|
11223
|
-
parser.setLanguage(language);
|
|
11224
|
-
const routeMethods = /* @__PURE__ */ new Map();
|
|
11225
|
-
const mountBases = /* @__PURE__ */ new Map();
|
|
11226
|
-
const middlewareNames = /* @__PURE__ */ new Map();
|
|
11227
|
-
try {
|
|
11228
|
-
for (const file of files) {
|
|
11229
|
-
const skip = isParsableSource(file.content, file.size);
|
|
11230
|
-
if (skip) {
|
|
11231
|
-
process.stderr.write(
|
|
11232
|
-
`[massu/ast] WARN: go-chi skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
11233
|
-
`
|
|
11234
|
-
);
|
|
11235
|
-
continue;
|
|
11236
|
-
}
|
|
11237
|
-
try {
|
|
11238
|
-
for (const hit of runQuery(parser, file.content, ROUTE_METHOD_QUERY, "chi-route-method", file.path)) {
|
|
11239
|
-
const method = hit.captures.method;
|
|
11240
|
-
if (method && !routeMethods.has(method)) {
|
|
11241
|
-
routeMethods.set(method, { line: hit.line, file: file.path });
|
|
11242
|
-
}
|
|
11243
|
-
}
|
|
11244
|
-
for (const hit of runQuery(parser, file.content, MOUNT_PREFIX_QUERY, "chi-mount-prefix", file.path)) {
|
|
11245
|
-
const raw = hit.captures.mount_path;
|
|
11246
|
-
if (!raw) continue;
|
|
11247
|
-
const literal = raw.replace(/^["`]/, "").replace(/["`]$/, "");
|
|
11248
|
-
const base = extractPrefixBase4(literal);
|
|
11249
|
-
if (base && !mountBases.has(base)) {
|
|
11250
|
-
mountBases.set(base, { line: hit.line, file: file.path });
|
|
11251
|
-
}
|
|
11252
|
-
}
|
|
11253
|
-
for (const hit of runQuery(parser, file.content, MIDDLEWARE_USE_QUERY, "chi-middleware-use", file.path)) {
|
|
11254
|
-
const name = hit.captures.middleware_name;
|
|
11255
|
-
if (name && !middlewareNames.has(name)) {
|
|
11256
|
-
middlewareNames.set(name, { line: hit.line, file: file.path });
|
|
11257
|
-
}
|
|
11258
|
-
}
|
|
11259
|
-
} catch (e2) {
|
|
11260
|
-
if (e2 instanceof InvalidQueryError) {
|
|
11261
|
-
throw e2;
|
|
11262
|
-
}
|
|
11263
|
-
continue;
|
|
11264
|
-
}
|
|
11265
|
-
}
|
|
11266
|
-
} finally {
|
|
11267
|
-
try {
|
|
11268
|
-
parser.delete();
|
|
11269
|
-
} catch {
|
|
11270
|
-
}
|
|
11271
|
-
}
|
|
11272
|
-
const conventions = {};
|
|
11273
|
-
const provenance = [];
|
|
11274
|
-
if (routeMethods.size === 1) {
|
|
11275
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11276
|
-
conventions.route_method = name;
|
|
11277
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "chi-route-method" });
|
|
11278
|
-
} else if (routeMethods.size >= 2) {
|
|
11279
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11280
|
-
conventions.route_method = name;
|
|
11281
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "chi-route-method" });
|
|
11282
|
-
}
|
|
11283
|
-
if (mountBases.size >= 1) {
|
|
11284
|
-
const [base, { line, file }] = mountBases.entries().next().value;
|
|
11285
|
-
conventions.mount_prefix_base = base;
|
|
11286
|
-
provenance.push({ field: "mount_prefix_base", sourceFile: file, line, query: "chi-mount-prefix" });
|
|
11287
|
-
}
|
|
11288
|
-
if (middlewareNames.size >= 1) {
|
|
11289
|
-
const [name, { line, file }] = middlewareNames.entries().next().value;
|
|
11290
|
-
conventions.middleware_name = name;
|
|
11291
|
-
provenance.push({ field: "middleware_name", sourceFile: file, line, query: "chi-middleware-use" });
|
|
11292
|
-
}
|
|
11293
|
-
let confidence;
|
|
11294
|
-
if (Object.keys(conventions).length === 0) {
|
|
11295
|
-
confidence = "none";
|
|
11296
|
-
} else if (routeMethods.size === 1) {
|
|
11297
|
-
confidence = "high";
|
|
11298
|
-
} else if (routeMethods.size >= 2) {
|
|
11299
|
-
confidence = "low";
|
|
11300
|
-
} else {
|
|
11301
|
-
confidence = "medium";
|
|
11302
|
-
}
|
|
11303
|
-
return { conventions, provenance, confidence };
|
|
11304
|
-
}
|
|
11305
|
-
};
|
|
11306
11167
|
}
|
|
11307
11168
|
});
|
|
11308
11169
|
|
|
11309
11170
|
// src/detect/adapters/rails.ts
|
|
11310
|
-
import {
|
|
11311
|
-
function extractRootController(target) {
|
|
11312
|
-
const idx = target.indexOf("#");
|
|
11313
|
-
if (idx <= 0) return null;
|
|
11314
|
-
const controller = target.slice(0, idx).trim();
|
|
11315
|
-
return controller || null;
|
|
11316
|
-
}
|
|
11317
|
-
var ROUTE_METHOD_QUERY2, NAMESPACE_QUERY, ROOT_ROUTE_QUERY, railsAdapter;
|
|
11171
|
+
import { railsAdapter } from "@massu/adapter-rails";
|
|
11318
11172
|
var init_rails = __esm({
|
|
11319
11173
|
"src/detect/adapters/rails.ts"() {
|
|
11320
11174
|
"use strict";
|
|
11321
|
-
init_query_helpers();
|
|
11322
|
-
init_tree_sitter_loader();
|
|
11323
|
-
init_parse_guard();
|
|
11324
|
-
ROUTE_METHOD_QUERY2 = `
|
|
11325
|
-
(call
|
|
11326
|
-
method: (identifier) @method (#match? @method "^(get|post|put|patch|delete|options|head)$")
|
|
11327
|
-
arguments: (argument_list
|
|
11328
|
-
.
|
|
11329
|
-
(string) @route_path))
|
|
11330
|
-
`;
|
|
11331
|
-
NAMESPACE_QUERY = `
|
|
11332
|
-
(call
|
|
11333
|
-
method: (identifier) @_method (#eq? @_method "namespace")
|
|
11334
|
-
arguments: (argument_list
|
|
11335
|
-
.
|
|
11336
|
-
[
|
|
11337
|
-
(simple_symbol) @namespace_symbol
|
|
11338
|
-
(string) @namespace_string
|
|
11339
|
-
]))
|
|
11340
|
-
`;
|
|
11341
|
-
ROOT_ROUTE_QUERY = `
|
|
11342
|
-
(call
|
|
11343
|
-
method: (identifier) @_method (#eq? @_method "root")
|
|
11344
|
-
arguments: (argument_list
|
|
11345
|
-
.
|
|
11346
|
-
(string) @root_target))
|
|
11347
|
-
|
|
11348
|
-
(call
|
|
11349
|
-
method: (identifier) @_method (#eq? @_method "root")
|
|
11350
|
-
arguments: (argument_list
|
|
11351
|
-
(pair
|
|
11352
|
-
key: (hash_key_symbol) @_key (#eq? @_key "to")
|
|
11353
|
-
value: (string) @root_target)))
|
|
11354
|
-
`;
|
|
11355
|
-
railsAdapter = {
|
|
11356
|
-
id: "rails",
|
|
11357
|
-
languages: ["ruby"],
|
|
11358
|
-
matches(signals) {
|
|
11359
|
-
if (!signals.gemfile) return false;
|
|
11360
|
-
return /^\s*gem\s+['"]rails['"]/im.test(signals.gemfile);
|
|
11361
|
-
},
|
|
11362
|
-
async introspect(files, _rootDir) {
|
|
11363
|
-
if (files.length === 0) {
|
|
11364
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11365
|
-
}
|
|
11366
|
-
let language;
|
|
11367
|
-
try {
|
|
11368
|
-
language = await loadGrammar("ruby");
|
|
11369
|
-
} catch (e2) {
|
|
11370
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11371
|
-
}
|
|
11372
|
-
const parser = new Parser8();
|
|
11373
|
-
parser.setLanguage(language);
|
|
11374
|
-
const routeMethods = /* @__PURE__ */ new Map();
|
|
11375
|
-
const namespaces = /* @__PURE__ */ new Map();
|
|
11376
|
-
const rootControllers = /* @__PURE__ */ new Map();
|
|
11377
|
-
try {
|
|
11378
|
-
for (const file of files) {
|
|
11379
|
-
const skip = isParsableSource(file.content, file.size);
|
|
11380
|
-
if (skip) {
|
|
11381
|
-
process.stderr.write(
|
|
11382
|
-
`[massu/ast] WARN: rails skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
11383
|
-
`
|
|
11384
|
-
);
|
|
11385
|
-
continue;
|
|
11386
|
-
}
|
|
11387
|
-
try {
|
|
11388
|
-
for (const hit of runQuery(parser, file.content, ROUTE_METHOD_QUERY2, "rails-route-method", file.path)) {
|
|
11389
|
-
const method = hit.captures.method;
|
|
11390
|
-
if (method && !routeMethods.has(method)) {
|
|
11391
|
-
routeMethods.set(method, { line: hit.line, file: file.path });
|
|
11392
|
-
}
|
|
11393
|
-
}
|
|
11394
|
-
for (const hit of runQuery(parser, file.content, NAMESPACE_QUERY, "rails-namespace", file.path)) {
|
|
11395
|
-
const symbolRaw = hit.captures.namespace_symbol;
|
|
11396
|
-
const stringRaw = hit.captures.namespace_string;
|
|
11397
|
-
const name = symbolRaw ? symbolRaw.replace(/^:/, "") : stringRaw ? stringRaw.replace(/^['"]/, "").replace(/['"]$/, "") : null;
|
|
11398
|
-
if (!name) continue;
|
|
11399
|
-
const path = "/" + name;
|
|
11400
|
-
if (!namespaces.has(path)) {
|
|
11401
|
-
namespaces.set(path, { line: hit.line, file: file.path });
|
|
11402
|
-
}
|
|
11403
|
-
}
|
|
11404
|
-
for (const hit of runQuery(parser, file.content, ROOT_ROUTE_QUERY, "rails-root", file.path)) {
|
|
11405
|
-
const raw = hit.captures.root_target;
|
|
11406
|
-
if (!raw) continue;
|
|
11407
|
-
const literal = raw.replace(/^['"]/, "").replace(/['"]$/, "");
|
|
11408
|
-
const controller = extractRootController(literal);
|
|
11409
|
-
if (controller && !rootControllers.has(controller)) {
|
|
11410
|
-
rootControllers.set(controller, { line: hit.line, file: file.path });
|
|
11411
|
-
}
|
|
11412
|
-
}
|
|
11413
|
-
} catch (e2) {
|
|
11414
|
-
if (e2 instanceof InvalidQueryError) {
|
|
11415
|
-
throw e2;
|
|
11416
|
-
}
|
|
11417
|
-
continue;
|
|
11418
|
-
}
|
|
11419
|
-
}
|
|
11420
|
-
} finally {
|
|
11421
|
-
try {
|
|
11422
|
-
parser.delete();
|
|
11423
|
-
} catch {
|
|
11424
|
-
}
|
|
11425
|
-
}
|
|
11426
|
-
const conventions = {};
|
|
11427
|
-
const provenance = [];
|
|
11428
|
-
if (routeMethods.size === 1) {
|
|
11429
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11430
|
-
conventions.route_method = name;
|
|
11431
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "rails-route-method" });
|
|
11432
|
-
} else if (routeMethods.size >= 2) {
|
|
11433
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11434
|
-
conventions.route_method = name;
|
|
11435
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "rails-route-method" });
|
|
11436
|
-
}
|
|
11437
|
-
if (namespaces.size >= 1) {
|
|
11438
|
-
const [path, { line, file }] = namespaces.entries().next().value;
|
|
11439
|
-
conventions.api_namespace = path;
|
|
11440
|
-
provenance.push({ field: "api_namespace", sourceFile: file, line, query: "rails-namespace" });
|
|
11441
|
-
}
|
|
11442
|
-
if (rootControllers.size >= 1) {
|
|
11443
|
-
const [name, { line, file }] = rootControllers.entries().next().value;
|
|
11444
|
-
conventions.root_controller = name;
|
|
11445
|
-
provenance.push({ field: "root_controller", sourceFile: file, line, query: "rails-root" });
|
|
11446
|
-
}
|
|
11447
|
-
let confidence;
|
|
11448
|
-
if (Object.keys(conventions).length === 0) {
|
|
11449
|
-
confidence = "none";
|
|
11450
|
-
} else if (routeMethods.size === 1) {
|
|
11451
|
-
confidence = "high";
|
|
11452
|
-
} else if (routeMethods.size >= 2) {
|
|
11453
|
-
confidence = "low";
|
|
11454
|
-
} else {
|
|
11455
|
-
confidence = "medium";
|
|
11456
|
-
}
|
|
11457
|
-
return { conventions, provenance, confidence };
|
|
11458
|
-
}
|
|
11459
|
-
};
|
|
11460
11175
|
}
|
|
11461
11176
|
});
|
|
11462
11177
|
|
|
11463
11178
|
// src/detect/adapters/phoenix.ts
|
|
11464
|
-
import {
|
|
11465
|
-
function extractPrefixBase5(prefix3) {
|
|
11466
|
-
if (!prefix3.startsWith("/")) return null;
|
|
11467
|
-
const stripped = prefix3.replace(/^\/+/, "");
|
|
11468
|
-
const firstSeg = stripped.split("/")[0];
|
|
11469
|
-
if (!firstSeg) return null;
|
|
11470
|
-
return "/" + firstSeg;
|
|
11471
|
-
}
|
|
11472
|
-
var ROUTE_METHOD_QUERY3, SCOPE_PATH_QUERY, ROUTER_MODULE_QUERY, phoenixAdapter;
|
|
11179
|
+
import { phoenixAdapter } from "@massu/adapter-phoenix";
|
|
11473
11180
|
var init_phoenix = __esm({
|
|
11474
11181
|
"src/detect/adapters/phoenix.ts"() {
|
|
11475
11182
|
"use strict";
|
|
11476
|
-
init_query_helpers();
|
|
11477
|
-
init_tree_sitter_loader();
|
|
11478
|
-
init_parse_guard();
|
|
11479
|
-
ROUTE_METHOD_QUERY3 = `
|
|
11480
|
-
(call
|
|
11481
|
-
(identifier) @method (#match? @method "^(get|post|put|patch|delete|options|head)$")
|
|
11482
|
-
(arguments
|
|
11483
|
-
.
|
|
11484
|
-
(string) @route_path))
|
|
11485
|
-
`;
|
|
11486
|
-
SCOPE_PATH_QUERY = `
|
|
11487
|
-
(call
|
|
11488
|
-
(identifier) @_method (#eq? @_method "scope")
|
|
11489
|
-
(arguments
|
|
11490
|
-
.
|
|
11491
|
-
(string) @scope_path)
|
|
11492
|
-
(do_block))
|
|
11493
|
-
`;
|
|
11494
|
-
ROUTER_MODULE_QUERY = `
|
|
11495
|
-
(call
|
|
11496
|
-
(identifier) @_method (#eq? @_method "defmodule")
|
|
11497
|
-
(arguments
|
|
11498
|
-
.
|
|
11499
|
-
(alias) @module_name (#match? @module_name "Router$"))
|
|
11500
|
-
(do_block))
|
|
11501
|
-
`;
|
|
11502
|
-
phoenixAdapter = {
|
|
11503
|
-
id: "phoenix",
|
|
11504
|
-
languages: ["elixir"],
|
|
11505
|
-
matches(signals) {
|
|
11506
|
-
if (!signals.mixExs) return false;
|
|
11507
|
-
return /\{\s*:phoenix\b(?!_)/.test(signals.mixExs);
|
|
11508
|
-
},
|
|
11509
|
-
async introspect(files, _rootDir) {
|
|
11510
|
-
if (files.length === 0) {
|
|
11511
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11512
|
-
}
|
|
11513
|
-
let language;
|
|
11514
|
-
try {
|
|
11515
|
-
language = await loadGrammar("elixir");
|
|
11516
|
-
} catch (e2) {
|
|
11517
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11518
|
-
}
|
|
11519
|
-
const parser = new Parser9();
|
|
11520
|
-
parser.setLanguage(language);
|
|
11521
|
-
const routeMethods = /* @__PURE__ */ new Map();
|
|
11522
|
-
const scopePaths = /* @__PURE__ */ new Map();
|
|
11523
|
-
const routerModules = /* @__PURE__ */ new Map();
|
|
11524
|
-
try {
|
|
11525
|
-
for (const file of files) {
|
|
11526
|
-
const skip = isParsableSource(file.content, file.size);
|
|
11527
|
-
if (skip) {
|
|
11528
|
-
process.stderr.write(
|
|
11529
|
-
`[massu/ast] WARN: phoenix skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
11530
|
-
`
|
|
11531
|
-
);
|
|
11532
|
-
continue;
|
|
11533
|
-
}
|
|
11534
|
-
try {
|
|
11535
|
-
for (const hit of runQuery(parser, file.content, ROUTE_METHOD_QUERY3, "phoenix-route-method", file.path)) {
|
|
11536
|
-
const method = hit.captures.method;
|
|
11537
|
-
if (method && !routeMethods.has(method)) {
|
|
11538
|
-
routeMethods.set(method, { line: hit.line, file: file.path });
|
|
11539
|
-
}
|
|
11540
|
-
}
|
|
11541
|
-
for (const hit of runQuery(parser, file.content, SCOPE_PATH_QUERY, "phoenix-scope-path", file.path)) {
|
|
11542
|
-
const raw = hit.captures.scope_path;
|
|
11543
|
-
if (!raw) continue;
|
|
11544
|
-
const literal = raw.replace(/^["']/, "").replace(/["']$/, "");
|
|
11545
|
-
const base = extractPrefixBase5(literal);
|
|
11546
|
-
if (base && !scopePaths.has(base)) {
|
|
11547
|
-
scopePaths.set(base, { line: hit.line, file: file.path });
|
|
11548
|
-
}
|
|
11549
|
-
}
|
|
11550
|
-
for (const hit of runQuery(parser, file.content, ROUTER_MODULE_QUERY, "phoenix-router-module", file.path)) {
|
|
11551
|
-
const name = hit.captures.module_name;
|
|
11552
|
-
if (name && !routerModules.has(name)) {
|
|
11553
|
-
routerModules.set(name, { line: hit.line, file: file.path });
|
|
11554
|
-
}
|
|
11555
|
-
}
|
|
11556
|
-
} catch (e2) {
|
|
11557
|
-
if (e2 instanceof InvalidQueryError) {
|
|
11558
|
-
throw e2;
|
|
11559
|
-
}
|
|
11560
|
-
continue;
|
|
11561
|
-
}
|
|
11562
|
-
}
|
|
11563
|
-
} finally {
|
|
11564
|
-
try {
|
|
11565
|
-
parser.delete();
|
|
11566
|
-
} catch {
|
|
11567
|
-
}
|
|
11568
|
-
}
|
|
11569
|
-
const conventions = {};
|
|
11570
|
-
const provenance = [];
|
|
11571
|
-
if (routeMethods.size === 1) {
|
|
11572
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11573
|
-
conventions.route_method = name;
|
|
11574
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "phoenix-route-method" });
|
|
11575
|
-
} else if (routeMethods.size >= 2) {
|
|
11576
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11577
|
-
conventions.route_method = name;
|
|
11578
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "phoenix-route-method" });
|
|
11579
|
-
}
|
|
11580
|
-
if (scopePaths.size >= 1) {
|
|
11581
|
-
const [base, { line, file }] = scopePaths.entries().next().value;
|
|
11582
|
-
conventions.scope_prefix_base = base;
|
|
11583
|
-
provenance.push({ field: "scope_prefix_base", sourceFile: file, line, query: "phoenix-scope-path" });
|
|
11584
|
-
}
|
|
11585
|
-
if (routerModules.size >= 1) {
|
|
11586
|
-
const [name, { line, file }] = routerModules.entries().next().value;
|
|
11587
|
-
conventions.router_module = name;
|
|
11588
|
-
provenance.push({ field: "router_module", sourceFile: file, line, query: "phoenix-router-module" });
|
|
11589
|
-
}
|
|
11590
|
-
let confidence;
|
|
11591
|
-
if (Object.keys(conventions).length === 0) {
|
|
11592
|
-
confidence = "none";
|
|
11593
|
-
} else if (routeMethods.size === 1) {
|
|
11594
|
-
confidence = "high";
|
|
11595
|
-
} else if (routeMethods.size >= 2) {
|
|
11596
|
-
confidence = "low";
|
|
11597
|
-
} else {
|
|
11598
|
-
confidence = "medium";
|
|
11599
|
-
}
|
|
11600
|
-
return { conventions, provenance, confidence };
|
|
11601
|
-
}
|
|
11602
|
-
};
|
|
11603
11183
|
}
|
|
11604
11184
|
});
|
|
11605
11185
|
|
|
11606
11186
|
// src/detect/adapters/aspnet.ts
|
|
11607
|
-
import {
|
|
11608
|
-
function extractPrefixBase6(prefix3) {
|
|
11609
|
-
const stripped = prefix3.replace(/^\/+/, "");
|
|
11610
|
-
const firstSeg = stripped.split("/")[0];
|
|
11611
|
-
if (!firstSeg) return null;
|
|
11612
|
-
return "/" + firstSeg;
|
|
11613
|
-
}
|
|
11614
|
-
var MAP_VERB_QUERY, HTTP_ATTR_QUERY, ROUTE_ATTR_QUERY, CONTROLLER_CLASS_QUERY, aspnetAdapter;
|
|
11187
|
+
import { aspnetAdapter } from "@massu/adapter-aspnet";
|
|
11615
11188
|
var init_aspnet = __esm({
|
|
11616
11189
|
"src/detect/adapters/aspnet.ts"() {
|
|
11617
11190
|
"use strict";
|
|
11618
|
-
init_query_helpers();
|
|
11619
|
-
init_tree_sitter_loader();
|
|
11620
|
-
init_parse_guard();
|
|
11621
|
-
MAP_VERB_QUERY = `
|
|
11622
|
-
(invocation_expression
|
|
11623
|
-
function: (member_access_expression
|
|
11624
|
-
name: (identifier) @method (#match? @method "^Map(Get|Post|Put|Patch|Delete|Head|Options)$"))
|
|
11625
|
-
arguments: (argument_list
|
|
11626
|
-
.
|
|
11627
|
-
(argument (string_literal) @route_path)))
|
|
11628
|
-
`;
|
|
11629
|
-
HTTP_ATTR_QUERY = `
|
|
11630
|
-
(attribute
|
|
11631
|
-
name: (identifier) @attr_name (#match? @attr_name "^Http(Get|Post|Put|Patch|Delete|Head|Options)$"))
|
|
11632
|
-
`;
|
|
11633
|
-
ROUTE_ATTR_QUERY = `
|
|
11634
|
-
(attribute
|
|
11635
|
-
name: (identifier) @_attr_name (#eq? @_attr_name "Route")
|
|
11636
|
-
(attribute_argument_list
|
|
11637
|
-
(attribute_argument (string_literal) @route_template)))
|
|
11638
|
-
`;
|
|
11639
|
-
CONTROLLER_CLASS_QUERY = `
|
|
11640
|
-
(class_declaration
|
|
11641
|
-
name: (identifier) @class_name (#match? @class_name "Controller$"))
|
|
11642
|
-
`;
|
|
11643
|
-
aspnetAdapter = {
|
|
11644
|
-
id: "aspnet",
|
|
11645
|
-
languages: ["csharp"],
|
|
11646
|
-
matches(signals) {
|
|
11647
|
-
if (!signals.csproj) return false;
|
|
11648
|
-
if (/Sdk\s*=\s*["']Microsoft\.NET\.Sdk\.Web["']/i.test(signals.csproj)) return true;
|
|
11649
|
-
if (/Microsoft\.AspNetCore\.App/i.test(signals.csproj)) return true;
|
|
11650
|
-
return false;
|
|
11651
|
-
},
|
|
11652
|
-
async introspect(files, _rootDir) {
|
|
11653
|
-
if (files.length === 0) {
|
|
11654
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11655
|
-
}
|
|
11656
|
-
let language;
|
|
11657
|
-
try {
|
|
11658
|
-
language = await loadGrammar("csharp");
|
|
11659
|
-
} catch (e2) {
|
|
11660
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11661
|
-
}
|
|
11662
|
-
const parser = new Parser10();
|
|
11663
|
-
parser.setLanguage(language);
|
|
11664
|
-
const routeMethods = /* @__PURE__ */ new Map();
|
|
11665
|
-
const prefixBases = /* @__PURE__ */ new Map();
|
|
11666
|
-
const controllerClasses = /* @__PURE__ */ new Map();
|
|
11667
|
-
try {
|
|
11668
|
-
for (const file of files) {
|
|
11669
|
-
const skip = isParsableSource(file.content, file.size);
|
|
11670
|
-
if (skip) {
|
|
11671
|
-
process.stderr.write(
|
|
11672
|
-
`[massu/ast] WARN: aspnet skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
11673
|
-
`
|
|
11674
|
-
);
|
|
11675
|
-
continue;
|
|
11676
|
-
}
|
|
11677
|
-
try {
|
|
11678
|
-
for (const hit of runQuery(parser, file.content, MAP_VERB_QUERY, "aspnet-map-verb", file.path)) {
|
|
11679
|
-
const methodRaw = hit.captures.method;
|
|
11680
|
-
if (!methodRaw) continue;
|
|
11681
|
-
const verb = methodRaw.replace(/^Map/, "");
|
|
11682
|
-
if (!routeMethods.has(verb)) {
|
|
11683
|
-
routeMethods.set(verb, { line: hit.line, file: file.path });
|
|
11684
|
-
}
|
|
11685
|
-
const pathRaw = hit.captures.route_path;
|
|
11686
|
-
if (pathRaw) {
|
|
11687
|
-
const literal = pathRaw.replace(/^["']/, "").replace(/["']$/, "");
|
|
11688
|
-
const base = extractPrefixBase6(literal);
|
|
11689
|
-
if (base && !prefixBases.has(base)) {
|
|
11690
|
-
prefixBases.set(base, { line: hit.line, file: file.path });
|
|
11691
|
-
}
|
|
11692
|
-
}
|
|
11693
|
-
}
|
|
11694
|
-
for (const hit of runQuery(parser, file.content, HTTP_ATTR_QUERY, "aspnet-http-attr", file.path)) {
|
|
11695
|
-
const attrRaw = hit.captures.attr_name;
|
|
11696
|
-
if (!attrRaw) continue;
|
|
11697
|
-
const verb = attrRaw.replace(/^Http/, "");
|
|
11698
|
-
if (!routeMethods.has(verb)) {
|
|
11699
|
-
routeMethods.set(verb, { line: hit.line, file: file.path });
|
|
11700
|
-
}
|
|
11701
|
-
}
|
|
11702
|
-
for (const hit of runQuery(parser, file.content, ROUTE_ATTR_QUERY, "aspnet-route-attr", file.path)) {
|
|
11703
|
-
const tplRaw = hit.captures.route_template;
|
|
11704
|
-
if (!tplRaw) continue;
|
|
11705
|
-
const literal = tplRaw.replace(/^["']/, "").replace(/["']$/, "");
|
|
11706
|
-
const base = extractPrefixBase6(literal);
|
|
11707
|
-
if (base && !prefixBases.has(base)) {
|
|
11708
|
-
prefixBases.set(base, { line: hit.line, file: file.path });
|
|
11709
|
-
}
|
|
11710
|
-
}
|
|
11711
|
-
for (const hit of runQuery(parser, file.content, CONTROLLER_CLASS_QUERY, "aspnet-controller-class", file.path)) {
|
|
11712
|
-
const name = hit.captures.class_name;
|
|
11713
|
-
if (name && !controllerClasses.has(name)) {
|
|
11714
|
-
controllerClasses.set(name, { line: hit.line, file: file.path });
|
|
11715
|
-
}
|
|
11716
|
-
}
|
|
11717
|
-
} catch (e2) {
|
|
11718
|
-
if (e2 instanceof InvalidQueryError) {
|
|
11719
|
-
throw e2;
|
|
11720
|
-
}
|
|
11721
|
-
continue;
|
|
11722
|
-
}
|
|
11723
|
-
}
|
|
11724
|
-
} finally {
|
|
11725
|
-
try {
|
|
11726
|
-
parser.delete();
|
|
11727
|
-
} catch {
|
|
11728
|
-
}
|
|
11729
|
-
}
|
|
11730
|
-
const conventions = {};
|
|
11731
|
-
const provenance = [];
|
|
11732
|
-
if (routeMethods.size === 1) {
|
|
11733
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11734
|
-
conventions.route_method = name;
|
|
11735
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "aspnet-map-verb" });
|
|
11736
|
-
} else if (routeMethods.size >= 2) {
|
|
11737
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11738
|
-
conventions.route_method = name;
|
|
11739
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "aspnet-map-verb" });
|
|
11740
|
-
}
|
|
11741
|
-
if (prefixBases.size >= 1) {
|
|
11742
|
-
const [base, { line, file }] = prefixBases.entries().next().value;
|
|
11743
|
-
conventions.route_prefix_base = base;
|
|
11744
|
-
provenance.push({ field: "route_prefix_base", sourceFile: file, line, query: "aspnet-route-prefix" });
|
|
11745
|
-
}
|
|
11746
|
-
if (controllerClasses.size >= 1) {
|
|
11747
|
-
const [name, { line, file }] = controllerClasses.entries().next().value;
|
|
11748
|
-
conventions.controller_class = name;
|
|
11749
|
-
provenance.push({ field: "controller_class", sourceFile: file, line, query: "aspnet-controller-class" });
|
|
11750
|
-
}
|
|
11751
|
-
let confidence;
|
|
11752
|
-
if (Object.keys(conventions).length === 0) {
|
|
11753
|
-
confidence = "none";
|
|
11754
|
-
} else if (routeMethods.size === 1) {
|
|
11755
|
-
confidence = "high";
|
|
11756
|
-
} else if (routeMethods.size >= 2) {
|
|
11757
|
-
confidence = "low";
|
|
11758
|
-
} else {
|
|
11759
|
-
confidence = "medium";
|
|
11760
|
-
}
|
|
11761
|
-
return { conventions, provenance, confidence };
|
|
11762
|
-
}
|
|
11763
|
-
};
|
|
11764
11191
|
}
|
|
11765
11192
|
});
|
|
11766
11193
|
|
|
11767
11194
|
// src/detect/adapters/spring.ts
|
|
11768
|
-
import {
|
|
11769
|
-
function extractPrefixBase7(prefix3) {
|
|
11770
|
-
const stripped = prefix3.replace(/^\/+/, "");
|
|
11771
|
-
const firstSeg = stripped.split("/")[0];
|
|
11772
|
-
if (!firstSeg) return null;
|
|
11773
|
-
return "/" + firstSeg;
|
|
11774
|
-
}
|
|
11775
|
-
var HTTP_MAPPING_QUERY, HTTP_MAPPING_NO_ARGS_QUERY, REQUEST_MAPPING_QUERY, CONTROLLER_CLASS_QUERY2, springAdapter;
|
|
11195
|
+
import { springAdapter } from "@massu/adapter-spring";
|
|
11776
11196
|
var init_spring = __esm({
|
|
11777
11197
|
"src/detect/adapters/spring.ts"() {
|
|
11778
11198
|
"use strict";
|
|
11779
|
-
init_query_helpers();
|
|
11780
|
-
init_tree_sitter_loader();
|
|
11781
|
-
init_parse_guard();
|
|
11782
|
-
HTTP_MAPPING_QUERY = `
|
|
11783
|
-
(annotation
|
|
11784
|
-
name: (identifier) @method (#match? @method "^(Get|Post|Put|Patch|Delete|Head|Options)Mapping$")
|
|
11785
|
-
arguments: (annotation_argument_list
|
|
11786
|
-
(string_literal) @route_path))
|
|
11787
|
-
`;
|
|
11788
|
-
HTTP_MAPPING_NO_ARGS_QUERY = `
|
|
11789
|
-
(marker_annotation
|
|
11790
|
-
name: (identifier) @method (#match? @method "^(Get|Post|Put|Patch|Delete|Head|Options)Mapping$"))
|
|
11791
|
-
`;
|
|
11792
|
-
REQUEST_MAPPING_QUERY = `
|
|
11793
|
-
(annotation
|
|
11794
|
-
name: (identifier) @_name (#eq? @_name "RequestMapping")
|
|
11795
|
-
arguments: (annotation_argument_list
|
|
11796
|
-
(string_literal) @route_template))
|
|
11797
|
-
`;
|
|
11798
|
-
CONTROLLER_CLASS_QUERY2 = `
|
|
11799
|
-
(class_declaration
|
|
11800
|
-
(modifiers
|
|
11801
|
-
(marker_annotation
|
|
11802
|
-
name: (identifier) @_anno (#match? @_anno "^(RestController|Controller)$")))
|
|
11803
|
-
name: (identifier) @class_name)
|
|
11804
|
-
|
|
11805
|
-
(class_declaration
|
|
11806
|
-
(modifiers
|
|
11807
|
-
(annotation
|
|
11808
|
-
name: (identifier) @_anno (#match? @_anno "^(RestController|Controller)$")))
|
|
11809
|
-
name: (identifier) @class_name)
|
|
11810
|
-
`;
|
|
11811
|
-
springAdapter = {
|
|
11812
|
-
id: "spring",
|
|
11813
|
-
languages: ["java"],
|
|
11814
|
-
matches(signals) {
|
|
11815
|
-
if (signals.pomXml && /\bspring-boot-starter[\w-]*\b/.test(signals.pomXml)) {
|
|
11816
|
-
return true;
|
|
11817
|
-
}
|
|
11818
|
-
if (signals.gradleBuild && /\bspring-boot-starter[\w-]*\b/.test(signals.gradleBuild)) {
|
|
11819
|
-
return true;
|
|
11820
|
-
}
|
|
11821
|
-
if (signals.pomXml && /\borg\.springframework\b/.test(signals.pomXml)) {
|
|
11822
|
-
return true;
|
|
11823
|
-
}
|
|
11824
|
-
if (signals.gradleBuild && /\borg\.springframework\b/.test(signals.gradleBuild)) {
|
|
11825
|
-
return true;
|
|
11826
|
-
}
|
|
11827
|
-
return false;
|
|
11828
|
-
},
|
|
11829
|
-
async introspect(files, _rootDir) {
|
|
11830
|
-
if (files.length === 0) {
|
|
11831
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11832
|
-
}
|
|
11833
|
-
let language;
|
|
11834
|
-
try {
|
|
11835
|
-
language = await loadGrammar("java");
|
|
11836
|
-
} catch (e2) {
|
|
11837
|
-
return { conventions: {}, provenance: [], confidence: "none" };
|
|
11838
|
-
}
|
|
11839
|
-
const parser = new Parser11();
|
|
11840
|
-
parser.setLanguage(language);
|
|
11841
|
-
const routeMethods = /* @__PURE__ */ new Map();
|
|
11842
|
-
const prefixBases = /* @__PURE__ */ new Map();
|
|
11843
|
-
const controllerClasses = /* @__PURE__ */ new Map();
|
|
11844
|
-
try {
|
|
11845
|
-
for (const file of files) {
|
|
11846
|
-
const skip = isParsableSource(file.content, file.size);
|
|
11847
|
-
if (skip) {
|
|
11848
|
-
process.stderr.write(
|
|
11849
|
-
`[massu/ast] WARN: spring skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
11850
|
-
`
|
|
11851
|
-
);
|
|
11852
|
-
continue;
|
|
11853
|
-
}
|
|
11854
|
-
try {
|
|
11855
|
-
for (const hit of runQuery(parser, file.content, HTTP_MAPPING_QUERY, "spring-http-mapping", file.path)) {
|
|
11856
|
-
const methodRaw = hit.captures.method;
|
|
11857
|
-
if (!methodRaw) continue;
|
|
11858
|
-
const verb = methodRaw.replace(/Mapping$/, "");
|
|
11859
|
-
if (!routeMethods.has(verb)) {
|
|
11860
|
-
routeMethods.set(verb, { line: hit.line, file: file.path });
|
|
11861
|
-
}
|
|
11862
|
-
}
|
|
11863
|
-
for (const hit of runQuery(parser, file.content, HTTP_MAPPING_NO_ARGS_QUERY, "spring-http-mapping-marker", file.path)) {
|
|
11864
|
-
const methodRaw = hit.captures.method;
|
|
11865
|
-
if (!methodRaw) continue;
|
|
11866
|
-
const verb = methodRaw.replace(/Mapping$/, "");
|
|
11867
|
-
if (!routeMethods.has(verb)) {
|
|
11868
|
-
routeMethods.set(verb, { line: hit.line, file: file.path });
|
|
11869
|
-
}
|
|
11870
|
-
}
|
|
11871
|
-
for (const hit of runQuery(parser, file.content, REQUEST_MAPPING_QUERY, "spring-request-mapping", file.path)) {
|
|
11872
|
-
const tplRaw = hit.captures.route_template;
|
|
11873
|
-
if (!tplRaw) continue;
|
|
11874
|
-
const literal = tplRaw.replace(/^["']/, "").replace(/["']$/, "");
|
|
11875
|
-
const base = extractPrefixBase7(literal);
|
|
11876
|
-
if (base && !prefixBases.has(base)) {
|
|
11877
|
-
prefixBases.set(base, { line: hit.line, file: file.path });
|
|
11878
|
-
}
|
|
11879
|
-
}
|
|
11880
|
-
for (const hit of runQuery(parser, file.content, CONTROLLER_CLASS_QUERY2, "spring-controller-class", file.path)) {
|
|
11881
|
-
const name = hit.captures.class_name;
|
|
11882
|
-
if (name && !controllerClasses.has(name)) {
|
|
11883
|
-
controllerClasses.set(name, { line: hit.line, file: file.path });
|
|
11884
|
-
}
|
|
11885
|
-
}
|
|
11886
|
-
} catch (e2) {
|
|
11887
|
-
if (e2 instanceof InvalidQueryError) {
|
|
11888
|
-
throw e2;
|
|
11889
|
-
}
|
|
11890
|
-
continue;
|
|
11891
|
-
}
|
|
11892
|
-
}
|
|
11893
|
-
} finally {
|
|
11894
|
-
try {
|
|
11895
|
-
parser.delete();
|
|
11896
|
-
} catch {
|
|
11897
|
-
}
|
|
11898
|
-
}
|
|
11899
|
-
const conventions = {};
|
|
11900
|
-
const provenance = [];
|
|
11901
|
-
if (routeMethods.size === 1) {
|
|
11902
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11903
|
-
conventions.route_method = name;
|
|
11904
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "spring-http-mapping" });
|
|
11905
|
-
} else if (routeMethods.size >= 2) {
|
|
11906
|
-
const [name, { line, file }] = routeMethods.entries().next().value;
|
|
11907
|
-
conventions.route_method = name;
|
|
11908
|
-
provenance.push({ field: "route_method", sourceFile: file, line, query: "spring-http-mapping" });
|
|
11909
|
-
}
|
|
11910
|
-
if (prefixBases.size >= 1) {
|
|
11911
|
-
const [base, { line, file }] = prefixBases.entries().next().value;
|
|
11912
|
-
conventions.route_prefix_base = base;
|
|
11913
|
-
provenance.push({ field: "route_prefix_base", sourceFile: file, line, query: "spring-request-mapping" });
|
|
11914
|
-
}
|
|
11915
|
-
if (controllerClasses.size >= 1) {
|
|
11916
|
-
const [name, { line, file }] = controllerClasses.entries().next().value;
|
|
11917
|
-
conventions.controller_class = name;
|
|
11918
|
-
provenance.push({ field: "controller_class", sourceFile: file, line, query: "spring-controller-class" });
|
|
11919
|
-
}
|
|
11920
|
-
let confidence;
|
|
11921
|
-
if (Object.keys(conventions).length === 0) {
|
|
11922
|
-
confidence = "none";
|
|
11923
|
-
} else if (routeMethods.size === 1) {
|
|
11924
|
-
confidence = "high";
|
|
11925
|
-
} else if (routeMethods.size >= 2) {
|
|
11926
|
-
confidence = "low";
|
|
11927
|
-
} else {
|
|
11928
|
-
confidence = "medium";
|
|
11929
|
-
}
|
|
11930
|
-
return { conventions, provenance, confidence };
|
|
11931
|
-
}
|
|
11932
|
-
};
|
|
11933
11199
|
}
|
|
11934
11200
|
});
|
|
11935
11201
|
|