@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/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 { Parser as Parser7 } from "web-tree-sitter";
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 { Parser as Parser8 } from "web-tree-sitter";
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 { Parser as Parser9 } from "web-tree-sitter";
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 { Parser as Parser10 } from "web-tree-sitter";
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 { Parser as Parser11 } from "web-tree-sitter";
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