@codacy/analysis-runner 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +191 -35
- package/dist/index.js +1502 -860
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -38,7 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
clearAdapters: () => clearAdapters,
|
|
39
39
|
computePatternSummary: () => computePatternSummary,
|
|
40
40
|
configureApiToken: () => configureApiToken,
|
|
41
|
-
configureProxy: () =>
|
|
41
|
+
configureProxy: () => import_tooling8.configureProxy,
|
|
42
42
|
createCodacyConfig: () => createCodacyConfig,
|
|
43
43
|
createLogger: () => createLogger,
|
|
44
44
|
createNullLogger: () => createNullLogger,
|
|
@@ -76,9 +76,11 @@ __export(index_exports, {
|
|
|
76
76
|
resolveApiToken: () => resolveApiToken,
|
|
77
77
|
resolveBaselinePath: () => resolveBaselinePath,
|
|
78
78
|
resolveConfigPath: () => resolveConfigPath,
|
|
79
|
+
resolveDiscoveryExcludes: () => resolveDiscoveryExcludes,
|
|
79
80
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
80
81
|
resolveHeadCommit: () => resolveHeadCommit,
|
|
81
|
-
resolveProxyConfig: () =>
|
|
82
|
+
resolveProxyConfig: () => import_tooling8.resolveProxyConfig,
|
|
83
|
+
resolveRemoteAuth: () => resolveRemoteAuth,
|
|
82
84
|
resolveRepositoryRoot: () => resolveRepositoryRoot,
|
|
83
85
|
resolveResultsApiBaseUrl: () => resolveResultsApiBaseUrl,
|
|
84
86
|
rotateLogFiles: () => rotateLogFiles,
|
|
@@ -515,7 +517,7 @@ function isDependencyManifest(relativePath) {
|
|
|
515
517
|
if (DEPENDENCY_MANIFEST_FILES.has(base)) return true;
|
|
516
518
|
return DEPENDENCY_MANIFEST_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
517
519
|
}
|
|
518
|
-
function listRepoFiles(repositoryRoot) {
|
|
520
|
+
function listRepoFiles(repositoryRoot, exclude) {
|
|
519
521
|
try {
|
|
520
522
|
const tracked = (0, import_child_process2.execSync)("git ls-files", {
|
|
521
523
|
cwd: repositoryRoot,
|
|
@@ -534,7 +536,8 @@ function listRepoFiles(repositoryRoot) {
|
|
|
534
536
|
for (const f of untracked.split("\n")) {
|
|
535
537
|
if (f) files.add(f);
|
|
536
538
|
}
|
|
537
|
-
|
|
539
|
+
const all = Array.from(files).sort();
|
|
540
|
+
return exclude && exclude.length > 0 ? applyExclusions(all, [...exclude], []) : all;
|
|
538
541
|
} catch {
|
|
539
542
|
return [];
|
|
540
543
|
}
|
|
@@ -1652,6 +1655,11 @@ function formatSarif(result) {
|
|
|
1652
1655
|
ruleIndex: ruleIndex.get(issue.patternId) ?? 0,
|
|
1653
1656
|
level: mapSeverityToSarif(issue.severity),
|
|
1654
1657
|
message: { text: issue.message },
|
|
1658
|
+
// SARIF has no first-class field for a tool's own finding id, so it rides in
|
|
1659
|
+
// the result property bag. `upload` reads it back from here — without it the
|
|
1660
|
+
// SARIF path would silently drop `sourceId` and produce different Codacy
|
|
1661
|
+
// result UUIDs than the JSON path for the same analysis.
|
|
1662
|
+
...issue.sourceId ? { properties: { sourceId: issue.sourceId } } : {},
|
|
1655
1663
|
locations: [
|
|
1656
1664
|
{
|
|
1657
1665
|
physicalLocation: {
|
|
@@ -1716,7 +1724,8 @@ function formatContainer(result) {
|
|
|
1716
1724
|
patternId: issue.patternId,
|
|
1717
1725
|
line: issue.line,
|
|
1718
1726
|
message: issue.message,
|
|
1719
|
-
suggestion: issue.suggestion
|
|
1727
|
+
suggestion: issue.suggestion,
|
|
1728
|
+
sourceId: issue.sourceId
|
|
1720
1729
|
})
|
|
1721
1730
|
).join("\n");
|
|
1722
1731
|
}
|
|
@@ -2323,642 +2332,16 @@ var import_promises4 = __toESM(require("fs/promises"));
|
|
|
2323
2332
|
var import_os3 = __toESM(require("os"));
|
|
2324
2333
|
var import_path8 = __toESM(require("path"));
|
|
2325
2334
|
var import_child_process4 = require("child_process");
|
|
2326
|
-
var
|
|
2335
|
+
var import_tooling7 = require("@codacy/tooling");
|
|
2327
2336
|
|
|
2328
|
-
// src/discover.ts
|
|
2337
|
+
// src/discover/index.ts
|
|
2329
2338
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
2330
2339
|
var import_path6 = __toESM(require("path"));
|
|
2331
2340
|
|
|
2332
|
-
// src/discover
|
|
2333
|
-
var
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
["react-dom", { displayName: "React DOM", kind: "framework" }],
|
|
2337
|
-
["react-native", { displayName: "React Native", kind: "framework" }],
|
|
2338
|
-
["vue", { displayName: "Vue.js", kind: "framework" }],
|
|
2339
|
-
["nuxt", { displayName: "Nuxt", kind: "framework" }],
|
|
2340
|
-
["@angular/core", { displayName: "Angular", kind: "framework" }],
|
|
2341
|
-
["svelte", { displayName: "Svelte", kind: "framework" }],
|
|
2342
|
-
["@sveltejs/kit", { displayName: "SvelteKit", kind: "framework" }],
|
|
2343
|
-
["next", { displayName: "Next.js", kind: "framework" }],
|
|
2344
|
-
["ember-source", { displayName: "Ember.js", kind: "framework" }],
|
|
2345
|
-
["lit", { displayName: "Lit", kind: "framework" }],
|
|
2346
|
-
["solid-js", { displayName: "Solid", kind: "framework" }],
|
|
2347
|
-
["astro", { displayName: "Astro", kind: "framework" }],
|
|
2348
|
-
["@builder.io/qwik", { displayName: "Qwik", kind: "framework" }],
|
|
2349
|
-
["@builder.io/qwik-city", { displayName: "Qwik", kind: "framework" }],
|
|
2350
|
-
["@remix-run/react", { displayName: "Remix", kind: "framework" }],
|
|
2351
|
-
["gatsby", { displayName: "Gatsby", kind: "framework" }],
|
|
2352
|
-
["preact", { displayName: "Preact", kind: "framework" }],
|
|
2353
|
-
["express", { displayName: "Express", kind: "framework" }],
|
|
2354
|
-
["@nestjs/core", { displayName: "NestJS", kind: "framework" }],
|
|
2355
|
-
["fastify", { displayName: "Fastify", kind: "framework" }],
|
|
2356
|
-
["koa", { displayName: "Koa", kind: "framework" }],
|
|
2357
|
-
["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
|
|
2358
|
-
["hono", { displayName: "Hono", kind: "framework" }],
|
|
2359
|
-
["electron", { displayName: "Electron", kind: "framework" }],
|
|
2360
|
-
// AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
|
|
2361
|
-
// and the v1 `@aws-cdk/*` construct packages (prefix rule below).
|
|
2362
|
-
["aws-cdk-lib", { displayName: "AWS CDK", kind: "framework" }],
|
|
2363
|
-
["aws-cdk", { displayName: "AWS CDK", kind: "framework" }],
|
|
2364
|
-
// --- JavaScript / TypeScript — Libraries ---
|
|
2365
|
-
["lodash", { displayName: "Lodash", kind: "library" }],
|
|
2366
|
-
["underscore", { displayName: "Underscore.js", kind: "library" }],
|
|
2367
|
-
["ramda", { displayName: "Ramda", kind: "library" }],
|
|
2368
|
-
["jest", { displayName: "Jest", kind: "library" }],
|
|
2369
|
-
["vitest", { displayName: "Vitest", kind: "library" }],
|
|
2370
|
-
["@playwright/test", { displayName: "Playwright", kind: "library" }],
|
|
2371
|
-
["playwright", { displayName: "Playwright", kind: "library" }],
|
|
2372
|
-
["cypress", { displayName: "Cypress", kind: "library" }],
|
|
2373
|
-
["mocha", { displayName: "Mocha", kind: "library" }],
|
|
2374
|
-
["ava", { displayName: "AVA", kind: "library" }],
|
|
2375
|
-
["jasmine", { displayName: "Jasmine", kind: "library" }],
|
|
2376
|
-
["jasmine-core", { displayName: "Jasmine", kind: "library" }],
|
|
2377
|
-
["@testing-library/react", { displayName: "Testing Library", kind: "library" }],
|
|
2378
|
-
["@testing-library/vue", { displayName: "Testing Library", kind: "library" }],
|
|
2379
|
-
["@testing-library/dom", { displayName: "Testing Library", kind: "library" }],
|
|
2380
|
-
["tailwindcss", { displayName: "Tailwind CSS", kind: "library" }],
|
|
2381
|
-
["@storybook/react", { displayName: "Storybook", kind: "library" }],
|
|
2382
|
-
["@storybook/vue3", { displayName: "Storybook", kind: "library" }],
|
|
2383
|
-
["@storybook/svelte", { displayName: "Storybook", kind: "library" }],
|
|
2384
|
-
["redux", { displayName: "Redux", kind: "library" }],
|
|
2385
|
-
["@reduxjs/toolkit", { displayName: "Redux Toolkit", kind: "library" }],
|
|
2386
|
-
["mobx", { displayName: "MobX", kind: "library" }],
|
|
2387
|
-
["zustand", { displayName: "Zustand", kind: "library" }],
|
|
2388
|
-
["axios", { displayName: "Axios", kind: "library" }],
|
|
2389
|
-
["date-fns", { displayName: "date-fns", kind: "library" }],
|
|
2390
|
-
["dayjs", { displayName: "Day.js", kind: "library" }],
|
|
2391
|
-
["moment", { displayName: "Moment.js", kind: "library" }],
|
|
2392
|
-
["rxjs", { displayName: "RxJS", kind: "library" }],
|
|
2393
|
-
["zod", { displayName: "Zod", kind: "library" }],
|
|
2394
|
-
["joi", { displayName: "Joi", kind: "library" }],
|
|
2395
|
-
["yup", { displayName: "Yup", kind: "library" }],
|
|
2396
|
-
["prisma", { displayName: "Prisma", kind: "library" }],
|
|
2397
|
-
["@prisma/client", { displayName: "Prisma", kind: "library" }],
|
|
2398
|
-
["typeorm", { displayName: "TypeORM", kind: "library" }],
|
|
2399
|
-
["sequelize", { displayName: "Sequelize", kind: "library" }],
|
|
2400
|
-
["drizzle-orm", { displayName: "Drizzle ORM", kind: "library" }],
|
|
2401
|
-
["mongoose", { displayName: "Mongoose", kind: "library" }],
|
|
2402
|
-
["graphql", { displayName: "GraphQL", kind: "library" }],
|
|
2403
|
-
["@apollo/client", { displayName: "Apollo Client", kind: "library" }],
|
|
2404
|
-
["socket.io", { displayName: "Socket.IO", kind: "library" }],
|
|
2405
|
-
["three", { displayName: "Three.js", kind: "library" }],
|
|
2406
|
-
["d3", { displayName: "D3.js", kind: "library" }],
|
|
2407
|
-
["chart.js", { displayName: "Chart.js", kind: "library" }],
|
|
2408
|
-
["sass", { displayName: "Sass", kind: "library" }],
|
|
2409
|
-
["node-sass", { displayName: "Sass", kind: "library" }],
|
|
2410
|
-
["less", { displayName: "Less", kind: "library" }],
|
|
2411
|
-
["styled-components", { displayName: "styled-components", kind: "library" }],
|
|
2412
|
-
["@emotion/react", { displayName: "Emotion", kind: "library" }],
|
|
2413
|
-
["pino", { displayName: "Pino", kind: "library" }],
|
|
2414
|
-
["winston", { displayName: "Winston", kind: "library" }],
|
|
2415
|
-
["i18next", { displayName: "i18next", kind: "library" }],
|
|
2416
|
-
// react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
|
|
2417
|
-
// lints both, so we report the umbrella name "formatjs" (matches the mapping key).
|
|
2418
|
-
["react-intl", { displayName: "formatjs", kind: "library" }],
|
|
2419
|
-
["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
|
|
2420
|
-
["next-intl", { displayName: "next-intl", kind: "library" }],
|
|
2421
|
-
// Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
|
|
2422
|
-
["flow-bin", { displayName: "Flow", kind: "library" }],
|
|
2423
|
-
// Legacy JS libraries with dedicated ESLint plugins.
|
|
2424
|
-
["backbone", { displayName: "Backbone.js", kind: "library" }],
|
|
2425
|
-
["chai", { displayName: "Chai", kind: "library" }],
|
|
2426
|
-
["codeceptjs", { displayName: "CodeceptJS", kind: "library" }],
|
|
2427
|
-
["webdriverio", { displayName: "WebdriverIO", kind: "library" }],
|
|
2428
|
-
["react-relay", { displayName: "Relay", kind: "library" }],
|
|
2429
|
-
["relay-runtime", { displayName: "Relay", kind: "library" }],
|
|
2430
|
-
// --- JavaScript / TypeScript — Tools ---
|
|
2431
|
-
["typescript", { displayName: "TypeScript", kind: "tool" }],
|
|
2432
|
-
["eslint", { displayName: "ESLint", kind: "tool" }],
|
|
2433
|
-
["prettier", { displayName: "Prettier", kind: "tool" }],
|
|
2434
|
-
["webpack", { displayName: "Webpack", kind: "tool" }],
|
|
2435
|
-
["vite", { displayName: "Vite", kind: "tool" }],
|
|
2436
|
-
["esbuild", { displayName: "esbuild", kind: "tool" }],
|
|
2437
|
-
["rollup", { displayName: "Rollup", kind: "tool" }],
|
|
2438
|
-
["turbo", { displayName: "Turborepo", kind: "tool" }],
|
|
2439
|
-
["tsup", { displayName: "tsup", kind: "tool" }],
|
|
2440
|
-
["@biomejs/biome", { displayName: "Biome", kind: "tool" }],
|
|
2441
|
-
["stylelint", { displayName: "Stylelint", kind: "tool" }],
|
|
2442
|
-
// --- JavaScript / TypeScript — Platforms ---
|
|
2443
|
-
["@lwc/engine-dom", { displayName: "Lightning Web Components", kind: "platform" }],
|
|
2444
|
-
["@salesforce/core", { displayName: "Salesforce", kind: "platform" }],
|
|
2445
|
-
["@shopify/polaris", { displayName: "Shopify Polaris", kind: "platform" }],
|
|
2446
|
-
// --- Python — Frameworks ---
|
|
2447
|
-
["django", { displayName: "Django", kind: "framework" }],
|
|
2448
|
-
["flask", { displayName: "Flask", kind: "framework" }],
|
|
2449
|
-
["fastapi", { displayName: "FastAPI", kind: "framework" }],
|
|
2450
|
-
["tornado", { displayName: "Tornado", kind: "framework" }],
|
|
2451
|
-
["pyramid", { displayName: "Pyramid", kind: "framework" }],
|
|
2452
|
-
["starlette", { displayName: "Starlette", kind: "framework" }],
|
|
2453
|
-
["sanic", { displayName: "Sanic", kind: "framework" }],
|
|
2454
|
-
["bottle", { displayName: "Bottle", kind: "framework" }],
|
|
2455
|
-
["streamlit", { displayName: "Streamlit", kind: "framework" }],
|
|
2456
|
-
["gradio", { displayName: "Gradio", kind: "framework" }],
|
|
2457
|
-
["apache-airflow", { displayName: "Airflow", kind: "framework" }],
|
|
2458
|
-
// --- Python — Libraries ---
|
|
2459
|
-
["flask-babel", { displayName: "Flask-Babel", kind: "library" }],
|
|
2460
|
-
["pytest", { displayName: "pytest", kind: "library" }],
|
|
2461
|
-
["numpy", { displayName: "NumPy", kind: "library" }],
|
|
2462
|
-
["pandas", { displayName: "pandas", kind: "library" }],
|
|
2463
|
-
["requests", { displayName: "Requests", kind: "library" }],
|
|
2464
|
-
["sqlalchemy", { displayName: "SQLAlchemy", kind: "library" }],
|
|
2465
|
-
["celery", { displayName: "Celery", kind: "library" }],
|
|
2466
|
-
["pydantic", { displayName: "Pydantic", kind: "library" }],
|
|
2467
|
-
["boto3", { displayName: "Boto3 (AWS SDK)", kind: "library" }],
|
|
2468
|
-
["scipy", { displayName: "SciPy", kind: "library" }],
|
|
2469
|
-
["scikit-learn", { displayName: "scikit-learn", kind: "library" }],
|
|
2470
|
-
["tensorflow", { displayName: "TensorFlow", kind: "library" }],
|
|
2471
|
-
["torch", { displayName: "PyTorch", kind: "library" }],
|
|
2472
|
-
["httpx", { displayName: "HTTPX", kind: "library" }],
|
|
2473
|
-
["aiohttp", { displayName: "aiohttp", kind: "library" }],
|
|
2474
|
-
["pillow", { displayName: "Pillow", kind: "library" }],
|
|
2475
|
-
["beautifulsoup4", { displayName: "Beautiful Soup", kind: "library" }],
|
|
2476
|
-
["scrapy", { displayName: "Scrapy", kind: "library" }],
|
|
2477
|
-
["alembic", { displayName: "Alembic", kind: "library" }],
|
|
2478
|
-
["marshmallow", { displayName: "Marshmallow", kind: "library" }],
|
|
2479
|
-
["click", { displayName: "Click", kind: "library" }],
|
|
2480
|
-
["typer", { displayName: "Typer", kind: "library" }],
|
|
2481
|
-
["rich", { displayName: "Rich", kind: "library" }],
|
|
2482
|
-
["polars", { displayName: "Polars", kind: "library" }],
|
|
2483
|
-
["dask", { displayName: "Dask", kind: "library" }],
|
|
2484
|
-
["matplotlib", { displayName: "Matplotlib", kind: "library" }],
|
|
2485
|
-
["seaborn", { displayName: "Seaborn", kind: "library" }],
|
|
2486
|
-
["plotly", { displayName: "Plotly", kind: "library" }],
|
|
2487
|
-
// --- Python — Tools ---
|
|
2488
|
-
["black", { displayName: "Black", kind: "tool" }],
|
|
2489
|
-
["ruff", { displayName: "Ruff", kind: "tool" }],
|
|
2490
|
-
["mypy", { displayName: "mypy", kind: "tool" }],
|
|
2491
|
-
["pylint", { displayName: "Pylint", kind: "tool" }],
|
|
2492
|
-
["flake8", { displayName: "Flake8", kind: "tool" }],
|
|
2493
|
-
["bandit", { displayName: "Bandit", kind: "tool" }],
|
|
2494
|
-
["isort", { displayName: "isort", kind: "tool" }],
|
|
2495
|
-
// --- Ruby — Frameworks ---
|
|
2496
|
-
["rails", { displayName: "Ruby on Rails", kind: "framework" }],
|
|
2497
|
-
["sinatra", { displayName: "Sinatra", kind: "framework" }],
|
|
2498
|
-
["hanami", { displayName: "Hanami", kind: "framework" }],
|
|
2499
|
-
["grape", { displayName: "Grape", kind: "framework" }],
|
|
2500
|
-
["padrino", { displayName: "Padrino", kind: "framework" }],
|
|
2501
|
-
// --- Ruby — Libraries ---
|
|
2502
|
-
["rspec", { displayName: "RSpec", kind: "library" }],
|
|
2503
|
-
["rspec-core", { displayName: "RSpec", kind: "library" }],
|
|
2504
|
-
["rspec-rails", { displayName: "RSpec Rails", kind: "library" }],
|
|
2505
|
-
["capybara", { displayName: "Capybara", kind: "library" }],
|
|
2506
|
-
["sidekiq", { displayName: "Sidekiq", kind: "library" }],
|
|
2507
|
-
["devise", { displayName: "Devise", kind: "library" }],
|
|
2508
|
-
["pundit", { displayName: "Pundit", kind: "library" }],
|
|
2509
|
-
["activeadmin", { displayName: "ActiveAdmin", kind: "library" }],
|
|
2510
|
-
["puma", { displayName: "Puma", kind: "library" }],
|
|
2511
|
-
["nokogiri", { displayName: "Nokogiri", kind: "library" }],
|
|
2512
|
-
["faraday", { displayName: "Faraday", kind: "library" }],
|
|
2513
|
-
["httparty", { displayName: "HTTParty", kind: "library" }],
|
|
2514
|
-
["minitest", { displayName: "Minitest", kind: "library" }],
|
|
2515
|
-
["factory_bot", { displayName: "FactoryBot", kind: "library" }],
|
|
2516
|
-
["dry-validation", { displayName: "dry-validation", kind: "library" }],
|
|
2517
|
-
// --- Ruby — Tools ---
|
|
2518
|
-
["rubocop", { displayName: "RuboCop", kind: "tool" }],
|
|
2519
|
-
["bundler", { displayName: "Bundler", kind: "tool" }],
|
|
2520
|
-
["solargraph", { displayName: "Solargraph", kind: "tool" }],
|
|
2521
|
-
// --- Java / Kotlin — Frameworks ---
|
|
2522
|
-
["spring-boot-starter", { displayName: "Spring Boot", kind: "framework" }],
|
|
2523
|
-
["spring-boot-starter-web", { displayName: "Spring Boot", kind: "framework" }],
|
|
2524
|
-
["spring-core", { displayName: "Spring Framework", kind: "framework" }],
|
|
2525
|
-
["quarkus-core", { displayName: "Quarkus", kind: "framework" }],
|
|
2526
|
-
["micronaut-core", { displayName: "Micronaut", kind: "framework" }],
|
|
2527
|
-
["ktor-server-core", { displayName: "Ktor", kind: "framework" }],
|
|
2528
|
-
["android", { displayName: "Android SDK", kind: "framework" }],
|
|
2529
|
-
// --- Java / Kotlin — Libraries ---
|
|
2530
|
-
["junit-jupiter", { displayName: "JUnit 5", kind: "library" }],
|
|
2531
|
-
["junit", { displayName: "JUnit", kind: "library" }],
|
|
2532
|
-
["junit-jupiter-api", { displayName: "JUnit 5", kind: "library" }],
|
|
2533
|
-
["mockito-core", { displayName: "Mockito", kind: "library" }],
|
|
2534
|
-
["lombok", { displayName: "Lombok", kind: "library" }],
|
|
2535
|
-
["jackson-databind", { displayName: "Jackson", kind: "library" }],
|
|
2536
|
-
["jackson-core", { displayName: "Jackson", kind: "library" }],
|
|
2537
|
-
["hibernate-core", { displayName: "Hibernate", kind: "library" }],
|
|
2538
|
-
["guava", { displayName: "Guava", kind: "library" }],
|
|
2539
|
-
["commons-lang3", { displayName: "Apache Commons Lang", kind: "library" }],
|
|
2540
|
-
["commons-io", { displayName: "Apache Commons IO", kind: "library" }],
|
|
2541
|
-
["slf4j-api", { displayName: "SLF4J", kind: "library" }],
|
|
2542
|
-
["log4j-core", { displayName: "Log4j", kind: "library" }],
|
|
2543
|
-
["logback-classic", { displayName: "Logback", kind: "library" }],
|
|
2544
|
-
["testng", { displayName: "TestNG", kind: "library" }],
|
|
2545
|
-
["assertj-core", { displayName: "AssertJ", kind: "library" }],
|
|
2546
|
-
["gson", { displayName: "Gson", kind: "library" }],
|
|
2547
|
-
["okhttp", { displayName: "OkHttp", kind: "library" }],
|
|
2548
|
-
["retrofit", { displayName: "Retrofit", kind: "library" }],
|
|
2549
|
-
["spock-core", { displayName: "Spock", kind: "library" }],
|
|
2550
|
-
// --- Groovy — Frameworks ---
|
|
2551
|
-
// Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
|
|
2552
|
-
// the `grails-`/`gorm-` prefix rules below catch the rest.
|
|
2553
|
-
["grails-core", { displayName: "Grails", kind: "framework" }],
|
|
2554
|
-
["gorm-core", { displayName: "GORM", kind: "library" }],
|
|
2555
|
-
// --- Go — Frameworks ---
|
|
2556
|
-
["github.com/gin-gonic/gin", { displayName: "Gin", kind: "framework" }],
|
|
2557
|
-
["github.com/labstack/echo", { displayName: "Echo", kind: "framework" }],
|
|
2558
|
-
["github.com/gofiber/fiber", { displayName: "Fiber", kind: "framework" }],
|
|
2559
|
-
["github.com/go-chi/chi", { displayName: "Chi", kind: "framework" }],
|
|
2560
|
-
["github.com/gorilla/mux", { displayName: "Gorilla Mux", kind: "framework" }],
|
|
2561
|
-
["github.com/beego/beego", { displayName: "Beego", kind: "framework" }],
|
|
2562
|
-
// --- Go — Libraries ---
|
|
2563
|
-
["github.com/stretchr/testify", { displayName: "Testify", kind: "library" }],
|
|
2564
|
-
["github.com/spf13/cobra", { displayName: "Cobra", kind: "library" }],
|
|
2565
|
-
["github.com/spf13/viper", { displayName: "Viper", kind: "library" }],
|
|
2566
|
-
["go.uber.org/zap", { displayName: "Zap", kind: "library" }],
|
|
2567
|
-
["github.com/sirupsen/logrus", { displayName: "Logrus", kind: "library" }],
|
|
2568
|
-
["gorm.io/gorm", { displayName: "GORM", kind: "library" }],
|
|
2569
|
-
["github.com/go-kit/kit", { displayName: "Go Kit", kind: "library" }],
|
|
2570
|
-
["google.golang.org/protobuf", { displayName: "Protocol Buffers", kind: "library" }],
|
|
2571
|
-
["google.golang.org/grpc", { displayName: "gRPC", kind: "library" }],
|
|
2572
|
-
["github.com/jackc/pgx", { displayName: "pgx", kind: "library" }],
|
|
2573
|
-
["github.com/redis/go-redis", { displayName: "go-redis", kind: "library" }],
|
|
2574
|
-
["github.com/aws/aws-sdk-go-v2", { displayName: "AWS SDK for Go", kind: "library" }],
|
|
2575
|
-
["github.com/prometheus/client_golang", { displayName: "Prometheus Client", kind: "library" }],
|
|
2576
|
-
// --- Rust — Frameworks ---
|
|
2577
|
-
["actix-web", { displayName: "Actix Web", kind: "framework" }],
|
|
2578
|
-
["axum", { displayName: "Axum", kind: "framework" }],
|
|
2579
|
-
["rocket", { displayName: "Rocket", kind: "framework" }],
|
|
2580
|
-
["warp", { displayName: "Warp", kind: "framework" }],
|
|
2581
|
-
["tauri", { displayName: "Tauri", kind: "framework" }],
|
|
2582
|
-
// --- Rust — Libraries ---
|
|
2583
|
-
["tokio", { displayName: "Tokio", kind: "library" }],
|
|
2584
|
-
["serde", { displayName: "Serde", kind: "library" }],
|
|
2585
|
-
["clap", { displayName: "clap", kind: "library" }],
|
|
2586
|
-
["reqwest", { displayName: "reqwest", kind: "library" }],
|
|
2587
|
-
["diesel", { displayName: "Diesel", kind: "library" }],
|
|
2588
|
-
["sqlx", { displayName: "SQLx", kind: "library" }],
|
|
2589
|
-
["tracing", { displayName: "Tracing", kind: "library" }],
|
|
2590
|
-
["anyhow", { displayName: "anyhow", kind: "library" }],
|
|
2591
|
-
["thiserror", { displayName: "thiserror", kind: "library" }],
|
|
2592
|
-
["rayon", { displayName: "Rayon", kind: "library" }],
|
|
2593
|
-
// --- PHP — Frameworks ---
|
|
2594
|
-
["laravel/framework", { displayName: "Laravel", kind: "framework" }],
|
|
2595
|
-
["symfony/framework-bundle", { displayName: "Symfony", kind: "framework" }],
|
|
2596
|
-
["symfony/symfony", { displayName: "Symfony", kind: "framework" }],
|
|
2597
|
-
["codeigniter4/framework", { displayName: "CodeIgniter", kind: "framework" }],
|
|
2598
|
-
["slim/slim", { displayName: "Slim", kind: "framework" }],
|
|
2599
|
-
["cakephp/cakephp", { displayName: "CakePHP", kind: "framework" }],
|
|
2600
|
-
// WordPress is rarely a real Composer dependency; these are the common
|
|
2601
|
-
// "WordPress as a dependency" packages used by Bedrock/Composer-based sites.
|
|
2602
|
-
["johnpbloch/wordpress-core", { displayName: "WordPress", kind: "framework" }],
|
|
2603
|
-
["johnpbloch/wordpress", { displayName: "WordPress", kind: "framework" }],
|
|
2604
|
-
["roots/wordpress", { displayName: "WordPress", kind: "framework" }],
|
|
2605
|
-
["drupal/core", { displayName: "Drupal", kind: "framework" }],
|
|
2606
|
-
["drupal/core-recommended", { displayName: "Drupal", kind: "framework" }],
|
|
2607
|
-
["magento/product-community-edition", { displayName: "Magento", kind: "framework" }],
|
|
2608
|
-
["magento/product-enterprise-edition", { displayName: "Magento", kind: "framework" }],
|
|
2609
|
-
["magento/framework", { displayName: "Magento", kind: "framework" }],
|
|
2610
|
-
// --- PHP — Libraries ---
|
|
2611
|
-
["phpunit/phpunit", { displayName: "PHPUnit", kind: "library" }],
|
|
2612
|
-
["guzzlehttp/guzzle", { displayName: "Guzzle", kind: "library" }],
|
|
2613
|
-
["monolog/monolog", { displayName: "Monolog", kind: "library" }],
|
|
2614
|
-
["nesbot/carbon", { displayName: "Carbon", kind: "library" }],
|
|
2615
|
-
["doctrine/orm", { displayName: "Doctrine ORM", kind: "library" }],
|
|
2616
|
-
["doctrine/dbal", { displayName: "Doctrine DBAL", kind: "library" }],
|
|
2617
|
-
["league/flysystem", { displayName: "Flysystem", kind: "library" }],
|
|
2618
|
-
["php-amqplib/php-amqplib", { displayName: "php-amqplib", kind: "library" }],
|
|
2619
|
-
["predis/predis", { displayName: "Predis", kind: "library" }],
|
|
2620
|
-
// --- PHP — Tools ---
|
|
2621
|
-
["phpstan/phpstan", { displayName: "PHPStan", kind: "tool" }],
|
|
2622
|
-
["squizlabs/php_codesniffer", { displayName: "PHP_CodeSniffer", kind: "tool" }],
|
|
2623
|
-
// --- Swift — Frameworks ---
|
|
2624
|
-
["vapor", { displayName: "Vapor", kind: "framework" }],
|
|
2625
|
-
// --- Swift — Libraries ---
|
|
2626
|
-
["Alamofire", { displayName: "Alamofire", kind: "library" }],
|
|
2627
|
-
["SnapKit", { displayName: "SnapKit", kind: "library" }],
|
|
2628
|
-
["Kingfisher", { displayName: "Kingfisher", kind: "library" }],
|
|
2629
|
-
["Moya", { displayName: "Moya", kind: "library" }],
|
|
2630
|
-
["RxSwift", { displayName: "RxSwift", kind: "library" }],
|
|
2631
|
-
["RealmSwift", { displayName: "Realm", kind: "library" }],
|
|
2632
|
-
// --- Scala — Frameworks ---
|
|
2633
|
-
["play-server", { displayName: "Play Framework", kind: "framework" }],
|
|
2634
|
-
["akka-actor", { displayName: "Akka", kind: "framework" }],
|
|
2635
|
-
["spray-can", { displayName: "Spray", kind: "framework" }],
|
|
2636
|
-
// --- Scala — Libraries ---
|
|
2637
|
-
["scalatest", { displayName: "ScalaTest", kind: "library" }],
|
|
2638
|
-
["specs2-core", { displayName: "Specs2", kind: "library" }],
|
|
2639
|
-
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
2640
|
-
["circe-core", { displayName: "Circe", kind: "library" }],
|
|
2641
|
-
// --- Elixir — Frameworks ---
|
|
2642
|
-
["phoenix", { displayName: "Phoenix", kind: "framework" }],
|
|
2643
|
-
["plug", { displayName: "Plug", kind: "framework" }],
|
|
2644
|
-
["plug_cowboy", { displayName: "Plug", kind: "framework" }],
|
|
2645
|
-
// --- Elixir — Libraries ---
|
|
2646
|
-
["ecto", { displayName: "Ecto", kind: "library" }],
|
|
2647
|
-
["ecto_sql", { displayName: "Ecto", kind: "library" }],
|
|
2648
|
-
["phoenix_ecto", { displayName: "Ecto", kind: "library" }],
|
|
2649
|
-
// --- Crystal — Frameworks ---
|
|
2650
|
-
["amber", { displayName: "Amber", kind: "framework" }],
|
|
2651
|
-
["lucky", { displayName: "Lucky", kind: "framework" }],
|
|
2652
|
-
["kemal", { displayName: "Kemal", kind: "framework" }],
|
|
2653
|
-
// --- Dart — Frameworks ---
|
|
2654
|
-
// `flutter` and `angular` appear as plain keys in pubspec.yaml. NOTE: `angular`
|
|
2655
|
-
// here is Dart's AngularDart. The npm package `angular` is AngularJS (1.x) — a
|
|
2656
|
-
// different framework — so package.json overrides this via MANIFEST_SCOPED_CATALOG.
|
|
2657
|
-
["flutter", { displayName: "Flutter", kind: "framework" }],
|
|
2658
|
-
["flutter_test", { displayName: "Flutter", kind: "framework" }],
|
|
2659
|
-
["angular", { displayName: "Angular", kind: "framework" }],
|
|
2660
|
-
// --- Dart — Libraries ---
|
|
2661
|
-
// Kept to Dart-specific names with negligible cross-ecosystem collision risk
|
|
2662
|
-
// (generic names like "get"/"provider" are intentionally omitted).
|
|
2663
|
-
["riverpod", { displayName: "Riverpod", kind: "library" }],
|
|
2664
|
-
["flutter_bloc", { displayName: "Bloc", kind: "library" }],
|
|
2665
|
-
["dio", { displayName: "Dio", kind: "library" }],
|
|
2666
|
-
// --- .NET (C# / VB) — Libraries ---
|
|
2667
|
-
// NuGet package IDs. Microsoft.* families are also covered by prefix rules below.
|
|
2668
|
-
["EntityFramework", { displayName: "Entity Framework", kind: "library" }],
|
|
2669
|
-
["NUnit", { displayName: "NUnit", kind: "library" }],
|
|
2670
|
-
["Moq", { displayName: "Moq", kind: "library" }],
|
|
2671
|
-
["NSubstitute", { displayName: "NSubstitute", kind: "library" }],
|
|
2672
|
-
["FluentAssertions", { displayName: "FluentAssertions", kind: "library" }],
|
|
2673
|
-
["Shouldly", { displayName: "Shouldly", kind: "library" }],
|
|
2674
|
-
["NLog", { displayName: "NLog", kind: "library" }],
|
|
2675
|
-
["log4net", { displayName: "log4net", kind: "library" }],
|
|
2676
|
-
["Newtonsoft.Json", { displayName: "Json.NET", kind: "library" }],
|
|
2677
|
-
["AutoMapper", { displayName: "AutoMapper", kind: "library" }],
|
|
2678
|
-
["Dapper", { displayName: "Dapper", kind: "library" }],
|
|
2679
|
-
["MediatR", { displayName: "MediatR", kind: "library" }]
|
|
2680
|
-
]);
|
|
2681
|
-
var DEPENDENCY_PREFIX_RULES = [
|
|
2682
|
-
{ prefix: "@angular/", classification: { displayName: "Angular", kind: "framework" } },
|
|
2683
|
-
{ prefix: "@nestjs/", classification: { displayName: "NestJS", kind: "framework" } },
|
|
2684
|
-
{ prefix: "@remix-run/", classification: { displayName: "Remix", kind: "framework" } },
|
|
2685
|
-
{ prefix: "@sveltejs/", classification: { displayName: "SvelteKit", kind: "framework" } },
|
|
2686
|
-
{ prefix: "@nuxt/", classification: { displayName: "Nuxt", kind: "framework" } },
|
|
2687
|
-
{ prefix: "@storybook/", classification: { displayName: "Storybook", kind: "library" } },
|
|
2688
|
-
{
|
|
2689
|
-
prefix: "@testing-library/",
|
|
2690
|
-
classification: { displayName: "Testing Library", kind: "library" }
|
|
2691
|
-
},
|
|
2692
|
-
{ prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
|
|
2693
|
-
{ prefix: "@wdio/", classification: { displayName: "WebdriverIO", kind: "library" } },
|
|
2694
|
-
// AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
|
|
2695
|
-
{ prefix: "@aws-cdk/", classification: { displayName: "AWS CDK", kind: "framework" } },
|
|
2696
|
-
{ prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
|
|
2697
|
-
{ prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
|
|
2698
|
-
{ prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
|
|
2699
|
-
{ prefix: "@formatjs/", classification: { displayName: "formatjs", kind: "library" } },
|
|
2700
|
-
{ prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
|
|
2701
|
-
{ prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
|
|
2702
|
-
{ prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
|
|
2703
|
-
{ prefix: "@chakra-ui/", classification: { displayName: "Chakra UI", kind: "library" } },
|
|
2704
|
-
{
|
|
2705
|
-
prefix: "@lwc/",
|
|
2706
|
-
classification: { displayName: "Lightning Web Components", kind: "platform" }
|
|
2707
|
-
},
|
|
2708
|
-
{ prefix: "@salesforce/", classification: { displayName: "Salesforce", kind: "platform" } },
|
|
2709
|
-
{ prefix: "@shopify/", classification: { displayName: "Shopify", kind: "platform" } },
|
|
2710
|
-
{ prefix: "@aws-sdk/", classification: { displayName: "AWS SDK", kind: "library" } },
|
|
2711
|
-
{ prefix: "@google-cloud/", classification: { displayName: "Google Cloud", kind: "library" } },
|
|
2712
|
-
{ prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } },
|
|
2713
|
-
// Groovy — Grails/GORM artifact families (build.gradle artifactIds).
|
|
2714
|
-
{ prefix: "grails-", classification: { displayName: "Grails", kind: "framework" } },
|
|
2715
|
-
{ prefix: "gorm-", classification: { displayName: "GORM", kind: "library" } },
|
|
2716
|
-
// .NET — Microsoft package families and common test/logging libraries. More
|
|
2717
|
-
// specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
|
|
2718
|
-
{
|
|
2719
|
-
prefix: "Microsoft.AspNetCore.Components.WebAssembly",
|
|
2720
|
-
classification: { displayName: "Blazor", kind: "framework" }
|
|
2721
|
-
},
|
|
2722
|
-
{
|
|
2723
|
-
prefix: "Microsoft.AspNetCore.",
|
|
2724
|
-
classification: { displayName: "ASP.NET Core", kind: "framework" }
|
|
2725
|
-
},
|
|
2726
|
-
{
|
|
2727
|
-
prefix: "Microsoft.EntityFrameworkCore",
|
|
2728
|
-
classification: { displayName: "Entity Framework", kind: "library" }
|
|
2729
|
-
},
|
|
2730
|
-
{
|
|
2731
|
-
prefix: "Microsoft.Azure.Functions",
|
|
2732
|
-
classification: { displayName: "Azure Functions", kind: "framework" }
|
|
2733
|
-
},
|
|
2734
|
-
{
|
|
2735
|
-
prefix: "Microsoft.Azure.WebJobs",
|
|
2736
|
-
classification: { displayName: "Azure Functions", kind: "framework" }
|
|
2737
|
-
},
|
|
2738
|
-
{ prefix: "xunit", classification: { displayName: "xUnit", kind: "library" } },
|
|
2739
|
-
{ prefix: "MSTest.", classification: { displayName: "MSTest", kind: "library" } },
|
|
2740
|
-
{ prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
|
|
2741
|
-
];
|
|
2742
|
-
var MANIFEST_SCOPED_CATALOG = /* @__PURE__ */ new Map([
|
|
2743
|
-
["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])]
|
|
2744
|
-
]);
|
|
2745
|
-
function classifyDependency(name, manifestBasename) {
|
|
2746
|
-
if (manifestBasename) {
|
|
2747
|
-
const scoped = MANIFEST_SCOPED_CATALOG.get(manifestBasename)?.get(name);
|
|
2748
|
-
if (scoped) return scoped;
|
|
2749
|
-
}
|
|
2750
|
-
const exact = DEPENDENCY_CATALOG.get(name);
|
|
2751
|
-
if (exact) return exact;
|
|
2752
|
-
for (const rule of DEPENDENCY_PREFIX_RULES) {
|
|
2753
|
-
if (name.startsWith(rule.prefix)) return rule.classification;
|
|
2754
|
-
}
|
|
2755
|
-
return void 0;
|
|
2756
|
-
}
|
|
2757
|
-
function pathBasename(filePath) {
|
|
2758
|
-
return filePath.split("/").pop() ?? filePath;
|
|
2759
|
-
}
|
|
2760
|
-
var FILE_SIGNAL_RULES = [
|
|
2761
|
-
{
|
|
2762
|
-
name: ".flowconfig",
|
|
2763
|
-
matches: (f) => pathBasename(f) === ".flowconfig",
|
|
2764
|
-
classification: { displayName: "Flow", kind: "library" }
|
|
2765
|
-
},
|
|
2766
|
-
{
|
|
2767
|
-
name: "cdk.json",
|
|
2768
|
-
matches: (f) => pathBasename(f) === "cdk.json",
|
|
2769
|
-
classification: { displayName: "AWS CDK", kind: "framework" }
|
|
2770
|
-
},
|
|
2771
|
-
{
|
|
2772
|
-
name: ".meteor/release",
|
|
2773
|
-
matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
|
|
2774
|
-
classification: { displayName: "Meteor", kind: "framework" }
|
|
2775
|
-
},
|
|
2776
|
-
{
|
|
2777
|
-
name: "sfdx-project.json",
|
|
2778
|
-
matches: (f) => pathBasename(f) === "sfdx-project.json",
|
|
2779
|
-
classification: { displayName: "Salesforce", kind: "platform" }
|
|
2780
|
-
},
|
|
2781
|
-
{
|
|
2782
|
-
name: "lwc.config.json",
|
|
2783
|
-
matches: (f) => pathBasename(f) === "lwc.config.json",
|
|
2784
|
-
classification: { displayName: "Lightning Web Components", kind: "platform" }
|
|
2785
|
-
},
|
|
2786
|
-
{
|
|
2787
|
-
name: "suitecloud.config.js",
|
|
2788
|
-
matches: (f) => pathBasename(f) === "suitecloud.config.js",
|
|
2789
|
-
classification: { displayName: "SuiteScript", kind: "platform" }
|
|
2790
|
-
},
|
|
2791
|
-
{
|
|
2792
|
-
name: ".github/workflows",
|
|
2793
|
-
matches: (f) => f.startsWith(".github/workflows/"),
|
|
2794
|
-
classification: { displayName: "GitHub Actions", kind: "framework" }
|
|
2795
|
-
},
|
|
2796
|
-
{
|
|
2797
|
-
name: ".circleci",
|
|
2798
|
-
matches: (f) => f.startsWith(".circleci/"),
|
|
2799
|
-
classification: { displayName: "CircleCI", kind: "framework" }
|
|
2800
|
-
},
|
|
2801
|
-
{
|
|
2802
|
-
name: ".gitlab-ci.yml",
|
|
2803
|
-
matches: (f) => f === ".gitlab-ci.yml" || f.startsWith(".gitlab/"),
|
|
2804
|
-
classification: { displayName: "GitLab CI", kind: "framework" }
|
|
2805
|
-
},
|
|
2806
|
-
{
|
|
2807
|
-
name: "bitbucket-pipelines.yml",
|
|
2808
|
-
matches: (f) => f === "bitbucket-pipelines.yml",
|
|
2809
|
-
classification: { displayName: "Bitbucket Pipelines", kind: "framework" }
|
|
2810
|
-
},
|
|
2811
|
-
{
|
|
2812
|
-
name: "azure-pipelines.yml",
|
|
2813
|
-
matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
|
|
2814
|
-
classification: { displayName: "Azure Pipelines", kind: "framework" }
|
|
2815
|
-
}
|
|
2816
|
-
];
|
|
2817
|
-
function detectFileSignals(files) {
|
|
2818
|
-
const result = [];
|
|
2819
|
-
for (const rule of FILE_SIGNAL_RULES) {
|
|
2820
|
-
const hit = files.find((f) => rule.matches(f));
|
|
2821
|
-
if (!hit) continue;
|
|
2822
|
-
result.push({
|
|
2823
|
-
name: rule.name,
|
|
2824
|
-
displayName: rule.classification.displayName,
|
|
2825
|
-
kind: rule.classification.kind,
|
|
2826
|
-
source: "file",
|
|
2827
|
-
manifestFile: hit
|
|
2828
|
-
});
|
|
2829
|
-
}
|
|
2830
|
-
return result;
|
|
2831
|
-
}
|
|
2832
|
-
var PACKAGE_JSON_SIGNAL_DISPLAY_NAMES = ["ESLint Plugin"];
|
|
2833
|
-
function isEslintPluginName(name) {
|
|
2834
|
-
return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
|
|
2835
|
-
}
|
|
2836
|
-
function detectPackageJsonSignals(content) {
|
|
2837
|
-
let pkg;
|
|
2838
|
-
try {
|
|
2839
|
-
pkg = JSON.parse(content);
|
|
2840
|
-
} catch {
|
|
2841
|
-
return [];
|
|
2842
|
-
}
|
|
2843
|
-
const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
|
|
2844
|
-
if (name && isEslintPluginName(name)) {
|
|
2845
|
-
return [
|
|
2846
|
-
{
|
|
2847
|
-
name,
|
|
2848
|
-
displayName: "ESLint Plugin",
|
|
2849
|
-
kind: "tool",
|
|
2850
|
-
source: "manifest",
|
|
2851
|
-
manifestFile: "package.json"
|
|
2852
|
-
}
|
|
2853
|
-
];
|
|
2854
|
-
}
|
|
2855
|
-
return [];
|
|
2856
|
-
}
|
|
2857
|
-
function getKnownDisplayNames() {
|
|
2858
|
-
const names = /* @__PURE__ */ new Set();
|
|
2859
|
-
for (const { displayName } of DEPENDENCY_CATALOG.values()) names.add(displayName);
|
|
2860
|
-
for (const scoped of MANIFEST_SCOPED_CATALOG.values())
|
|
2861
|
-
for (const { displayName } of scoped.values()) names.add(displayName);
|
|
2862
|
-
for (const rule of DEPENDENCY_PREFIX_RULES) names.add(rule.classification.displayName);
|
|
2863
|
-
for (const rule of FILE_SIGNAL_RULES) names.add(rule.classification.displayName);
|
|
2864
|
-
for (const name of PACKAGE_JSON_SIGNAL_DISPLAY_NAMES) names.add(name);
|
|
2865
|
-
return names;
|
|
2866
|
-
}
|
|
2867
|
-
var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
|
|
2868
|
-
"Dockerfile",
|
|
2869
|
-
"docker-compose.yml",
|
|
2870
|
-
"docker-compose.yaml",
|
|
2871
|
-
"compose.yml",
|
|
2872
|
-
"compose.yaml",
|
|
2873
|
-
"Makefile",
|
|
2874
|
-
"Justfile",
|
|
2875
|
-
"pom.xml",
|
|
2876
|
-
"build.gradle",
|
|
2877
|
-
"build.gradle.kts",
|
|
2878
|
-
"build.sbt",
|
|
2879
|
-
"go.mod",
|
|
2880
|
-
"Cargo.toml",
|
|
2881
|
-
"Gemfile",
|
|
2882
|
-
"Podfile",
|
|
2883
|
-
"Package.swift",
|
|
2884
|
-
"Jenkinsfile",
|
|
2885
|
-
"Vagrantfile",
|
|
2886
|
-
"Procfile",
|
|
2887
|
-
"pubspec.yaml",
|
|
2888
|
-
"mix.exs",
|
|
2889
|
-
"shard.yml",
|
|
2890
|
-
"packages.config",
|
|
2891
|
-
"Directory.Packages.props"
|
|
2892
|
-
]);
|
|
2893
|
-
var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
|
|
2894
|
-
".dockerignore",
|
|
2895
|
-
"Taskfile.yml",
|
|
2896
|
-
"Rakefile",
|
|
2897
|
-
"Gruntfile.js",
|
|
2898
|
-
"Gulpfile.js",
|
|
2899
|
-
"settings.gradle",
|
|
2900
|
-
"settings.gradle.kts",
|
|
2901
|
-
"go.sum",
|
|
2902
|
-
"Cargo.lock",
|
|
2903
|
-
"CLAUDE.md",
|
|
2904
|
-
"AGENTS.md",
|
|
2905
|
-
"agents.md",
|
|
2906
|
-
".cursorrules",
|
|
2907
|
-
"copilot-instructions.md",
|
|
2908
|
-
".travis.yml",
|
|
2909
|
-
".gitlab-ci.yml",
|
|
2910
|
-
"azure-pipelines.yml",
|
|
2911
|
-
"bitbucket-pipelines.yml",
|
|
2912
|
-
"appveyor.yml",
|
|
2913
|
-
"package.json",
|
|
2914
|
-
"requirements.txt",
|
|
2915
|
-
"pyproject.toml",
|
|
2916
|
-
"Pipfile",
|
|
2917
|
-
"composer.json",
|
|
2918
|
-
"tsconfig.json",
|
|
2919
|
-
"jsconfig.json",
|
|
2920
|
-
"serverless.yml",
|
|
2921
|
-
"serverless.yaml",
|
|
2922
|
-
"cdk.json",
|
|
2923
|
-
"pulumi.yaml",
|
|
2924
|
-
"README.md",
|
|
2925
|
-
"CHANGELOG.md",
|
|
2926
|
-
"CONTRIBUTING.md",
|
|
2927
|
-
"LICENSE",
|
|
2928
|
-
"renovate.json",
|
|
2929
|
-
".nvmrc",
|
|
2930
|
-
".node-version",
|
|
2931
|
-
".python-version",
|
|
2932
|
-
".ruby-version",
|
|
2933
|
-
".tool-versions"
|
|
2934
|
-
]);
|
|
2935
|
-
var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
|
|
2936
|
-
var CONFIG_HEURISTIC_PATTERNS = [
|
|
2937
|
-
/^\..*rc$/,
|
|
2938
|
-
// .eslintrc, .prettierrc, etc.
|
|
2939
|
-
/^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
|
|
2940
|
-
// .eslintrc.json, etc.
|
|
2941
|
-
/^.*\.config\.(js|ts|mjs|cjs)$/,
|
|
2942
|
-
// vite.config.ts, jest.config.js, etc.
|
|
2943
|
-
/^\.env(\..+)?$/
|
|
2944
|
-
// .env, .env.local, .env.production
|
|
2945
|
-
];
|
|
2946
|
-
function isNotableFile(filePath) {
|
|
2947
|
-
const basename = filePath.split("/").pop() ?? filePath;
|
|
2948
|
-
const isRoot = !filePath.includes("/");
|
|
2949
|
-
if (NOTABLE_ANY_DEPTH.has(basename)) return true;
|
|
2950
|
-
if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
|
|
2951
|
-
if (/\.(csproj|vbproj|fsproj|sln)$/i.test(basename)) return true;
|
|
2952
|
-
for (const prefix of NOTABLE_PATH_PREFIXES) {
|
|
2953
|
-
if (filePath.startsWith(prefix)) return true;
|
|
2954
|
-
}
|
|
2955
|
-
if (!filePath.includes("/")) {
|
|
2956
|
-
for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
|
|
2957
|
-
if (pattern.test(basename)) return true;
|
|
2958
|
-
}
|
|
2959
|
-
}
|
|
2960
|
-
return false;
|
|
2961
|
-
}
|
|
2341
|
+
// src/discover/classify.ts
|
|
2342
|
+
var import_tooling6 = require("@codacy/tooling");
|
|
2343
|
+
|
|
2344
|
+
// src/discover/ecosystems/npm.ts
|
|
2962
2345
|
function parsePackageJson(content) {
|
|
2963
2346
|
try {
|
|
2964
2347
|
const pkg = JSON.parse(content);
|
|
@@ -2976,18 +2359,236 @@ function parsePackageJson(content) {
|
|
|
2976
2359
|
return [];
|
|
2977
2360
|
}
|
|
2978
2361
|
}
|
|
2362
|
+
var TELLTALES = [
|
|
2363
|
+
// --- Frameworks ---
|
|
2364
|
+
["react", "react"],
|
|
2365
|
+
["react-dom", "react-dom"],
|
|
2366
|
+
["react-native", "react-native"],
|
|
2367
|
+
["vue", "vue"],
|
|
2368
|
+
["nuxt", "nuxt"],
|
|
2369
|
+
["@angular/core", "angular"],
|
|
2370
|
+
["svelte", "svelte"],
|
|
2371
|
+
["@sveltejs/kit", "sveltekit"],
|
|
2372
|
+
["next", "next"],
|
|
2373
|
+
["ember-source", "ember"],
|
|
2374
|
+
["lit", "lit"],
|
|
2375
|
+
["solid-js", "solid"],
|
|
2376
|
+
["astro", "astro"],
|
|
2377
|
+
["@builder.io/qwik", "qwik"],
|
|
2378
|
+
["@builder.io/qwik-city", "qwik"],
|
|
2379
|
+
["@remix-run/react", "remix"],
|
|
2380
|
+
["gatsby", "gatsby"],
|
|
2381
|
+
["preact", "preact"],
|
|
2382
|
+
["express", "express"],
|
|
2383
|
+
["@nestjs/core", "nestjs"],
|
|
2384
|
+
["fastify", "fastify"],
|
|
2385
|
+
["koa", "koa"],
|
|
2386
|
+
["@hapi/hapi", "hapi"],
|
|
2387
|
+
["hono", "hono"],
|
|
2388
|
+
["electron", "electron"],
|
|
2389
|
+
// AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
|
|
2390
|
+
// and the v1 `@aws-cdk/*` construct packages (a prefix rule).
|
|
2391
|
+
["aws-cdk-lib", "aws-cdk"],
|
|
2392
|
+
["aws-cdk", "aws-cdk"],
|
|
2393
|
+
// The v2 SDK is one flat package; v3 is the scoped `@aws-sdk/*` family, which a
|
|
2394
|
+
// prefix rule below covers. Both mean "this code calls AWS".
|
|
2395
|
+
["aws-sdk", "aws-sdk"],
|
|
2396
|
+
// --- Libraries ---
|
|
2397
|
+
["lodash", "lodash"],
|
|
2398
|
+
["underscore", "underscore"],
|
|
2399
|
+
["ramda", "ramda"],
|
|
2400
|
+
["jest", "jest"],
|
|
2401
|
+
["vitest", "vitest"],
|
|
2402
|
+
["@playwright/test", "playwright"],
|
|
2403
|
+
["playwright", "playwright"],
|
|
2404
|
+
["puppeteer", "puppeteer"],
|
|
2405
|
+
["puppeteer-core", "puppeteer"],
|
|
2406
|
+
["cypress", "cypress"],
|
|
2407
|
+
["mocha", "mocha"],
|
|
2408
|
+
["ava", "ava"],
|
|
2409
|
+
["jasmine", "jasmine"],
|
|
2410
|
+
["jasmine-core", "jasmine"],
|
|
2411
|
+
["@testing-library/react", "testing-library"],
|
|
2412
|
+
["@testing-library/vue", "testing-library"],
|
|
2413
|
+
["@testing-library/dom", "testing-library"],
|
|
2414
|
+
["tailwindcss", "tailwind-css"],
|
|
2415
|
+
["@storybook/react", "storybook"],
|
|
2416
|
+
["@storybook/vue3", "storybook"],
|
|
2417
|
+
["@storybook/svelte", "storybook"],
|
|
2418
|
+
["redux", "redux"],
|
|
2419
|
+
["@reduxjs/toolkit", "redux-toolkit"],
|
|
2420
|
+
["mobx", "mobx"],
|
|
2421
|
+
["zustand", "zustand"],
|
|
2422
|
+
["axios", "axios"],
|
|
2423
|
+
["date-fns", "date-fns"],
|
|
2424
|
+
["dayjs", "dayjs"],
|
|
2425
|
+
["moment", "moment"],
|
|
2426
|
+
["rxjs", "rxjs"],
|
|
2427
|
+
["zod", "zod"],
|
|
2428
|
+
["joi", "joi"],
|
|
2429
|
+
["yup", "yup"],
|
|
2430
|
+
["prisma", "prisma"],
|
|
2431
|
+
["@prisma/client", "prisma"],
|
|
2432
|
+
["typeorm", "typeorm"],
|
|
2433
|
+
["sequelize", "sequelize"],
|
|
2434
|
+
["drizzle-orm", "drizzle-orm"],
|
|
2435
|
+
["mongoose", "mongoose"],
|
|
2436
|
+
["graphql", "graphql"],
|
|
2437
|
+
["@apollo/client", "apollo-client"],
|
|
2438
|
+
["socket.io", "socketio"],
|
|
2439
|
+
["jquery", "jquery"],
|
|
2440
|
+
["mongodb", "mongodb"],
|
|
2441
|
+
["three", "threejs"],
|
|
2442
|
+
["d3", "d3"],
|
|
2443
|
+
["chart.js", "chartjs"],
|
|
2444
|
+
["sass", "sass"],
|
|
2445
|
+
["node-sass", "sass"],
|
|
2446
|
+
["less", "less"],
|
|
2447
|
+
["styled-components", "styled-components"],
|
|
2448
|
+
["@emotion/react", "emotion"],
|
|
2449
|
+
["pino", "pino"],
|
|
2450
|
+
["winston", "winston"],
|
|
2451
|
+
["i18next", "i18next"],
|
|
2452
|
+
// react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
|
|
2453
|
+
// lints both, so we report the umbrella name "formatjs" (matches the mapping key).
|
|
2454
|
+
["react-intl", "formatjs"],
|
|
2455
|
+
["vue-i18n", "vue-i18n"],
|
|
2456
|
+
["next-intl", "next-intl"],
|
|
2457
|
+
// Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
|
|
2458
|
+
["flow-bin", "flow"],
|
|
2459
|
+
// Legacy JS libraries with dedicated ESLint plugins.
|
|
2460
|
+
["backbone", "backbone"],
|
|
2461
|
+
["chai", "chai"],
|
|
2462
|
+
["codeceptjs", "codeceptjs"],
|
|
2463
|
+
["webdriverio", "webdriverio"],
|
|
2464
|
+
["react-relay", "relay"],
|
|
2465
|
+
["relay-runtime", "relay"],
|
|
2466
|
+
// --- Tools ---
|
|
2467
|
+
["typescript", "typescript"],
|
|
2468
|
+
["eslint", "eslint"],
|
|
2469
|
+
["prettier", "prettier"],
|
|
2470
|
+
["webpack", "webpack"],
|
|
2471
|
+
["vite", "vite"],
|
|
2472
|
+
["esbuild", "esbuild"],
|
|
2473
|
+
["rollup", "rollup"],
|
|
2474
|
+
["turbo", "turborepo"],
|
|
2475
|
+
["tsup", "tsup"],
|
|
2476
|
+
["@biomejs/biome", "biome"],
|
|
2477
|
+
["stylelint", "stylelint"],
|
|
2478
|
+
// --- Platforms ---
|
|
2479
|
+
["@lwc/engine-dom", "lightning-web-components"],
|
|
2480
|
+
["@salesforce/core", "salesforce"],
|
|
2481
|
+
["@shopify/polaris", "shopify-polaris"],
|
|
2482
|
+
// The npm `angular` package is AngularJS 1.x — a different framework from both
|
|
2483
|
+
// modern Angular (the `@angular/*` scope) and pub's AngularDart. One global
|
|
2484
|
+
// name-keyed catalog could not hold all three; three entities can.
|
|
2485
|
+
["angular", "angular-js"]
|
|
2486
|
+
];
|
|
2487
|
+
var PREFIXES = [
|
|
2488
|
+
["@angular/", "angular"],
|
|
2489
|
+
["@nestjs/", "nestjs"],
|
|
2490
|
+
["@remix-run/", "remix"],
|
|
2491
|
+
["@sveltejs/", "sveltekit"],
|
|
2492
|
+
["@nuxt/", "nuxt"],
|
|
2493
|
+
["@storybook/", "storybook"],
|
|
2494
|
+
["@testing-library/", "testing-library"],
|
|
2495
|
+
["@tanstack/", "tanstack"],
|
|
2496
|
+
["@wdio/", "webdriverio"],
|
|
2497
|
+
// AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
|
|
2498
|
+
["@aws-cdk/", "aws-cdk"],
|
|
2499
|
+
["@reduxjs/", "redux"],
|
|
2500
|
+
["@prisma/", "prisma"],
|
|
2501
|
+
["@apollo/", "apollo"],
|
|
2502
|
+
["@formatjs/", "formatjs"],
|
|
2503
|
+
["@lingui/", "lingui"],
|
|
2504
|
+
["@emotion/", "emotion"],
|
|
2505
|
+
["@mui/", "material-ui"],
|
|
2506
|
+
["@chakra-ui/", "chakra-ui"],
|
|
2507
|
+
["@lwc/", "lightning-web-components"],
|
|
2508
|
+
["@salesforce/", "salesforce"],
|
|
2509
|
+
["@shopify/", "shopify"],
|
|
2510
|
+
["@aws-sdk/", "aws-sdk"],
|
|
2511
|
+
["@google-cloud/", "google-cloud"],
|
|
2512
|
+
["@azure/", "azure-sdk"]
|
|
2513
|
+
];
|
|
2514
|
+
var npm = {
|
|
2515
|
+
id: "npm",
|
|
2516
|
+
manifests: [{ basename: "package.json", parser: parsePackageJson }],
|
|
2517
|
+
telltales: TELLTALES,
|
|
2518
|
+
prefixes: PREFIXES
|
|
2519
|
+
};
|
|
2520
|
+
|
|
2521
|
+
// src/discover/ecosystems/pypi.ts
|
|
2522
|
+
function parseRequirementLine(rawLine) {
|
|
2523
|
+
const line = rawLine.trim();
|
|
2524
|
+
if (!line || line.startsWith("#") || line.startsWith("-")) return void 0;
|
|
2525
|
+
const noComment = line.split("#")[0].trim();
|
|
2526
|
+
if (!noComment) return void 0;
|
|
2527
|
+
const match = noComment.match(/^([a-zA-Z0-9_.-]+(?:\[[^\]]*\])?)\s*([<>=!~@;].*)?$/);
|
|
2528
|
+
if (!match) return void 0;
|
|
2529
|
+
const rawName = match[1].replace(/\[.*\]$/, "");
|
|
2530
|
+
return { name: rawName.toLowerCase().replace(/_/g, "-"), version: match[2]?.trim() };
|
|
2531
|
+
}
|
|
2979
2532
|
function parseRequirementsTxt(content) {
|
|
2980
2533
|
const deps = [];
|
|
2981
2534
|
for (const rawLine of content.split("\n")) {
|
|
2982
|
-
const
|
|
2983
|
-
if (
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2535
|
+
const dep = parseRequirementLine(rawLine);
|
|
2536
|
+
if (dep) deps.push(dep);
|
|
2537
|
+
}
|
|
2538
|
+
return deps;
|
|
2539
|
+
}
|
|
2540
|
+
function sliceBalanced(content, fromIndex, open, close) {
|
|
2541
|
+
const start = content.indexOf(open, fromIndex);
|
|
2542
|
+
if (start === -1) return void 0;
|
|
2543
|
+
let depth = 0;
|
|
2544
|
+
let quote;
|
|
2545
|
+
for (let i = start; i < content.length; i++) {
|
|
2546
|
+
const ch = content[i];
|
|
2547
|
+
if (quote) {
|
|
2548
|
+
if (ch === "\\") i++;
|
|
2549
|
+
else if (ch === quote) quote = void 0;
|
|
2550
|
+
continue;
|
|
2551
|
+
}
|
|
2552
|
+
if (ch === '"' || ch === "'") {
|
|
2553
|
+
quote = ch;
|
|
2554
|
+
continue;
|
|
2555
|
+
}
|
|
2556
|
+
if (ch === open) depth++;
|
|
2557
|
+
else if (ch === close && --depth === 0) {
|
|
2558
|
+
return { body: content.slice(start + 1, i), endIndex: i };
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
return void 0;
|
|
2562
|
+
}
|
|
2563
|
+
function parseSetupPy(content) {
|
|
2564
|
+
const deps = [];
|
|
2565
|
+
const collectQuoted = (block) => {
|
|
2566
|
+
for (const quoted of block.matchAll(/["']([^"']+)["']/g)) {
|
|
2567
|
+
const dep = parseRequirementLine(quoted[1]);
|
|
2568
|
+
if (dep) deps.push(dep);
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
const valueStartOf = (key) => {
|
|
2572
|
+
const match = new RegExp(`\\b${key}\\s*=\\s*`).exec(content);
|
|
2573
|
+
return match ? match.index + match[0].length : -1;
|
|
2574
|
+
};
|
|
2575
|
+
for (const key of ["install_requires", "tests_require", "setup_requires"]) {
|
|
2576
|
+
const start = valueStartOf(key);
|
|
2577
|
+
if (start === -1 || content[start] !== "[") continue;
|
|
2578
|
+
const list = sliceBalanced(content, start, "[", "]");
|
|
2579
|
+
if (list) collectQuoted(list.body);
|
|
2580
|
+
}
|
|
2581
|
+
const extrasStart = valueStartOf("extras_require");
|
|
2582
|
+
if (extrasStart !== -1 && content[extrasStart] === "{") {
|
|
2583
|
+
const dict = sliceBalanced(content, extrasStart, "{", "}");
|
|
2584
|
+
if (dict) {
|
|
2585
|
+
let cursor = 0;
|
|
2586
|
+
while (cursor < dict.body.length) {
|
|
2587
|
+
const list = sliceBalanced(dict.body, cursor, "[", "]");
|
|
2588
|
+
if (!list) break;
|
|
2589
|
+
collectQuoted(list.body);
|
|
2590
|
+
cursor = list.endIndex + 1;
|
|
2591
|
+
}
|
|
2991
2592
|
}
|
|
2992
2593
|
}
|
|
2993
2594
|
return deps;
|
|
@@ -3048,6 +2649,107 @@ function parsePipfile(content) {
|
|
|
3048
2649
|
}
|
|
3049
2650
|
return deps;
|
|
3050
2651
|
}
|
|
2652
|
+
var TELLTALES2 = [
|
|
2653
|
+
// --- Frameworks ---
|
|
2654
|
+
["django", "django"],
|
|
2655
|
+
["flask", "flask"],
|
|
2656
|
+
["fastapi", "fastapi"],
|
|
2657
|
+
["tornado", "tornado"],
|
|
2658
|
+
["pyramid", "pyramid"],
|
|
2659
|
+
["starlette", "starlette"],
|
|
2660
|
+
["sanic", "sanic"],
|
|
2661
|
+
["bottle", "bottle"],
|
|
2662
|
+
["streamlit", "streamlit"],
|
|
2663
|
+
["gradio", "gradio"],
|
|
2664
|
+
["apache-airflow", "airflow"],
|
|
2665
|
+
// --- Libraries ---
|
|
2666
|
+
["flask-babel", "flask-babel"],
|
|
2667
|
+
["pytest", "pytest"],
|
|
2668
|
+
["numpy", "numpy"],
|
|
2669
|
+
["pandas", "pandas"],
|
|
2670
|
+
["requests", "requests"],
|
|
2671
|
+
["sqlalchemy", "sqlalchemy"],
|
|
2672
|
+
["celery", "celery"],
|
|
2673
|
+
["pydantic", "pydantic"],
|
|
2674
|
+
["boto3", "boto3"],
|
|
2675
|
+
// `botocore` is boto3's transport layer and `aws-lambda-powertools` depends on
|
|
2676
|
+
// it; either declared alone still means "this is AWS code". Lambda runtimes
|
|
2677
|
+
// preinstall boto3, so it is routinely absent from requirements.txt.
|
|
2678
|
+
["botocore", "boto3"],
|
|
2679
|
+
["aws-lambda-powertools", "boto3"],
|
|
2680
|
+
["scipy", "scipy"],
|
|
2681
|
+
["scikit-learn", "scikit-learn"],
|
|
2682
|
+
["tensorflow", "tensorflow"],
|
|
2683
|
+
["torch", "pytorch"],
|
|
2684
|
+
["httpx", "httpx"],
|
|
2685
|
+
["aiohttp", "aiohttp"],
|
|
2686
|
+
["pillow", "pillow"],
|
|
2687
|
+
["beautifulsoup4", "beautiful-soup"],
|
|
2688
|
+
["scrapy", "scrapy"],
|
|
2689
|
+
["alembic", "alembic"],
|
|
2690
|
+
["marshmallow", "marshmallow"],
|
|
2691
|
+
["click", "click"],
|
|
2692
|
+
["typer", "typer"],
|
|
2693
|
+
["rich", "rich"],
|
|
2694
|
+
["polars", "polars"],
|
|
2695
|
+
["dask", "dask"],
|
|
2696
|
+
["matplotlib", "matplotlib"],
|
|
2697
|
+
["seaborn", "seaborn"],
|
|
2698
|
+
["plotly", "plotly"],
|
|
2699
|
+
["jinja2", "jinja2"],
|
|
2700
|
+
["mako", "mako"],
|
|
2701
|
+
["lxml", "lxml"],
|
|
2702
|
+
["pyyaml", "pyyaml"],
|
|
2703
|
+
["paramiko", "paramiko"],
|
|
2704
|
+
["twilio", "twilio"],
|
|
2705
|
+
// Structured-concurrency libraries. Ruff's `ASYNC1xx` rules are written against
|
|
2706
|
+
// trio's checkpoint model and anyio implements the same one, so either gates them.
|
|
2707
|
+
["trio", "trio"],
|
|
2708
|
+
["anyio", "anyio"],
|
|
2709
|
+
// The two crypto packages are distinct projects with distinct Bandit checks
|
|
2710
|
+
// (`B413` imports PyCrypto, `B414` PyCryptodome), so they must not be merged.
|
|
2711
|
+
// `pycryptodomex` is the same project published under its `Cryptodome` namespace.
|
|
2712
|
+
["pycrypto", "pycrypto"],
|
|
2713
|
+
["pycryptodome", "pycryptodome"],
|
|
2714
|
+
["pycryptodomex", "pycryptodome"],
|
|
2715
|
+
// `pysnmp` was renamed to `pysnmplib` for part of its history; both are the
|
|
2716
|
+
// library Bandit's `B508`/`B509` weak-SNMP checks apply to.
|
|
2717
|
+
["pysnmp", "pysnmp"],
|
|
2718
|
+
["pysnmplib", "pysnmp"],
|
|
2719
|
+
// MongoDB drivers, one entry per ecosystem — they all gate the same
|
|
2720
|
+
// MongoDB-specific patterns (Opengrep's `java.mongodb.`/`python.pymongo.`).
|
|
2721
|
+
["pymongo", "mongodb"],
|
|
2722
|
+
["motor", "mongodb"],
|
|
2723
|
+
// --- Tools ---
|
|
2724
|
+
["black", "black"],
|
|
2725
|
+
["ruff", "ruff"],
|
|
2726
|
+
["mypy", "mypy"],
|
|
2727
|
+
["pylint", "pylint"],
|
|
2728
|
+
["flake8", "flake8"],
|
|
2729
|
+
["bandit", "bandit"],
|
|
2730
|
+
["isort", "isort"]
|
|
2731
|
+
];
|
|
2732
|
+
var pypi = {
|
|
2733
|
+
id: "pypi",
|
|
2734
|
+
manifests: [
|
|
2735
|
+
{ basename: "requirements.txt", parser: parseRequirementsTxt },
|
|
2736
|
+
{ basename: "pyproject.toml", parser: parsePyprojectToml },
|
|
2737
|
+
{ basename: "Pipfile", parser: parsePipfile },
|
|
2738
|
+
{ basename: "setup.py", parser: parseSetupPy },
|
|
2739
|
+
{
|
|
2740
|
+
// The requirements family, by path: real projects split it by environment
|
|
2741
|
+
// (`requirements-dev.txt`, `requirements/base.txt`, `requirements.in` for
|
|
2742
|
+
// pip-tools). Matching only the exact name made every dependency of a split
|
|
2743
|
+
// layout invisible, and with it the gating for Django, Flask, FastAPI,
|
|
2744
|
+
// pytest, pandas and NumPy patterns.
|
|
2745
|
+
matches: (filePath, basename) => /^requirements[a-zA-Z0-9_.-]*\.(txt|in)$/i.test(basename) || /(^|\/)requirements\/[^/]+\.(txt|in)$/i.test(filePath),
|
|
2746
|
+
parser: parseRequirementsTxt
|
|
2747
|
+
}
|
|
2748
|
+
],
|
|
2749
|
+
telltales: TELLTALES2
|
|
2750
|
+
};
|
|
2751
|
+
|
|
2752
|
+
// src/discover/ecosystems/gem.ts
|
|
3051
2753
|
function parseGemfile(content) {
|
|
3052
2754
|
const deps = [];
|
|
3053
2755
|
const gemRegex = /^\s*gem\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
|
|
@@ -3057,63 +2759,339 @@ function parseGemfile(content) {
|
|
|
3057
2759
|
}
|
|
3058
2760
|
return deps;
|
|
3059
2761
|
}
|
|
3060
|
-
|
|
2762
|
+
var TELLTALES3 = [
|
|
2763
|
+
// --- Frameworks ---
|
|
2764
|
+
["rails", "ruby-on-rails"],
|
|
2765
|
+
["sinatra", "sinatra"],
|
|
2766
|
+
["hanami", "hanami"],
|
|
2767
|
+
["grape", "grape"],
|
|
2768
|
+
["padrino", "padrino"],
|
|
2769
|
+
// --- Libraries ---
|
|
2770
|
+
["rspec", "rspec"],
|
|
2771
|
+
["rspec-core", "rspec"],
|
|
2772
|
+
["rspec-rails", "rspec-rails"],
|
|
2773
|
+
["capybara", "capybara"],
|
|
2774
|
+
["sidekiq", "sidekiq"],
|
|
2775
|
+
["devise", "devise"],
|
|
2776
|
+
["pundit", "pundit"],
|
|
2777
|
+
["activeadmin", "activeadmin"],
|
|
2778
|
+
["puma", "puma"],
|
|
2779
|
+
["nokogiri", "nokogiri"],
|
|
2780
|
+
["faraday", "faraday"],
|
|
2781
|
+
["httparty", "httparty"],
|
|
2782
|
+
["minitest", "minitest"],
|
|
2783
|
+
["factory_bot", "factorybot"],
|
|
2784
|
+
["dry-validation", "dry-validation"],
|
|
2785
|
+
// --- Tools ---
|
|
2786
|
+
["rubocop", "rubocop"],
|
|
2787
|
+
["bundler", "bundler"],
|
|
2788
|
+
// Sorbet ships as several gems; any of them means the project is Sorbet-typed.
|
|
2789
|
+
["sorbet", "sorbet"],
|
|
2790
|
+
["sorbet-runtime", "sorbet"],
|
|
2791
|
+
["sorbet-static", "sorbet"],
|
|
2792
|
+
["sorbet-static-and-runtime", "sorbet"],
|
|
2793
|
+
["solargraph", "solargraph"],
|
|
2794
|
+
// GetText, under both the plain gem and its Rails integration. RuboCop's
|
|
2795
|
+
// `I18n_GetText_*` cops rewrite `_()`-decorated strings, so they only apply to a
|
|
2796
|
+
// project that actually uses GetText rather than Rails' own I18n.
|
|
2797
|
+
["gettext", "gettext"],
|
|
2798
|
+
["gettext_i18n_rails", "gettext"],
|
|
2799
|
+
["rubocop-i18n", "gettext"],
|
|
2800
|
+
// --- Names other registries also publish ---
|
|
2801
|
+
// `faker`, `rake` and `graphql` all exist on npm and PyPI too, and all three
|
|
2802
|
+
// gate RuboCop cops. Reaching them only from a Gemfile is the whole point of
|
|
2803
|
+
// scoping by ecosystem — a JS dependency must not enable Ruby cops.
|
|
2804
|
+
["faker", "faker"],
|
|
2805
|
+
["rake", "rake"],
|
|
2806
|
+
["graphql", "graphql"]
|
|
2807
|
+
];
|
|
2808
|
+
var gem = {
|
|
2809
|
+
id: "gem",
|
|
2810
|
+
manifests: [{ basename: "Gemfile", parser: parseGemfile }],
|
|
2811
|
+
telltales: TELLTALES3
|
|
2812
|
+
};
|
|
2813
|
+
|
|
2814
|
+
// src/discover/ecosystems/maven.ts
|
|
2815
|
+
function parsePomXml(content) {
|
|
3061
2816
|
const deps = [];
|
|
3062
|
-
const depRegex =
|
|
2817
|
+
const depRegex = /<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g;
|
|
3063
2818
|
let match;
|
|
3064
2819
|
while ((match = depRegex.exec(content)) !== null) {
|
|
3065
|
-
deps.push({ name: match[1]
|
|
2820
|
+
deps.push({ name: match[1].trim() });
|
|
3066
2821
|
}
|
|
3067
2822
|
return deps;
|
|
3068
2823
|
}
|
|
3069
|
-
function
|
|
2824
|
+
function parseBuildGradle(content) {
|
|
3070
2825
|
const deps = [];
|
|
3071
|
-
const
|
|
2826
|
+
const depRegex = /(?:implementation|testImplementation|testCompileOnly|testRuntimeOnly|api|compileOnly|runtimeOnly|kapt|annotationProcessor)\s*(?:\(\s*)?["']([^"':]+):([^"':]+)(?::([^"']+))?["']/g;
|
|
3072
2827
|
let match;
|
|
3073
|
-
while ((match =
|
|
3074
|
-
|
|
3075
|
-
deps.push({ name: match[1], version: match[2] });
|
|
3076
|
-
}
|
|
2828
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
2829
|
+
deps.push({ name: match[2].trim(), version: match[3]?.trim() });
|
|
3077
2830
|
}
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
2831
|
+
return deps;
|
|
2832
|
+
}
|
|
2833
|
+
function parseVersionCatalogToml(content) {
|
|
2834
|
+
const deps = [];
|
|
2835
|
+
let inLibraries = false;
|
|
2836
|
+
for (const rawLine of content.split("\n")) {
|
|
2837
|
+
const line = rawLine.trim();
|
|
2838
|
+
if (line === "" || line.startsWith("#")) continue;
|
|
2839
|
+
if (line.startsWith("[")) {
|
|
2840
|
+
inLibraries = /^\[\s*libraries\s*\]/.test(line);
|
|
2841
|
+
continue;
|
|
2842
|
+
}
|
|
2843
|
+
if (!inLibraries) continue;
|
|
2844
|
+
const moduleMatch = line.match(/\bmodule\s*=\s*["'][^"':]+:([^"']+)["']/);
|
|
2845
|
+
const nameMatch = line.match(/\bname\s*=\s*["']([^"']+)["']/);
|
|
2846
|
+
const versionMatch = line.match(/\bversion\s*=\s*["']([^"']+)["']/);
|
|
2847
|
+
if (moduleMatch || nameMatch) {
|
|
2848
|
+
deps.push({
|
|
2849
|
+
name: (moduleMatch?.[1] ?? nameMatch?.[1] ?? "").trim(),
|
|
2850
|
+
version: versionMatch?.[1]?.trim()
|
|
2851
|
+
});
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
const shorthand = line.match(/^[A-Za-z0-9_.-]+\s*=\s*["'][^"':]+:([^"':]+)(?::([^"']+))?["']/);
|
|
2855
|
+
if (shorthand) {
|
|
2856
|
+
deps.push({ name: shorthand[1].trim(), version: shorthand[2]?.trim() });
|
|
3086
2857
|
}
|
|
3087
2858
|
}
|
|
3088
2859
|
return deps;
|
|
3089
2860
|
}
|
|
3090
|
-
function
|
|
2861
|
+
function parseBuildSbt(content) {
|
|
3091
2862
|
const deps = [];
|
|
3092
|
-
const depRegex =
|
|
2863
|
+
const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s*%(?!%)\s*(?:"([^"]+)")?/g;
|
|
3093
2864
|
let match;
|
|
3094
2865
|
while ((match = depRegex.exec(content)) !== null) {
|
|
3095
|
-
deps.push({ name: match[
|
|
2866
|
+
deps.push({ name: match[2], version: match[3] });
|
|
3096
2867
|
}
|
|
3097
2868
|
return deps;
|
|
3098
2869
|
}
|
|
3099
|
-
function
|
|
3100
|
-
const deps =
|
|
3101
|
-
const
|
|
2870
|
+
function parseSbtBuildDefinition(content) {
|
|
2871
|
+
const deps = parseBuildSbt(content);
|
|
2872
|
+
const pluginRegex = /addSbtPlugin\s*\(\s*"([^"]+)"\s*%%?\s*"([^"]+)"/g;
|
|
3102
2873
|
let match;
|
|
3103
|
-
while ((match =
|
|
3104
|
-
deps.push({ name: match[
|
|
2874
|
+
while ((match = pluginRegex.exec(content)) !== null) {
|
|
2875
|
+
deps.push({ name: `${match[1]}:${match[2]}` });
|
|
3105
2876
|
}
|
|
3106
2877
|
return deps;
|
|
3107
2878
|
}
|
|
3108
|
-
|
|
2879
|
+
var TELLTALES4 = [
|
|
2880
|
+
// --- Java / Kotlin — Frameworks ---
|
|
2881
|
+
["spring-boot-starter", "spring-boot"],
|
|
2882
|
+
["spring-boot-starter-web", "spring-boot"],
|
|
2883
|
+
["spring-core", "spring-framework"],
|
|
2884
|
+
["quarkus-core", "quarkus"],
|
|
2885
|
+
["micronaut-core", "micronaut"],
|
|
2886
|
+
["ktor-server-core", "ktor"],
|
|
2887
|
+
["android", "android-sdk"],
|
|
2888
|
+
// --- Java / Kotlin — Libraries ---
|
|
2889
|
+
["junit-jupiter", "junit-5"],
|
|
2890
|
+
["junit", "junit"],
|
|
2891
|
+
["junit-jupiter-api", "junit-5"],
|
|
2892
|
+
// Spring Boot's test starter is the single most common Java test declaration and
|
|
2893
|
+
// pulls JUnit 5 transitively; without it PMD's JUnit rules are unreachable for
|
|
2894
|
+
// most Spring projects. The `junit-jupiter-`/`junit-platform-` prefixes below
|
|
2895
|
+
// cover `-engine`/`-params`/`-launcher`, which Gradle declares separately.
|
|
2896
|
+
["spring-boot-starter-test", "junit-5"],
|
|
2897
|
+
["mockito-core", "mockito"],
|
|
2898
|
+
["lombok", "lombok"],
|
|
2899
|
+
["jackson-databind", "jackson"],
|
|
2900
|
+
["jackson-core", "jackson"],
|
|
2901
|
+
["hibernate-core", "hibernate"],
|
|
2902
|
+
["guava", "guava"],
|
|
2903
|
+
["commons-lang3", "apache-commons-lang"],
|
|
2904
|
+
["commons-io", "apache-commons-io"],
|
|
2905
|
+
["commons-logging", "apache-commons-logging"],
|
|
2906
|
+
["commons-fileupload", "apache-commons-fileupload"],
|
|
2907
|
+
["commons-fileupload2-core", "apache-commons-fileupload"],
|
|
2908
|
+
// The EJB/Jakarta EE API jars, under every name they have shipped as. PMD's
|
|
2909
|
+
// session-bean naming and static-field rules only mean anything with these on
|
|
2910
|
+
// the classpath.
|
|
2911
|
+
["jakarta.ejb-api", "jakarta-ee"],
|
|
2912
|
+
["javax.ejb-api", "jakarta-ee"],
|
|
2913
|
+
["jakarta.jakartaee-api", "jakarta-ee"],
|
|
2914
|
+
["jakarta.jakartaee-web-api", "jakarta-ee"],
|
|
2915
|
+
["javaee-api", "jakarta-ee"],
|
|
2916
|
+
["javaee-web-api", "jakarta-ee"],
|
|
2917
|
+
["slf4j-api", "slf4j"],
|
|
2918
|
+
["log4j-core", "log4j"],
|
|
2919
|
+
["logback-classic", "logback"],
|
|
2920
|
+
["testng", "testng"],
|
|
2921
|
+
["assertj-core", "assertj"],
|
|
2922
|
+
["gson", "gson"],
|
|
2923
|
+
["okhttp", "okhttp"],
|
|
2924
|
+
["retrofit", "retrofit"],
|
|
2925
|
+
["spock-core", "spock"],
|
|
2926
|
+
["mongodb-driver-sync", "mongodb"],
|
|
2927
|
+
["mongodb-driver-core", "mongodb"],
|
|
2928
|
+
["mongodb-driver-reactivestreams", "mongodb"],
|
|
2929
|
+
["mongo-java-driver", "mongodb"],
|
|
2930
|
+
// --- Groovy — Frameworks ---
|
|
2931
|
+
// Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
|
|
2932
|
+
// the `grails-`/`gorm-` prefix rules catch the rest.
|
|
2933
|
+
["grails-core", "grails"],
|
|
2934
|
+
["gorm-core", "grails-gorm"],
|
|
2935
|
+
// --- Scala — Frameworks ---
|
|
2936
|
+
// Play is declared as an sbt *plugin*, so `group:artifact` (from
|
|
2937
|
+
// `addSbtPlugin`) is the reliable signal; the library artifacts that follow only
|
|
2938
|
+
// appear in some builds. `play-json` is deliberately absent — it is a
|
|
2939
|
+
// standalone JSON library used widely outside Play.
|
|
2940
|
+
["com.typesafe.play:sbt-plugin", "play-framework"],
|
|
2941
|
+
["org.playframework:sbt-plugin", "play-framework"],
|
|
2942
|
+
["play", "play-framework"],
|
|
2943
|
+
["play-server", "play-framework"],
|
|
2944
|
+
["play-ws", "play-framework"],
|
|
2945
|
+
["play-test", "play-framework"],
|
|
2946
|
+
["play-slick", "play-framework"],
|
|
2947
|
+
["twirl-api", "play-framework"],
|
|
2948
|
+
["scalatestplus-play", "play-framework"],
|
|
2949
|
+
["akka-actor", "akka"],
|
|
2950
|
+
["spray-can", "spray"],
|
|
2951
|
+
// --- Scala — Libraries ---
|
|
2952
|
+
["scalatest", "scalatest"],
|
|
2953
|
+
["specs2-core", "specs2"],
|
|
2954
|
+
["cats-core", "cats"],
|
|
2955
|
+
["circe-core", "circe"],
|
|
2956
|
+
// Slick gates Opengrep's `scala.slick.` SQL-injection patterns.
|
|
2957
|
+
["slick", "slick"],
|
|
2958
|
+
["slick-hikaricp", "slick"],
|
|
2959
|
+
// jwt-scala publishes one artifact per JSON backend; all gate the same
|
|
2960
|
+
// hardcoded-secret patterns.
|
|
2961
|
+
["jwt-core", "jwt-scala"],
|
|
2962
|
+
["jwt-circe", "jwt-scala"],
|
|
2963
|
+
["jwt-play-json", "jwt-scala"],
|
|
2964
|
+
["jwt-json4s", "jwt-scala"],
|
|
2965
|
+
["jwt-spray-json", "jwt-scala"],
|
|
2966
|
+
["jwt-upickle", "jwt-scala"],
|
|
2967
|
+
["jwt-argonaut", "jwt-scala"],
|
|
2968
|
+
["munit", "munit"],
|
|
2969
|
+
["scalacheck", "scalacheck"],
|
|
2970
|
+
["scala-logging", "scala-logging"],
|
|
2971
|
+
["chimney", "chimney"],
|
|
2972
|
+
// --- WebJars — front-end libraries republished to Maven Central ---
|
|
2973
|
+
// A JVM web app serves its JavaScript from a WebJar instead of from npm, so
|
|
2974
|
+
// `"org.webjars" % "jquery"` is real evidence of jQuery in the repo — and jQuery
|
|
2975
|
+
// gates Opengrep's `javascript.jquery.` rules. This is the one place where an
|
|
2976
|
+
// entity legitimately belongs to two registries, and dropping it was the only
|
|
2977
|
+
// recall loss the before/after sweep over local repos turned up (codacy-website).
|
|
2978
|
+
// The parsers emit bare artifact ids, so the `org.webjars` group is not visible
|
|
2979
|
+
// here; nothing else on Maven Central is named after a front-end library, so
|
|
2980
|
+
// matching the artifact alone is safe.
|
|
2981
|
+
["jquery", "jquery"]
|
|
2982
|
+
];
|
|
2983
|
+
var PREFIXES2 = [
|
|
2984
|
+
// Kotlin — coroutines ships one artifact per target (`-core`, `-android`,
|
|
2985
|
+
// `-test`, `-jdk8`, `-reactive`, …) and any of them means the project uses
|
|
2986
|
+
// coroutines, which is what detekt's `coroutines` rules gate on.
|
|
2987
|
+
["kotlinx-coroutines-", "kotlinx-coroutines"],
|
|
2988
|
+
// JUnit 5 ships one artifact per role (`-engine`, `-params`, `-launcher`), and
|
|
2989
|
+
// Gradle builds declare them separately from `junit-jupiter` itself.
|
|
2990
|
+
["junit-jupiter-", "junit-5"],
|
|
2991
|
+
["junit-platform-", "junit-5"],
|
|
2992
|
+
// A modern Android project declares `androidx.*`, never the deprecated
|
|
2993
|
+
// `com.google.android:android` jar the exact telltale matches.
|
|
2994
|
+
["androidx-", "android-sdk"],
|
|
2995
|
+
["androidx.", "android-sdk"],
|
|
2996
|
+
// Groovy — Grails/GORM artifact families (build.gradle artifactIds).
|
|
2997
|
+
["grails-", "grails"],
|
|
2998
|
+
["gorm-", "grails-gorm"],
|
|
2999
|
+
// Scala — artifact families published one module per feature (sbt artifactIds).
|
|
3000
|
+
// Enumerating members would go stale on every release; the prefixes are
|
|
3001
|
+
// distinctive enough not to collide with other ecosystems.
|
|
3002
|
+
["http4s-", "http4s"],
|
|
3003
|
+
["akka-", "akka"],
|
|
3004
|
+
["pekko-", "pekko"],
|
|
3005
|
+
["zio-", "zio"],
|
|
3006
|
+
// Must precede any broader `cats-` rule: cats-effect is its own project.
|
|
3007
|
+
["cats-effect", "cats-effect"],
|
|
3008
|
+
["doobie-", "doobie"],
|
|
3009
|
+
["fs2-", "fs2"],
|
|
3010
|
+
["circe-", "circe"],
|
|
3011
|
+
["pureconfig", "pureconfig"],
|
|
3012
|
+
["kamon-", "kamon"],
|
|
3013
|
+
["flyway-", "flyway"],
|
|
3014
|
+
["testcontainers-scala", "testcontainers"]
|
|
3015
|
+
];
|
|
3016
|
+
var maven = {
|
|
3017
|
+
id: "maven",
|
|
3018
|
+
manifests: [
|
|
3019
|
+
{ basename: "pom.xml", parser: parsePomXml },
|
|
3020
|
+
{ basename: "build.gradle", parser: parseBuildGradle },
|
|
3021
|
+
{ basename: "build.gradle.kts", parser: parseBuildGradle },
|
|
3022
|
+
{ basename: "libs.versions.toml", parser: parseVersionCatalogToml },
|
|
3023
|
+
{ basename: "build.sbt", parser: parseBuildSbt },
|
|
3024
|
+
{
|
|
3025
|
+
// sbt build definition: `project/Dependencies.scala`, `project/plugins.sbt`
|
|
3026
|
+
// and the meta-build's own `project/project/*`. Matching by directory rather
|
|
3027
|
+
// than basename is what this rule exists for — the Scala file can be called
|
|
3028
|
+
// anything, and `*.scala` on its own is source code, not a manifest.
|
|
3029
|
+
//
|
|
3030
|
+
// Case-sensitive deliberately: sbt's directory is always lowercase `project`,
|
|
3031
|
+
// while `Project` is an ordinary domain-package name, and reading
|
|
3032
|
+
// `…/domain/Project/Project.scala` as a manifest would be wrong.
|
|
3033
|
+
matches: (filePath) => /(^|\/)project\/[^/]+\.(scala|sbt)$/.test(filePath),
|
|
3034
|
+
parser: parseSbtBuildDefinition
|
|
3035
|
+
}
|
|
3036
|
+
],
|
|
3037
|
+
telltales: TELLTALES4,
|
|
3038
|
+
prefixes: PREFIXES2
|
|
3039
|
+
};
|
|
3040
|
+
|
|
3041
|
+
// src/discover/ecosystems/golang.ts
|
|
3042
|
+
function parseGoMod(content) {
|
|
3109
3043
|
const deps = [];
|
|
3110
|
-
const
|
|
3044
|
+
const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
|
|
3111
3045
|
let match;
|
|
3112
|
-
while ((match =
|
|
3113
|
-
|
|
3046
|
+
while ((match = singleRegex.exec(content)) !== null) {
|
|
3047
|
+
if (!match[1].startsWith("(")) {
|
|
3048
|
+
deps.push({ name: match[1], version: match[2] });
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
const blockRegex = /require\s*\(([\s\S]*?)\)/g;
|
|
3052
|
+
while ((match = blockRegex.exec(content)) !== null) {
|
|
3053
|
+
const lines = match[1].split("\n");
|
|
3054
|
+
for (const line of lines) {
|
|
3055
|
+
const m = line.trim().match(/^(\S+)\s+(\S+)/);
|
|
3056
|
+
if (m && !m[1].startsWith("//")) {
|
|
3057
|
+
deps.push({ name: m[1], version: m[2] });
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3114
3060
|
}
|
|
3115
3061
|
return deps;
|
|
3116
3062
|
}
|
|
3063
|
+
var TELLTALES5 = [
|
|
3064
|
+
// --- Frameworks ---
|
|
3065
|
+
["github.com/gin-gonic/gin", "gin"],
|
|
3066
|
+
["github.com/labstack/echo", "echo"],
|
|
3067
|
+
["github.com/gofiber/fiber", "fiber"],
|
|
3068
|
+
["github.com/go-chi/chi", "chi"],
|
|
3069
|
+
["github.com/gorilla/mux", "gorilla-mux"],
|
|
3070
|
+
["github.com/beego/beego", "beego"],
|
|
3071
|
+
// --- Libraries ---
|
|
3072
|
+
["github.com/stretchr/testify", "testify"],
|
|
3073
|
+
["github.com/spf13/cobra", "cobra"],
|
|
3074
|
+
["github.com/spf13/viper", "viper"],
|
|
3075
|
+
["go.uber.org/zap", "zap"],
|
|
3076
|
+
["github.com/sirupsen/logrus", "logrus"],
|
|
3077
|
+
["gorm.io/gorm", "go-gorm"],
|
|
3078
|
+
["github.com/go-kit/kit", "go-kit"],
|
|
3079
|
+
["google.golang.org/protobuf", "protocol-buffers"],
|
|
3080
|
+
["google.golang.org/grpc", "grpc"],
|
|
3081
|
+
["github.com/jackc/pgx", "pgx"],
|
|
3082
|
+
["github.com/redis/go-redis", "go-redis"],
|
|
3083
|
+
// v1 is still widely deployed; both gate Opengrep's AWS Lambda packs.
|
|
3084
|
+
["github.com/aws/aws-sdk-go", "aws-sdk-go"],
|
|
3085
|
+
["github.com/aws/aws-sdk-go-v2", "aws-sdk-go"],
|
|
3086
|
+
["github.com/prometheus/client_golang", "prometheus-client"]
|
|
3087
|
+
];
|
|
3088
|
+
var golang = {
|
|
3089
|
+
id: "golang",
|
|
3090
|
+
manifests: [{ basename: "go.mod", parser: parseGoMod }],
|
|
3091
|
+
telltales: TELLTALES5
|
|
3092
|
+
};
|
|
3093
|
+
|
|
3094
|
+
// src/discover/ecosystems/cargo.ts
|
|
3117
3095
|
function parseCargoToml(content) {
|
|
3118
3096
|
const deps = [];
|
|
3119
3097
|
let inDepsSection = false;
|
|
@@ -3137,6 +3115,32 @@ function parseCargoToml(content) {
|
|
|
3137
3115
|
}
|
|
3138
3116
|
return deps;
|
|
3139
3117
|
}
|
|
3118
|
+
var TELLTALES6 = [
|
|
3119
|
+
// --- Frameworks ---
|
|
3120
|
+
["actix-web", "actix-web"],
|
|
3121
|
+
["axum", "axum"],
|
|
3122
|
+
["rocket", "rocket"],
|
|
3123
|
+
["warp", "warp"],
|
|
3124
|
+
["tauri", "tauri"],
|
|
3125
|
+
// --- Libraries ---
|
|
3126
|
+
["tokio", "tokio"],
|
|
3127
|
+
["serde", "serde"],
|
|
3128
|
+
["clap", "clap"],
|
|
3129
|
+
["reqwest", "reqwest"],
|
|
3130
|
+
["diesel", "diesel"],
|
|
3131
|
+
["sqlx", "sqlx"],
|
|
3132
|
+
["tracing", "tracing"],
|
|
3133
|
+
["anyhow", "anyhow"],
|
|
3134
|
+
["thiserror", "thiserror"],
|
|
3135
|
+
["rayon", "rayon"]
|
|
3136
|
+
];
|
|
3137
|
+
var cargo = {
|
|
3138
|
+
id: "cargo",
|
|
3139
|
+
manifests: [{ basename: "Cargo.toml", parser: parseCargoToml }],
|
|
3140
|
+
telltales: TELLTALES6
|
|
3141
|
+
};
|
|
3142
|
+
|
|
3143
|
+
// src/discover/ecosystems/composer.ts
|
|
3140
3144
|
function parseComposerJson(content) {
|
|
3141
3145
|
try {
|
|
3142
3146
|
const pkg = JSON.parse(content);
|
|
@@ -3155,6 +3159,182 @@ function parseComposerJson(content) {
|
|
|
3155
3159
|
return [];
|
|
3156
3160
|
}
|
|
3157
3161
|
}
|
|
3162
|
+
var TELLTALES7 = [
|
|
3163
|
+
// --- Frameworks ---
|
|
3164
|
+
["laravel/framework", "laravel"],
|
|
3165
|
+
["symfony/framework-bundle", "symfony"],
|
|
3166
|
+
["symfony/symfony", "symfony"],
|
|
3167
|
+
["codeigniter4/framework", "codeigniter"],
|
|
3168
|
+
["slim/slim", "slim"],
|
|
3169
|
+
["cakephp/cakephp", "cakephp"],
|
|
3170
|
+
// WordPress is rarely a real Composer dependency; these are the common
|
|
3171
|
+
// "WordPress as a dependency" packages used by Bedrock/Composer-based sites.
|
|
3172
|
+
["johnpbloch/wordpress-core", "wordpress"],
|
|
3173
|
+
["johnpbloch/wordpress", "wordpress"],
|
|
3174
|
+
["roots/wordpress", "wordpress"],
|
|
3175
|
+
["drupal/core", "drupal"],
|
|
3176
|
+
["drupal/core-recommended", "drupal"],
|
|
3177
|
+
["magento/product-community-edition", "magento"],
|
|
3178
|
+
["magento/product-enterprise-edition", "magento"],
|
|
3179
|
+
["magento/framework", "magento"],
|
|
3180
|
+
// --- Libraries ---
|
|
3181
|
+
["phpunit/phpunit", "phpunit"],
|
|
3182
|
+
["guzzlehttp/guzzle", "guzzle"],
|
|
3183
|
+
["monolog/monolog", "monolog"],
|
|
3184
|
+
["nesbot/carbon", "carbon"],
|
|
3185
|
+
["doctrine/orm", "doctrine-orm"],
|
|
3186
|
+
["doctrine/dbal", "doctrine-dbal"],
|
|
3187
|
+
["league/flysystem", "flysystem"],
|
|
3188
|
+
["php-amqplib/php-amqplib", "php-amqplib"],
|
|
3189
|
+
["predis/predis", "predis"],
|
|
3190
|
+
// --- Tools ---
|
|
3191
|
+
["phpstan/phpstan", "phpstan"],
|
|
3192
|
+
["squizlabs/php_codesniffer", "php-codesniffer"]
|
|
3193
|
+
];
|
|
3194
|
+
var composer = {
|
|
3195
|
+
id: "composer",
|
|
3196
|
+
manifests: [{ basename: "composer.json", parser: parseComposerJson }],
|
|
3197
|
+
telltales: TELLTALES7
|
|
3198
|
+
};
|
|
3199
|
+
|
|
3200
|
+
// src/discover/ecosystems/nuget.ts
|
|
3201
|
+
function parseXmlDependencyElements(content, elementRegex, nameAttr) {
|
|
3202
|
+
const deps = [];
|
|
3203
|
+
const nameRegex = new RegExp(`\\b${nameAttr}\\s*=\\s*["']([^"']+)["']`, "i");
|
|
3204
|
+
const versionRegex = /\bversion\s*=\s*["']([^"']+)["']/i;
|
|
3205
|
+
let match;
|
|
3206
|
+
while ((match = elementRegex.exec(content)) !== null) {
|
|
3207
|
+
const attrs = match[1];
|
|
3208
|
+
const nameMatch = attrs.match(nameRegex);
|
|
3209
|
+
if (!nameMatch) continue;
|
|
3210
|
+
const versionMatch = attrs.match(versionRegex);
|
|
3211
|
+
deps.push({ name: nameMatch[1].trim(), version: versionMatch?.[1]?.trim() });
|
|
3212
|
+
}
|
|
3213
|
+
return deps;
|
|
3214
|
+
}
|
|
3215
|
+
function parseCsproj(content) {
|
|
3216
|
+
return parseXmlDependencyElements(
|
|
3217
|
+
content,
|
|
3218
|
+
/<Package(?:Reference|Version)\s+([^>]+?)\/?>/gi,
|
|
3219
|
+
"Include"
|
|
3220
|
+
);
|
|
3221
|
+
}
|
|
3222
|
+
function parsePackagesConfig(content) {
|
|
3223
|
+
return parseXmlDependencyElements(content, /<package\s+([^>]+?)\/?>/gi, "id");
|
|
3224
|
+
}
|
|
3225
|
+
var TELLTALES8 = [
|
|
3226
|
+
// --- Libraries ---
|
|
3227
|
+
// NuGet package IDs. Microsoft.* families are covered by the prefix rules.
|
|
3228
|
+
["EntityFramework", "entity-framework"],
|
|
3229
|
+
["NUnit", "nunit"],
|
|
3230
|
+
["Moq", "moq"],
|
|
3231
|
+
["NSubstitute", "nsubstitute"],
|
|
3232
|
+
["FluentAssertions", "fluentassertions"],
|
|
3233
|
+
["Shouldly", "shouldly"],
|
|
3234
|
+
["NLog", "nlog"],
|
|
3235
|
+
["log4net", "log4net"],
|
|
3236
|
+
["Newtonsoft.Json", "json-net"],
|
|
3237
|
+
["AutoMapper", "automapper"],
|
|
3238
|
+
["Dapper", "dapper"],
|
|
3239
|
+
["MediatR", "mediatr"]
|
|
3240
|
+
];
|
|
3241
|
+
var PREFIXES3 = [
|
|
3242
|
+
// .NET — Microsoft package families and common test/logging libraries. More
|
|
3243
|
+
// specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
|
|
3244
|
+
["Microsoft.AspNetCore.Components.WebAssembly", "blazor"],
|
|
3245
|
+
["Microsoft.AspNetCore.", "aspnet-core"],
|
|
3246
|
+
["Microsoft.EntityFrameworkCore", "entity-framework"],
|
|
3247
|
+
["Microsoft.Azure.Functions", "azure-functions"],
|
|
3248
|
+
["Microsoft.Azure.WebJobs", "azure-functions"],
|
|
3249
|
+
["xunit", "xunit"],
|
|
3250
|
+
["MSTest.", "mstest"],
|
|
3251
|
+
["Serilog", "serilog"]
|
|
3252
|
+
];
|
|
3253
|
+
var nuget = {
|
|
3254
|
+
id: "nuget",
|
|
3255
|
+
manifests: [
|
|
3256
|
+
{ basename: "packages.config", parser: parsePackagesConfig },
|
|
3257
|
+
{ basename: "Directory.Packages.props", parser: parseCsproj },
|
|
3258
|
+
// Project files carry an arbitrary basename, so they match by suffix. Matched
|
|
3259
|
+
// case-insensitively — .NET project extensions may be upper-cased.
|
|
3260
|
+
{ suffix: ".csproj", parser: parseCsproj },
|
|
3261
|
+
{ suffix: ".vbproj", parser: parseCsproj },
|
|
3262
|
+
{ suffix: ".fsproj", parser: parseCsproj }
|
|
3263
|
+
],
|
|
3264
|
+
telltales: TELLTALES8,
|
|
3265
|
+
prefixes: PREFIXES3
|
|
3266
|
+
};
|
|
3267
|
+
|
|
3268
|
+
// src/discover/ecosystems/pub.ts
|
|
3269
|
+
function parsePubspecYaml(content) {
|
|
3270
|
+
const deps = [];
|
|
3271
|
+
let inSection = false;
|
|
3272
|
+
let childIndent = -1;
|
|
3273
|
+
for (const rawLine of content.split("\n")) {
|
|
3274
|
+
const trimmed = rawLine.trim();
|
|
3275
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
3276
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3277
|
+
if (indent === 0) {
|
|
3278
|
+
inSection = /^(dependencies|dev_dependencies|dependency_overrides):/.test(trimmed);
|
|
3279
|
+
childIndent = -1;
|
|
3280
|
+
continue;
|
|
3281
|
+
}
|
|
3282
|
+
if (!inSection) continue;
|
|
3283
|
+
if (childIndent === -1) childIndent = indent;
|
|
3284
|
+
if (indent !== childIndent) continue;
|
|
3285
|
+
const m = trimmed.match(/^([a-zA-Z0-9_.]+)\s*:(?:\s*["']?\^?([0-9][^"'#\s]*))?/);
|
|
3286
|
+
if (m) deps.push({ name: m[1], version: m[2] });
|
|
3287
|
+
}
|
|
3288
|
+
return deps;
|
|
3289
|
+
}
|
|
3290
|
+
var TELLTALES9 = [
|
|
3291
|
+
// --- Frameworks ---
|
|
3292
|
+
// `flutter` and `angular` appear as plain keys in pubspec.yaml. `angular` here is
|
|
3293
|
+
// AngularDart — a different framework from npm's `angular` (AngularJS 1.x) and
|
|
3294
|
+
// from modern Angular — hence the distinct `angular-dart` entity.
|
|
3295
|
+
["flutter", "flutter"],
|
|
3296
|
+
["flutter_test", "flutter"],
|
|
3297
|
+
["angular", "angular-dart"],
|
|
3298
|
+
// --- Libraries ---
|
|
3299
|
+
// Kept to Dart-specific names with negligible cross-ecosystem collision risk
|
|
3300
|
+
// (generic names like "get"/"provider" are intentionally omitted).
|
|
3301
|
+
["riverpod", "riverpod"],
|
|
3302
|
+
["flutter_bloc", "bloc"],
|
|
3303
|
+
["dio", "dio"]
|
|
3304
|
+
];
|
|
3305
|
+
var pub = {
|
|
3306
|
+
id: "pub",
|
|
3307
|
+
manifests: [{ basename: "pubspec.yaml", parser: parsePubspecYaml }],
|
|
3308
|
+
telltales: TELLTALES9
|
|
3309
|
+
};
|
|
3310
|
+
|
|
3311
|
+
// src/discover/ecosystems/hex.ts
|
|
3312
|
+
function parseMixExs(content) {
|
|
3313
|
+
const deps = [];
|
|
3314
|
+
const depRegex = /\{:([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*"([^"]*)"/g;
|
|
3315
|
+
let match;
|
|
3316
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
3317
|
+
deps.push({ name: match[1], version: match[2] || void 0 });
|
|
3318
|
+
}
|
|
3319
|
+
return deps;
|
|
3320
|
+
}
|
|
3321
|
+
var TELLTALES10 = [
|
|
3322
|
+
// --- Frameworks ---
|
|
3323
|
+
["phoenix", "phoenix"],
|
|
3324
|
+
["plug", "plug"],
|
|
3325
|
+
["plug_cowboy", "plug"],
|
|
3326
|
+
// --- Libraries ---
|
|
3327
|
+
["ecto", "ecto"],
|
|
3328
|
+
["ecto_sql", "ecto"],
|
|
3329
|
+
["phoenix_ecto", "ecto"]
|
|
3330
|
+
];
|
|
3331
|
+
var hex = {
|
|
3332
|
+
id: "hex",
|
|
3333
|
+
manifests: [{ basename: "mix.exs", parser: parseMixExs }],
|
|
3334
|
+
telltales: TELLTALES10
|
|
3335
|
+
};
|
|
3336
|
+
|
|
3337
|
+
// src/discover/ecosystems/swift.ts
|
|
3158
3338
|
function parsePodfile(content) {
|
|
3159
3339
|
const deps = [];
|
|
3160
3340
|
const podRegex = /^\s*pod\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
|
|
@@ -3162,127 +3342,503 @@ function parsePodfile(content) {
|
|
|
3162
3342
|
while ((match = podRegex.exec(content)) !== null) {
|
|
3163
3343
|
deps.push({ name: match[1], version: match[2] });
|
|
3164
3344
|
}
|
|
3165
|
-
return deps;
|
|
3345
|
+
return deps;
|
|
3346
|
+
}
|
|
3347
|
+
function parsePackageSwift(content) {
|
|
3348
|
+
const deps = [];
|
|
3349
|
+
const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
|
|
3350
|
+
let match;
|
|
3351
|
+
while ((match = pkgRegex.exec(content)) !== null) {
|
|
3352
|
+
deps.push({ name: match[1] });
|
|
3353
|
+
}
|
|
3354
|
+
return deps;
|
|
3355
|
+
}
|
|
3356
|
+
var TELLTALES11 = [
|
|
3357
|
+
// --- Frameworks ---
|
|
3358
|
+
["vapor", "vapor"],
|
|
3359
|
+
// --- Libraries ---
|
|
3360
|
+
["Alamofire", "alamofire"],
|
|
3361
|
+
["SnapKit", "snapkit"],
|
|
3362
|
+
["Kingfisher", "kingfisher"],
|
|
3363
|
+
["Moya", "moya"],
|
|
3364
|
+
["RxSwift", "rxswift"],
|
|
3365
|
+
["RealmSwift", "realm"],
|
|
3366
|
+
// Quick and Nimble ship from separate repos and are declared independently —
|
|
3367
|
+
// Nimble is usable on its own with XCTest — so they are two entities, gating
|
|
3368
|
+
// SwiftLint's `quick_discouraged_*` and `prefer_nimble` rules respectively.
|
|
3369
|
+
// Ids are matched exactly and case-sensitively: `parsePackageSwift` yields the
|
|
3370
|
+
// GitHub repo name and `parsePodfile` the pod name, and both are `Quick`/`Nimble`.
|
|
3371
|
+
["Quick", "quick"],
|
|
3372
|
+
["Nimble", "nimble"]
|
|
3373
|
+
];
|
|
3374
|
+
var swift = {
|
|
3375
|
+
id: "swift",
|
|
3376
|
+
manifests: [
|
|
3377
|
+
{ basename: "Package.swift", parser: parsePackageSwift },
|
|
3378
|
+
{ basename: "Podfile", parser: parsePodfile }
|
|
3379
|
+
],
|
|
3380
|
+
telltales: TELLTALES11
|
|
3381
|
+
};
|
|
3382
|
+
|
|
3383
|
+
// src/discover/ecosystems/shards.ts
|
|
3384
|
+
function parseShardYml(content) {
|
|
3385
|
+
const deps = [];
|
|
3386
|
+
let inSection = false;
|
|
3387
|
+
let depIndent = -1;
|
|
3388
|
+
let current;
|
|
3389
|
+
const flush = () => {
|
|
3390
|
+
if (current) deps.push(current);
|
|
3391
|
+
current = void 0;
|
|
3392
|
+
};
|
|
3393
|
+
for (const rawLine of content.split("\n")) {
|
|
3394
|
+
const trimmed = rawLine.trim();
|
|
3395
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
3396
|
+
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3397
|
+
if (indent === 0) {
|
|
3398
|
+
flush();
|
|
3399
|
+
inSection = /^(dependencies|development_dependencies):/.test(trimmed);
|
|
3400
|
+
depIndent = -1;
|
|
3401
|
+
continue;
|
|
3402
|
+
}
|
|
3403
|
+
if (!inSection) continue;
|
|
3404
|
+
if (depIndent === -1) depIndent = indent;
|
|
3405
|
+
if (indent === depIndent) {
|
|
3406
|
+
flush();
|
|
3407
|
+
const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
|
|
3408
|
+
current = m ? { name: m[1] } : void 0;
|
|
3409
|
+
continue;
|
|
3410
|
+
}
|
|
3411
|
+
if (indent > depIndent && current) {
|
|
3412
|
+
const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
|
|
3413
|
+
if (v) current.version = v[1];
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
flush();
|
|
3417
|
+
return deps;
|
|
3418
|
+
}
|
|
3419
|
+
var TELLTALES12 = [
|
|
3420
|
+
// --- Frameworks ---
|
|
3421
|
+
["amber", "amber"],
|
|
3422
|
+
["lucky", "lucky"],
|
|
3423
|
+
["kemal", "kemal"]
|
|
3424
|
+
];
|
|
3425
|
+
var shards = {
|
|
3426
|
+
id: "shards",
|
|
3427
|
+
manifests: [{ basename: "shard.yml", parser: parseShardYml }],
|
|
3428
|
+
telltales: TELLTALES12
|
|
3429
|
+
};
|
|
3430
|
+
|
|
3431
|
+
// src/discover/ecosystems/index.ts
|
|
3432
|
+
var ECOSYSTEMS = [
|
|
3433
|
+
npm,
|
|
3434
|
+
pypi,
|
|
3435
|
+
gem,
|
|
3436
|
+
maven,
|
|
3437
|
+
golang,
|
|
3438
|
+
cargo,
|
|
3439
|
+
composer,
|
|
3440
|
+
nuget,
|
|
3441
|
+
pub,
|
|
3442
|
+
hex,
|
|
3443
|
+
swift,
|
|
3444
|
+
shards
|
|
3445
|
+
];
|
|
3446
|
+
var BY_BASENAME = /* @__PURE__ */ new Map();
|
|
3447
|
+
var BY_SUFFIX = [];
|
|
3448
|
+
var BY_PATH = [];
|
|
3449
|
+
for (const ecosystem of ECOSYSTEMS) {
|
|
3450
|
+
for (const rule of ecosystem.manifests) {
|
|
3451
|
+
const resolved = { ecosystem: ecosystem.id, parser: rule.parser };
|
|
3452
|
+
if ("basename" in rule) BY_BASENAME.set(rule.basename, resolved);
|
|
3453
|
+
else if ("suffix" in rule) BY_SUFFIX.push({ suffix: rule.suffix, resolved });
|
|
3454
|
+
else BY_PATH.push({ matches: rule.matches, resolved });
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
function getEcosystem(id) {
|
|
3458
|
+
const found = ECOSYSTEMS.find((e) => e.id === id);
|
|
3459
|
+
if (!found) throw new Error(`Unknown dependency ecosystem: ${id}`);
|
|
3460
|
+
return found;
|
|
3461
|
+
}
|
|
3462
|
+
function resolveManifest(filePath) {
|
|
3463
|
+
const basename = filePath.split("/").pop() ?? filePath;
|
|
3464
|
+
const byName = BY_BASENAME.get(basename);
|
|
3465
|
+
if (byName) return byName;
|
|
3466
|
+
const lowerBasename = basename.toLowerCase();
|
|
3467
|
+
for (const { suffix, resolved } of BY_SUFFIX) {
|
|
3468
|
+
if (lowerBasename.endsWith(suffix)) return resolved;
|
|
3469
|
+
}
|
|
3470
|
+
for (const { matches, resolved } of BY_PATH) {
|
|
3471
|
+
if (matches(filePath, basename)) return resolved;
|
|
3472
|
+
}
|
|
3473
|
+
return void 0;
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
// src/discover/signals.ts
|
|
3477
|
+
var import_tooling5 = require("@codacy/tooling");
|
|
3478
|
+
function pathBasename(filePath) {
|
|
3479
|
+
return filePath.split("/").pop() ?? filePath;
|
|
3480
|
+
}
|
|
3481
|
+
var FILE_SIGNAL_RULES = [
|
|
3482
|
+
{
|
|
3483
|
+
name: ".flowconfig",
|
|
3484
|
+
matches: (f) => pathBasename(f) === ".flowconfig",
|
|
3485
|
+
entity: "flow"
|
|
3486
|
+
},
|
|
3487
|
+
{
|
|
3488
|
+
name: "cdk.json",
|
|
3489
|
+
matches: (f) => pathBasename(f) === "cdk.json",
|
|
3490
|
+
entity: "aws-cdk"
|
|
3491
|
+
},
|
|
3492
|
+
{
|
|
3493
|
+
name: ".meteor/release",
|
|
3494
|
+
matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
|
|
3495
|
+
entity: "meteor"
|
|
3496
|
+
},
|
|
3497
|
+
{
|
|
3498
|
+
name: "sfdx-project.json",
|
|
3499
|
+
matches: (f) => pathBasename(f) === "sfdx-project.json",
|
|
3500
|
+
entity: "salesforce"
|
|
3501
|
+
},
|
|
3502
|
+
{
|
|
3503
|
+
name: "lwc.config.json",
|
|
3504
|
+
matches: (f) => pathBasename(f) === "lwc.config.json",
|
|
3505
|
+
entity: "lightning-web-components"
|
|
3506
|
+
},
|
|
3507
|
+
{
|
|
3508
|
+
name: "suitecloud.config.js",
|
|
3509
|
+
matches: (f) => pathBasename(f) === "suitecloud.config.js",
|
|
3510
|
+
entity: "suitescript"
|
|
3511
|
+
},
|
|
3512
|
+
{
|
|
3513
|
+
name: ".github/workflows",
|
|
3514
|
+
matches: (f) => f.startsWith(".github/workflows/"),
|
|
3515
|
+
entity: "github-actions"
|
|
3516
|
+
},
|
|
3517
|
+
{
|
|
3518
|
+
name: ".circleci",
|
|
3519
|
+
matches: (f) => f.startsWith(".circleci/"),
|
|
3520
|
+
entity: "circleci"
|
|
3521
|
+
},
|
|
3522
|
+
{
|
|
3523
|
+
name: ".gitlab-ci.yml",
|
|
3524
|
+
matches: (f) => f === ".gitlab-ci.yml" || f.startsWith(".gitlab/"),
|
|
3525
|
+
entity: "gitlab-ci"
|
|
3526
|
+
},
|
|
3527
|
+
{
|
|
3528
|
+
name: "bitbucket-pipelines.yml",
|
|
3529
|
+
matches: (f) => f === "bitbucket-pipelines.yml",
|
|
3530
|
+
entity: "bitbucket-pipelines"
|
|
3531
|
+
},
|
|
3532
|
+
{
|
|
3533
|
+
name: "azure-pipelines.yml",
|
|
3534
|
+
matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
|
|
3535
|
+
entity: "azure-pipelines"
|
|
3536
|
+
},
|
|
3537
|
+
{
|
|
3538
|
+
// Jenkins pipelines are Groovy scripts with no manifest at all — the
|
|
3539
|
+
// Jenkinsfile *is* the declaration. Gates CodeNarc's `jenkins`/
|
|
3540
|
+
// `jenkinspipeline` tagged patterns.
|
|
3541
|
+
name: "Jenkinsfile",
|
|
3542
|
+
matches: (f) => {
|
|
3543
|
+
const base = pathBasename(f);
|
|
3544
|
+
return base === "Jenkinsfile" || base.startsWith("Jenkinsfile.");
|
|
3545
|
+
},
|
|
3546
|
+
entity: "jenkins"
|
|
3547
|
+
},
|
|
3548
|
+
{
|
|
3549
|
+
// A `sorbet/` directory (typically `sorbet/config` + RBI files) marks a
|
|
3550
|
+
// Sorbet-typed Ruby project even when the gem is pulled in transitively.
|
|
3551
|
+
name: "sorbet/config",
|
|
3552
|
+
matches: (f) => f === "sorbet/config" || f.endsWith("/sorbet/config"),
|
|
3553
|
+
entity: "sorbet"
|
|
3554
|
+
},
|
|
3555
|
+
{
|
|
3556
|
+
name: "Rakefile",
|
|
3557
|
+
matches: (f) => {
|
|
3558
|
+
const base = pathBasename(f);
|
|
3559
|
+
return base === "Rakefile" || base === "rakefile" || base.endsWith(".rake");
|
|
3560
|
+
},
|
|
3561
|
+
entity: "rake"
|
|
3562
|
+
},
|
|
3563
|
+
{
|
|
3564
|
+
// XCTest ships with Xcode, so no manifest ever declares it and a file signal
|
|
3565
|
+
// is the only producer available. `FooTests.swift` / `FooTest.swift` is the
|
|
3566
|
+
// naming Xcode's templates and SwiftPM's `Tests/` layout both generate, and it
|
|
3567
|
+
// is what SwiftLint's own test rules key on. Matching the file name rather than
|
|
3568
|
+
// a `Tests/` directory also covers CocoaPods/Xcode projects, which have no
|
|
3569
|
+
// fixed directory convention.
|
|
3570
|
+
name: "*Tests.swift",
|
|
3571
|
+
matches: (f) => /Tests?\.swift$/.test(pathBasename(f)),
|
|
3572
|
+
entity: "xctest"
|
|
3573
|
+
},
|
|
3574
|
+
// ── Infrastructure-as-Code ─────────────────────────────────────────────────
|
|
3575
|
+
//
|
|
3576
|
+
// These gate Checkov, whose ~1,300 cloud-security checks are otherwise
|
|
3577
|
+
// universal: they are all default-on and the only other filter is the language
|
|
3578
|
+
// one, which passes on any repo containing a `.json` or `.yaml` file. Terraform
|
|
3579
|
+
// needs no rule here — `.tf` is the `Terraform` language, so `lang:Terraform`
|
|
3580
|
+
// already means "this repo has Terraform". What the language axis cannot
|
|
3581
|
+
// express is which IaC *framework* a YAML/JSON file belongs to, since
|
|
3582
|
+
// Kubernetes manifests, Helm charts, Ansible playbooks, CloudFormation
|
|
3583
|
+
// templates and ARM templates are all YAML or JSON.
|
|
3584
|
+
//
|
|
3585
|
+
// Bias these rules **permissive**, the opposite of the rules above. A Checkov
|
|
3586
|
+
// check declares the resource types it applies to, so a check enabled in a repo
|
|
3587
|
+
// that has none of them is inert — it produces no findings and costs only a line
|
|
3588
|
+
// of config. A false negative is what's expensive: it silently drops hundreds of
|
|
3589
|
+
// checks from a repo that needed them. Where a signal is ambiguous, prefer
|
|
3590
|
+
// matching.
|
|
3591
|
+
{
|
|
3592
|
+
// A Helm chart's templates render to Kubernetes manifests, so a chart is
|
|
3593
|
+
// evidence of Kubernetes as much as of Helm. `Chart.yaml` is the chart
|
|
3594
|
+
// manifest and carries no other meaning at a chart root.
|
|
3595
|
+
name: "Chart.yaml",
|
|
3596
|
+
matches: (f) => {
|
|
3597
|
+
const base = pathBasename(f);
|
|
3598
|
+
return base === "Chart.yaml" || base === "Chart.yml";
|
|
3599
|
+
},
|
|
3600
|
+
entity: "helm"
|
|
3601
|
+
},
|
|
3602
|
+
{
|
|
3603
|
+
// Kustomize is the one Kubernetes layout with a distinctive filename. Bare
|
|
3604
|
+
// manifests declare themselves only in their content (`apiVersion:`/`kind:`),
|
|
3605
|
+
// which a path-only rule cannot see. `kustomization.yaml` is exact; the
|
|
3606
|
+
// directory rule covers the far more common layout — loose manifests under
|
|
3607
|
+
// `k8s/`, `kubernetes/` or `manifests/`, applied with `kubectl apply -f`.
|
|
3608
|
+
//
|
|
3609
|
+
// Gating on `kustomization.yaml` alone dropped 14 default-on Opengrep rules
|
|
3610
|
+
// (privileged-container, secrets-in-config-file, skip-tls-verify-*, …) from
|
|
3611
|
+
// exactly that layout. Per the bias above, a directory name matched wrongly
|
|
3612
|
+
// only enables inert checks, while missing it loses real coverage.
|
|
3613
|
+
name: "k8s manifests",
|
|
3614
|
+
matches: (f) => {
|
|
3615
|
+
const base = pathBasename(f);
|
|
3616
|
+
if (base === "kustomization.yaml" || base === "kustomization.yml") return true;
|
|
3617
|
+
return /(^|\/)(k8s|kubernetes|manifests)\/.+\.ya?ml$/.test(f);
|
|
3618
|
+
},
|
|
3619
|
+
entity: "kubernetes"
|
|
3620
|
+
},
|
|
3621
|
+
{
|
|
3622
|
+
// `ansible.cfg` is unambiguous; the `roles/<name>/tasks/main.yml` layout is
|
|
3623
|
+
// Ansible's own role convention and is the only marker in a playbook repo
|
|
3624
|
+
// that ships no config file.
|
|
3625
|
+
name: "ansible.cfg",
|
|
3626
|
+
matches: (f) => pathBasename(f) === "ansible.cfg" || /(^|\/)roles\/[^/]+\/tasks\/main\.ya?ml$/.test(f),
|
|
3627
|
+
entity: "ansible"
|
|
3628
|
+
},
|
|
3629
|
+
{
|
|
3630
|
+
name: "serverless.yml",
|
|
3631
|
+
matches: (f) => /(^|\/)serverless\.(ya?ml|json|ts|js)$/.test(f),
|
|
3632
|
+
entity: "serverless-framework"
|
|
3633
|
+
},
|
|
3634
|
+
{
|
|
3635
|
+
// Bicep compiles to ARM, so `.bicep` is Azure IaC. Plain ARM templates are
|
|
3636
|
+
// JSON identified by their `$schema`, which needs a content probe.
|
|
3637
|
+
name: "*.bicep",
|
|
3638
|
+
matches: (f) => {
|
|
3639
|
+
const base = pathBasename(f);
|
|
3640
|
+
return base.endsWith(".bicep") || base.endsWith(".bicepparam");
|
|
3641
|
+
},
|
|
3642
|
+
entity: "bicep"
|
|
3643
|
+
},
|
|
3644
|
+
{
|
|
3645
|
+
// A CloudFormation template is identified by `AWSTemplateFormatVersion` in its
|
|
3646
|
+
// body, not by its name, so this covers only the conventional shells: the SAM
|
|
3647
|
+
// CLI's config file, SAM's root `template.yaml`, and a `cloudformation/`
|
|
3648
|
+
// directory. Deliberately loose per the note above — over-matching enables AWS
|
|
3649
|
+
// checks that then find nothing, under-matching drops 456 of them.
|
|
3650
|
+
name: "cloudformation template",
|
|
3651
|
+
matches: (f) => {
|
|
3652
|
+
const base = pathBasename(f);
|
|
3653
|
+
if (base === "samconfig.toml" || base === "samconfig.yaml" || base === "samconfig.yml") {
|
|
3654
|
+
return true;
|
|
3655
|
+
}
|
|
3656
|
+
if (f === "template.yaml" || f === "template.yml") return true;
|
|
3657
|
+
return /(^|\/)cloudformation\//.test(f);
|
|
3658
|
+
},
|
|
3659
|
+
entity: "cloudformation"
|
|
3660
|
+
},
|
|
3661
|
+
{
|
|
3662
|
+
// Play's routes file — `conf/routes`, plus the `conf/*.routes` sub-files a
|
|
3663
|
+
// multi-module app includes from it (Codacy's own website uses
|
|
3664
|
+
// `app/conf/website.routes`, `modules/legacy-api/conf/api.routes`). Nothing
|
|
3665
|
+
// but Play uses this layout, and it is version-independent, unlike the
|
|
3666
|
+
// artifact names. Note `conf/application.conf` is NOT a signal: plain
|
|
3667
|
+
// Typesafe Config uses the same filename without any Play involvement.
|
|
3668
|
+
name: "conf/routes",
|
|
3669
|
+
matches: (f) => /(^|\/)conf\/([^/]+\.)?routes$/.test(f),
|
|
3670
|
+
entity: "play-framework"
|
|
3671
|
+
}
|
|
3672
|
+
];
|
|
3673
|
+
function detectFileSignals(files) {
|
|
3674
|
+
const result = [];
|
|
3675
|
+
for (const rule of FILE_SIGNAL_RULES) {
|
|
3676
|
+
const hit = files.find((f) => rule.matches(f));
|
|
3677
|
+
if (!hit) continue;
|
|
3678
|
+
const entity = import_tooling5.DEPENDENCY_ENTITIES[rule.entity];
|
|
3679
|
+
result.push({
|
|
3680
|
+
name: rule.name,
|
|
3681
|
+
key: entity.key,
|
|
3682
|
+
displayName: entity.displayName,
|
|
3683
|
+
kind: entity.kind,
|
|
3684
|
+
source: "file",
|
|
3685
|
+
manifestFile: hit
|
|
3686
|
+
});
|
|
3687
|
+
}
|
|
3688
|
+
return result;
|
|
3166
3689
|
}
|
|
3167
|
-
function
|
|
3168
|
-
|
|
3169
|
-
const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
|
|
3170
|
-
let match;
|
|
3171
|
-
while ((match = pkgRegex.exec(content)) !== null) {
|
|
3172
|
-
deps.push({ name: match[1] });
|
|
3173
|
-
}
|
|
3174
|
-
return deps;
|
|
3690
|
+
function isEslintPluginName(name) {
|
|
3691
|
+
return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
|
|
3175
3692
|
}
|
|
3176
|
-
function
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
3183
|
-
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3184
|
-
if (indent === 0) {
|
|
3185
|
-
inSection = /^(dependencies|dev_dependencies|dependency_overrides):/.test(trimmed);
|
|
3186
|
-
childIndent = -1;
|
|
3187
|
-
continue;
|
|
3188
|
-
}
|
|
3189
|
-
if (!inSection) continue;
|
|
3190
|
-
if (childIndent === -1) childIndent = indent;
|
|
3191
|
-
if (indent !== childIndent) continue;
|
|
3192
|
-
const m = trimmed.match(/^([a-zA-Z0-9_.]+)\s*:(?:\s*["']?\^?([0-9][^"'#\s]*))?/);
|
|
3193
|
-
if (m) deps.push({ name: m[1], version: m[2] });
|
|
3693
|
+
function detectPackageJsonSignals(content) {
|
|
3694
|
+
let pkg;
|
|
3695
|
+
try {
|
|
3696
|
+
pkg = JSON.parse(content);
|
|
3697
|
+
} catch {
|
|
3698
|
+
return [];
|
|
3194
3699
|
}
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3210
|
-
if (indent === 0) {
|
|
3211
|
-
flush();
|
|
3212
|
-
inSection = /^(dependencies|development_dependencies):/.test(trimmed);
|
|
3213
|
-
depIndent = -1;
|
|
3214
|
-
continue;
|
|
3215
|
-
}
|
|
3216
|
-
if (!inSection) continue;
|
|
3217
|
-
if (depIndent === -1) depIndent = indent;
|
|
3218
|
-
if (indent === depIndent) {
|
|
3219
|
-
flush();
|
|
3220
|
-
const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
|
|
3221
|
-
current = m ? { name: m[1] } : void 0;
|
|
3222
|
-
continue;
|
|
3223
|
-
}
|
|
3224
|
-
if (indent > depIndent && current) {
|
|
3225
|
-
const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
|
|
3226
|
-
if (v) current.version = v[1];
|
|
3227
|
-
}
|
|
3700
|
+
const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
|
|
3701
|
+
if (name && isEslintPluginName(name)) {
|
|
3702
|
+
const entity = import_tooling5.DEPENDENCY_ENTITIES["eslint-plugin"];
|
|
3703
|
+
return [
|
|
3704
|
+
{
|
|
3705
|
+
name,
|
|
3706
|
+
key: entity.key,
|
|
3707
|
+
displayName: entity.displayName,
|
|
3708
|
+
kind: entity.kind,
|
|
3709
|
+
source: "manifest",
|
|
3710
|
+
manifestFile: "package.json",
|
|
3711
|
+
ecosystem: "npm"
|
|
3712
|
+
}
|
|
3713
|
+
];
|
|
3228
3714
|
}
|
|
3229
|
-
|
|
3230
|
-
return deps;
|
|
3715
|
+
return [];
|
|
3231
3716
|
}
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
let
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3717
|
+
|
|
3718
|
+
// src/discover/classify.ts
|
|
3719
|
+
var TELLTALE_INDEX = /* @__PURE__ */ new Map();
|
|
3720
|
+
function telltaleIndex(ecosystem) {
|
|
3721
|
+
let index = TELLTALE_INDEX.get(ecosystem);
|
|
3722
|
+
if (!index) {
|
|
3723
|
+
index = new Map(getEcosystem(ecosystem).telltales);
|
|
3724
|
+
TELLTALE_INDEX.set(ecosystem, index);
|
|
3725
|
+
}
|
|
3726
|
+
return index;
|
|
3727
|
+
}
|
|
3728
|
+
function classifyDependency(name, ecosystem) {
|
|
3729
|
+
const exact = telltaleIndex(ecosystem).get(name);
|
|
3730
|
+
if (exact) return import_tooling6.DEPENDENCY_ENTITIES[exact];
|
|
3731
|
+
for (const [prefix, entity] of getEcosystem(ecosystem).prefixes ?? []) {
|
|
3732
|
+
if (name.startsWith(prefix)) return import_tooling6.DEPENDENCY_ENTITIES[entity];
|
|
3243
3733
|
}
|
|
3244
|
-
return
|
|
3245
|
-
}
|
|
3246
|
-
function parseCsproj(content) {
|
|
3247
|
-
return parseXmlDependencyElements(
|
|
3248
|
-
content,
|
|
3249
|
-
/<Package(?:Reference|Version)\s+([^>]+?)\/?>/gi,
|
|
3250
|
-
"Include"
|
|
3251
|
-
);
|
|
3734
|
+
return void 0;
|
|
3252
3735
|
}
|
|
3253
|
-
function
|
|
3254
|
-
return
|
|
3736
|
+
function getKnownDisplayNames() {
|
|
3737
|
+
return new Set(Object.values(import_tooling6.DEPENDENCY_ENTITIES).map((entity) => entity.displayName));
|
|
3255
3738
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3739
|
+
|
|
3740
|
+
// src/discover/notable-files.ts
|
|
3741
|
+
var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
|
|
3742
|
+
"Dockerfile",
|
|
3743
|
+
"docker-compose.yml",
|
|
3744
|
+
"docker-compose.yaml",
|
|
3745
|
+
"compose.yml",
|
|
3746
|
+
"compose.yaml",
|
|
3747
|
+
"Makefile",
|
|
3748
|
+
"Justfile",
|
|
3749
|
+
"pom.xml",
|
|
3750
|
+
"build.gradle",
|
|
3751
|
+
"build.gradle.kts",
|
|
3752
|
+
"build.sbt",
|
|
3753
|
+
"go.mod",
|
|
3754
|
+
"Cargo.toml",
|
|
3755
|
+
"Gemfile",
|
|
3756
|
+
"Podfile",
|
|
3757
|
+
"Package.swift",
|
|
3758
|
+
"Jenkinsfile",
|
|
3759
|
+
"Vagrantfile",
|
|
3760
|
+
"Procfile",
|
|
3761
|
+
"pubspec.yaml",
|
|
3762
|
+
"mix.exs",
|
|
3763
|
+
"shard.yml",
|
|
3764
|
+
"packages.config",
|
|
3765
|
+
"Directory.Packages.props"
|
|
3276
3766
|
]);
|
|
3277
|
-
var
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3767
|
+
var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
|
|
3768
|
+
".dockerignore",
|
|
3769
|
+
"Taskfile.yml",
|
|
3770
|
+
"Rakefile",
|
|
3771
|
+
"Gruntfile.js",
|
|
3772
|
+
"Gulpfile.js",
|
|
3773
|
+
"settings.gradle",
|
|
3774
|
+
"settings.gradle.kts",
|
|
3775
|
+
"go.sum",
|
|
3776
|
+
"Cargo.lock",
|
|
3777
|
+
"CLAUDE.md",
|
|
3778
|
+
"AGENTS.md",
|
|
3779
|
+
"agents.md",
|
|
3780
|
+
".cursorrules",
|
|
3781
|
+
"copilot-instructions.md",
|
|
3782
|
+
".travis.yml",
|
|
3783
|
+
".gitlab-ci.yml",
|
|
3784
|
+
"azure-pipelines.yml",
|
|
3785
|
+
"bitbucket-pipelines.yml",
|
|
3786
|
+
"appveyor.yml",
|
|
3787
|
+
"package.json",
|
|
3788
|
+
"requirements.txt",
|
|
3789
|
+
"pyproject.toml",
|
|
3790
|
+
"Pipfile",
|
|
3791
|
+
"composer.json",
|
|
3792
|
+
"tsconfig.json",
|
|
3793
|
+
"jsconfig.json",
|
|
3794
|
+
"serverless.yml",
|
|
3795
|
+
"serverless.yaml",
|
|
3796
|
+
"cdk.json",
|
|
3797
|
+
"pulumi.yaml",
|
|
3798
|
+
"README.md",
|
|
3799
|
+
"CHANGELOG.md",
|
|
3800
|
+
"CONTRIBUTING.md",
|
|
3801
|
+
"LICENSE",
|
|
3802
|
+
"renovate.json",
|
|
3803
|
+
".nvmrc",
|
|
3804
|
+
".node-version",
|
|
3805
|
+
".python-version",
|
|
3806
|
+
".ruby-version",
|
|
3807
|
+
".tool-versions"
|
|
3281
3808
|
]);
|
|
3809
|
+
var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
|
|
3810
|
+
var CONFIG_HEURISTIC_PATTERNS = [
|
|
3811
|
+
/^\..*rc$/,
|
|
3812
|
+
// .eslintrc, .prettierrc, etc.
|
|
3813
|
+
/^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
|
|
3814
|
+
// .eslintrc.json, etc.
|
|
3815
|
+
/^.*\.config\.(js|ts|mjs|cjs)$/,
|
|
3816
|
+
// vite.config.ts, jest.config.js, etc.
|
|
3817
|
+
/^\.env(\..+)?$/
|
|
3818
|
+
// .env, .env.local, .env.production
|
|
3819
|
+
];
|
|
3820
|
+
function isNotableFile(filePath) {
|
|
3821
|
+
const basename = filePath.split("/").pop() ?? filePath;
|
|
3822
|
+
const isRoot = !filePath.includes("/");
|
|
3823
|
+
if (NOTABLE_ANY_DEPTH.has(basename)) return true;
|
|
3824
|
+
if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
|
|
3825
|
+
if (/\.(csproj|vbproj|fsproj|sln)$/i.test(basename)) return true;
|
|
3826
|
+
for (const prefix of NOTABLE_PATH_PREFIXES) {
|
|
3827
|
+
if (filePath.startsWith(prefix)) return true;
|
|
3828
|
+
}
|
|
3829
|
+
if (!filePath.includes("/")) {
|
|
3830
|
+
for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
|
|
3831
|
+
if (pattern.test(basename)) return true;
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
return false;
|
|
3835
|
+
}
|
|
3282
3836
|
|
|
3283
|
-
// src/discover.ts
|
|
3837
|
+
// src/discover/index.ts
|
|
3284
3838
|
async function discover(repositoryRoot, options) {
|
|
3285
|
-
const
|
|
3839
|
+
const unfiltered = listRepoFiles(repositoryRoot);
|
|
3840
|
+
const files = applyExclusions(unfiltered, [...options?.exclude ?? []], []);
|
|
3841
|
+
const excludedFiles = unfiltered.length - files.length;
|
|
3286
3842
|
const filePatterns = extractFilePatterns(files);
|
|
3287
3843
|
const notableFiles = findNotableFiles(files);
|
|
3288
3844
|
const languages = await detectLanguages(repositoryRoot, files);
|
|
@@ -3305,7 +3861,10 @@ async function discover(repositoryRoot, options) {
|
|
|
3305
3861
|
metadata: {
|
|
3306
3862
|
repositoryRoot: import_path6.default.resolve(repositoryRoot),
|
|
3307
3863
|
totalFiles: files.length,
|
|
3308
|
-
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3864
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3865
|
+
// Surfaced so a scan narrowed by excludes is never mistaken for a repo
|
|
3866
|
+
// that genuinely lacks those languages.
|
|
3867
|
+
...excludedFiles > 0 ? { excludedFiles } : {}
|
|
3309
3868
|
}
|
|
3310
3869
|
};
|
|
3311
3870
|
}
|
|
@@ -3328,43 +3887,33 @@ function findNotableFiles(files) {
|
|
|
3328
3887
|
}
|
|
3329
3888
|
return Array.from(notable).sort();
|
|
3330
3889
|
}
|
|
3331
|
-
function resolveManifestParser(filePath) {
|
|
3332
|
-
const basename = filePath.split("/").pop() ?? filePath;
|
|
3333
|
-
const byName = MANIFEST_PARSERS.get(basename);
|
|
3334
|
-
if (byName) return byName;
|
|
3335
|
-
const lowerBasename = basename.toLowerCase();
|
|
3336
|
-
for (const [suffix, parser] of MANIFEST_SUFFIX_PARSERS) {
|
|
3337
|
-
if (lowerBasename.endsWith(suffix)) return parser;
|
|
3338
|
-
}
|
|
3339
|
-
return void 0;
|
|
3340
|
-
}
|
|
3341
3890
|
async function extractDependencies(repositoryRoot, files) {
|
|
3342
3891
|
const allDeps = [];
|
|
3343
3892
|
for (const filePath of files) {
|
|
3344
|
-
const
|
|
3345
|
-
if (!
|
|
3893
|
+
const manifest = resolveManifest(filePath);
|
|
3894
|
+
if (!manifest) continue;
|
|
3346
3895
|
let content;
|
|
3347
3896
|
try {
|
|
3348
3897
|
content = await import_promises2.default.readFile(import_path6.default.join(repositoryRoot, filePath), "utf-8");
|
|
3349
3898
|
} catch {
|
|
3350
3899
|
continue;
|
|
3351
3900
|
}
|
|
3352
|
-
const
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
const classification = classifyDependency(raw.name, basename);
|
|
3356
|
-
if (classification) {
|
|
3901
|
+
for (const raw of manifest.parser(content)) {
|
|
3902
|
+
const entity = classifyDependency(raw.name, manifest.ecosystem);
|
|
3903
|
+
if (entity) {
|
|
3357
3904
|
allDeps.push({
|
|
3358
3905
|
name: raw.name,
|
|
3359
|
-
|
|
3360
|
-
|
|
3906
|
+
key: entity.key,
|
|
3907
|
+
displayName: entity.displayName,
|
|
3908
|
+
kind: entity.kind,
|
|
3361
3909
|
source: "manifest",
|
|
3362
3910
|
manifestFile: filePath,
|
|
3363
|
-
version: raw.version
|
|
3911
|
+
version: raw.version,
|
|
3912
|
+
ecosystem: manifest.ecosystem
|
|
3364
3913
|
});
|
|
3365
3914
|
}
|
|
3366
3915
|
}
|
|
3367
|
-
if (
|
|
3916
|
+
if (filePath.split("/").pop() === "package.json") {
|
|
3368
3917
|
for (const signal of detectPackageJsonSignals(content)) {
|
|
3369
3918
|
allDeps.push({ ...signal, manifestFile: filePath });
|
|
3370
3919
|
}
|
|
@@ -3376,8 +3925,8 @@ function deduplicateDeps(deps) {
|
|
|
3376
3925
|
const seen = /* @__PURE__ */ new Set();
|
|
3377
3926
|
const result = [];
|
|
3378
3927
|
for (const dep of deps) {
|
|
3379
|
-
if (!seen.has(dep.
|
|
3380
|
-
seen.add(dep.
|
|
3928
|
+
if (!seen.has(dep.key)) {
|
|
3929
|
+
seen.add(dep.key);
|
|
3381
3930
|
result.push(dep);
|
|
3382
3931
|
}
|
|
3383
3932
|
}
|
|
@@ -3469,6 +4018,28 @@ function applyCodacyYamlExcludes(config, yamlConfig, adapters, logger) {
|
|
|
3469
4018
|
function deduplicateStrings(arr) {
|
|
3470
4019
|
return [...new Set(arr)];
|
|
3471
4020
|
}
|
|
4021
|
+
async function resolveDiscoveryExcludes(repositoryRoot, adapters, existingConfig) {
|
|
4022
|
+
const yamlConfig = await readCodacyYaml(repositoryRoot);
|
|
4023
|
+
const global = deduplicateStrings([
|
|
4024
|
+
...existingConfig?.exclude ?? [],
|
|
4025
|
+
...yamlConfig?.excludePaths ?? []
|
|
4026
|
+
]);
|
|
4027
|
+
const perTool = /* @__PURE__ */ new Map();
|
|
4028
|
+
const addToolExcludes = (toolId, patterns) => {
|
|
4029
|
+
if (patterns.length === 0) return;
|
|
4030
|
+
perTool.set(toolId, deduplicateStrings([...perTool.get(toolId) ?? [], ...patterns]));
|
|
4031
|
+
};
|
|
4032
|
+
for (const tool of existingConfig?.tools ?? []) {
|
|
4033
|
+
addToolExcludes(tool.toolId, tool.exclude ?? []);
|
|
4034
|
+
}
|
|
4035
|
+
for (const [engineName, engineConfig] of yamlConfig?.engines ?? []) {
|
|
4036
|
+
if (engineConfig.excludePaths.length === 0) continue;
|
|
4037
|
+
const adapterId = resolveEngineToAdapter(engineName, adapters);
|
|
4038
|
+
if (!adapterId) continue;
|
|
4039
|
+
addToolExcludes(adapterId, engineConfig.excludePaths);
|
|
4040
|
+
}
|
|
4041
|
+
return { yamlConfig, global, perTool };
|
|
4042
|
+
}
|
|
3472
4043
|
|
|
3473
4044
|
// src/api/client/core/OpenAPI.ts
|
|
3474
4045
|
var OpenAPI = {
|
|
@@ -6908,8 +7479,14 @@ var ToolsService = class {
|
|
|
6908
7479
|
|
|
6909
7480
|
// src/api/index.ts
|
|
6910
7481
|
OpenAPI.BASE = (process.env.CODACY_API_BASE_URL || "https://app.codacy.com").replace(/\/$/, "") + "/api/v3";
|
|
6911
|
-
function configureApiToken(
|
|
6912
|
-
|
|
7482
|
+
function configureApiToken(auth) {
|
|
7483
|
+
const resolved = typeof auth === "string" ? { kind: "account-token", token: auth } : auth;
|
|
7484
|
+
if (!resolved || resolved.kind !== "account-token" && resolved.kind !== "repository-token" || !resolved.token) {
|
|
7485
|
+
throw new Error(
|
|
7486
|
+
'configureApiToken requires a non-empty apiToken string, or a RemoteAuth object ({ kind: "account-token" | "repository-token", token: string }).'
|
|
7487
|
+
);
|
|
7488
|
+
}
|
|
7489
|
+
OpenAPI.HEADERS = resolved.kind === "account-token" ? { "api-token": resolved.token } : { "project-token": resolved.token };
|
|
6913
7490
|
}
|
|
6914
7491
|
async function fetchAllPages(fetcher) {
|
|
6915
7492
|
const results = [];
|
|
@@ -7002,6 +7579,10 @@ async function listIgnoredFiles(provider, org, repo) {
|
|
|
7002
7579
|
}
|
|
7003
7580
|
|
|
7004
7581
|
// src/init.ts
|
|
7582
|
+
function isAuthFailure(err) {
|
|
7583
|
+
return err instanceof ApiError && (err.status === 401 || err.status === 403);
|
|
7584
|
+
}
|
|
7585
|
+
var AUTH_FAILURE_HINT = "The token was accepted for earlier reads but rejected here: if it is a repository token, check that it belongs to this repository; if it is an account token, check that the account has access to it.";
|
|
7005
7586
|
function deriveApiToolId(tool) {
|
|
7006
7587
|
return tool.prefix ? tool.prefix.replace(/_$/, "") : tool.shortName;
|
|
7007
7588
|
}
|
|
@@ -7033,7 +7614,7 @@ function buildAdapterByUuid(adapters, apiTools) {
|
|
|
7033
7614
|
}
|
|
7034
7615
|
return result;
|
|
7035
7616
|
}
|
|
7036
|
-
async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
7617
|
+
async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
|
|
7037
7618
|
let files;
|
|
7038
7619
|
if (precomputedFiles) {
|
|
7039
7620
|
files = precomputedFiles;
|
|
@@ -7049,11 +7630,14 @@ async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
|
7049
7630
|
return [];
|
|
7050
7631
|
}
|
|
7051
7632
|
}
|
|
7633
|
+
if (exclude && exclude.length > 0) {
|
|
7634
|
+
files = applyExclusions(files, [...exclude], []);
|
|
7635
|
+
}
|
|
7052
7636
|
const detected = /* @__PURE__ */ new Set();
|
|
7053
7637
|
for (const file of files) {
|
|
7054
7638
|
const basename = import_path8.default.basename(file);
|
|
7055
7639
|
const ext = import_path8.default.extname(file).toLowerCase();
|
|
7056
|
-
for (const [lang, config] of Object.entries(
|
|
7640
|
+
for (const [lang, config] of Object.entries(import_tooling7.LANGUAGE_CONFIG)) {
|
|
7057
7641
|
if (config.extensions.includes(ext)) {
|
|
7058
7642
|
detected.add(lang);
|
|
7059
7643
|
} else if (config.files.includes(basename)) {
|
|
@@ -7087,16 +7671,18 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
|
7087
7671
|
}
|
|
7088
7672
|
return found;
|
|
7089
7673
|
}
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
if (!yamlConfig) return { config, codacyYamlApplied: false };
|
|
7674
|
+
function enrichWithCodacyYaml(config, adapters, excludes) {
|
|
7675
|
+
if (!excludes.yamlConfig) return { config, codacyYamlApplied: false };
|
|
7093
7676
|
return {
|
|
7094
|
-
config: applyCodacyYamlExcludes(config, yamlConfig, adapters),
|
|
7677
|
+
config: applyCodacyYamlExcludes(config, excludes.yamlConfig, adapters),
|
|
7095
7678
|
codacyYamlApplied: true
|
|
7096
7679
|
};
|
|
7097
7680
|
}
|
|
7098
|
-
async function initLocalConfigBase(repositoryRoot, adapters) {
|
|
7099
|
-
const languages = await detectLanguages(
|
|
7681
|
+
async function initLocalConfigBase(repositoryRoot, adapters, excludes) {
|
|
7682
|
+
const languages = await detectLanguages(
|
|
7683
|
+
repositoryRoot,
|
|
7684
|
+
listRepoFiles(repositoryRoot, excludes.global)
|
|
7685
|
+
);
|
|
7100
7686
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7101
7687
|
const tools = [];
|
|
7102
7688
|
for (const [toolId, configPath] of localConfigs.entries()) {
|
|
@@ -7109,17 +7695,23 @@ async function initLocalConfigBase(repositoryRoot, adapters) {
|
|
|
7109
7695
|
}
|
|
7110
7696
|
return createCodacyConfig({ source: "local", languages, tools });
|
|
7111
7697
|
}
|
|
7112
|
-
async function initLocalConfig(repositoryRoot, adapters) {
|
|
7113
|
-
const
|
|
7114
|
-
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
7115
|
-
baseConfig,
|
|
7698
|
+
async function initLocalConfig(repositoryRoot, adapters, options) {
|
|
7699
|
+
const excludes = await resolveDiscoveryExcludes(
|
|
7116
7700
|
repositoryRoot,
|
|
7117
|
-
adapters
|
|
7701
|
+
adapters,
|
|
7702
|
+
options?.existingConfig
|
|
7118
7703
|
);
|
|
7704
|
+
const baseConfig = await initLocalConfigBase(repositoryRoot, adapters, excludes);
|
|
7705
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(baseConfig, adapters, excludes);
|
|
7119
7706
|
return { config, stats: { codacyYamlApplied } };
|
|
7120
7707
|
}
|
|
7121
|
-
async function initDefaultConfig(repositoryRoot, adapters) {
|
|
7122
|
-
const
|
|
7708
|
+
async function initDefaultConfig(repositoryRoot, adapters, options) {
|
|
7709
|
+
const excludes = await resolveDiscoveryExcludes(
|
|
7710
|
+
repositoryRoot,
|
|
7711
|
+
adapters,
|
|
7712
|
+
options?.existingConfig
|
|
7713
|
+
);
|
|
7714
|
+
const base = await initLocalConfigBase(repositoryRoot, adapters, excludes);
|
|
7123
7715
|
const toolsWithLocalConfig = new Set(
|
|
7124
7716
|
base.tools.filter((t) => t.useLocalConfigurationFile).map((t) => t.toolId)
|
|
7125
7717
|
);
|
|
@@ -7157,24 +7749,27 @@ async function initDefaultConfig(repositoryRoot, adapters) {
|
|
|
7157
7749
|
languages: base.metadata.languages,
|
|
7158
7750
|
tools: updatedTools
|
|
7159
7751
|
});
|
|
7160
|
-
const { config, codacyYamlApplied } =
|
|
7161
|
-
baseConfig,
|
|
7162
|
-
repositoryRoot,
|
|
7163
|
-
adapters
|
|
7164
|
-
);
|
|
7752
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(baseConfig, adapters, excludes);
|
|
7165
7753
|
return {
|
|
7166
7754
|
config,
|
|
7167
7755
|
stats: { totalApiTools: apiTools.length, addedTools, addedUnsupported, codacyYamlApplied }
|
|
7168
7756
|
};
|
|
7169
7757
|
}
|
|
7170
|
-
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters,
|
|
7171
|
-
configureApiToken(
|
|
7758
|
+
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, auth, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
|
|
7759
|
+
configureApiToken(auth);
|
|
7760
|
+
const repositoryLabel = `${provider}/${organization}/${repositoryName}`;
|
|
7172
7761
|
const repoInfo = await getRepository(provider, organization, repositoryName);
|
|
7173
7762
|
const remoteTools = await listRepositoryTools(provider, organization, repositoryName);
|
|
7174
7763
|
let ignoredFilesResult = { files: [], totalFetched: 0, capped: false };
|
|
7175
7764
|
try {
|
|
7176
7765
|
ignoredFilesResult = await listIgnoredFiles(provider, organization, repositoryName);
|
|
7177
|
-
} catch {
|
|
7766
|
+
} catch (err) {
|
|
7767
|
+
if (isAuthFailure(err)) {
|
|
7768
|
+
throw new Error(
|
|
7769
|
+
`Failed to fetch cloud-ignored files for ${repositoryLabel}: ${err.status} ${err.statusText}. ${AUTH_FAILURE_HINT} Aborting: continuing would silently drop this repository's cloud excludes from the generated config.`,
|
|
7770
|
+
{ cause: err }
|
|
7771
|
+
);
|
|
7772
|
+
}
|
|
7178
7773
|
}
|
|
7179
7774
|
onIgnoredFilesFetched?.(ignoredFilesResult.totalFetched, ignoredFilesResult.capped);
|
|
7180
7775
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
@@ -7183,7 +7778,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7183
7778
|
const apiToolByUuid = new Map(allApiTools.map((t) => [t.uuid, t]));
|
|
7184
7779
|
const matchedAdapterCount = remoteTools.filter((t) => adapterByUuid.has(t.uuid)).length;
|
|
7185
7780
|
onRemoteToolsFound?.(remoteTools.length, matchedAdapterCount);
|
|
7186
|
-
const fetchPatterns = async (uuid) => {
|
|
7781
|
+
const fetchPatterns = async (uuid, toolLabel) => {
|
|
7187
7782
|
try {
|
|
7188
7783
|
const configuredPatterns = await listRepositoryToolPatterns(
|
|
7189
7784
|
provider,
|
|
@@ -7198,7 +7793,13 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7198
7793
|
...params ? { parameters: params } : {}
|
|
7199
7794
|
};
|
|
7200
7795
|
});
|
|
7201
|
-
} catch {
|
|
7796
|
+
} catch (err) {
|
|
7797
|
+
if (isAuthFailure(err)) {
|
|
7798
|
+
throw new Error(
|
|
7799
|
+
`Failed to fetch enabled patterns for ${toolLabel} in ${repositoryLabel}: ${err.status} ${err.statusText}. ${AUTH_FAILURE_HINT} Aborting: continuing would write a configuration with no enabled patterns.`,
|
|
7800
|
+
{ cause: err }
|
|
7801
|
+
);
|
|
7802
|
+
}
|
|
7202
7803
|
return [];
|
|
7203
7804
|
}
|
|
7204
7805
|
};
|
|
@@ -7211,7 +7812,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7211
7812
|
if (adapter) {
|
|
7212
7813
|
matchedAdapters++;
|
|
7213
7814
|
const localConfigPath = localConfigs.get(adapter.id);
|
|
7214
|
-
const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
|
|
7815
|
+
const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid, adapter.displayName);
|
|
7215
7816
|
onToolProcessed?.(adapter.displayName, patterns2.length, usesLocalConfig);
|
|
7216
7817
|
tools.push({
|
|
7217
7818
|
toolId: adapter.id,
|
|
@@ -7226,7 +7827,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7226
7827
|
const apiTool = apiToolByUuid.get(remoteTool.uuid);
|
|
7227
7828
|
if (!apiTool || !isTrackableTool(apiTool)) continue;
|
|
7228
7829
|
const toolId = deriveApiToolId(apiTool);
|
|
7229
|
-
const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
|
|
7830
|
+
const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid, apiTool.name ?? toolId);
|
|
7230
7831
|
unsupportedTools++;
|
|
7231
7832
|
onToolProcessed?.(apiTool.name ?? toolId, patterns.length, usesLocalConfig);
|
|
7232
7833
|
tools.push({
|
|
@@ -7247,10 +7848,10 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7247
7848
|
if (ignoredFilesResult.files.length > 0) {
|
|
7248
7849
|
baseConfig.exclude = [.../* @__PURE__ */ new Set([...baseConfig.exclude ?? [], ...ignoredFilesResult.files])];
|
|
7249
7850
|
}
|
|
7250
|
-
const { config, codacyYamlApplied } =
|
|
7851
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(
|
|
7251
7852
|
baseConfig,
|
|
7252
|
-
|
|
7253
|
-
adapters
|
|
7853
|
+
adapters,
|
|
7854
|
+
await resolveDiscoveryExcludes(repositoryRoot, adapters)
|
|
7254
7855
|
);
|
|
7255
7856
|
return {
|
|
7256
7857
|
config,
|
|
@@ -7324,24 +7925,40 @@ function hasMatchingFiles(files, filePatterns) {
|
|
|
7324
7925
|
if (matcher.matchAll) return files.length > 0;
|
|
7325
7926
|
return files.some((file) => matcher.matches(file, file.split("/").pop() ?? ""));
|
|
7326
7927
|
}
|
|
7327
|
-
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString, loadUnsupportedPatterns) {
|
|
7328
|
-
const
|
|
7928
|
+
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString, loadUnsupportedPatterns, options) {
|
|
7929
|
+
const excludes = await resolveDiscoveryExcludes(
|
|
7930
|
+
repositoryRoot,
|
|
7931
|
+
adapters,
|
|
7932
|
+
options?.existingConfig
|
|
7933
|
+
);
|
|
7934
|
+
const discoveryResult = await discover(repositoryRoot, { exclude: excludes.global });
|
|
7329
7935
|
onDiscoveryComplete?.(discoveryResult);
|
|
7330
7936
|
const detectedKeys = /* @__PURE__ */ new Set();
|
|
7331
|
-
for (const lang of discoveryResult.languages) detectedKeys.add(lang);
|
|
7332
|
-
for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.
|
|
7333
|
-
for (const lib of discoveryResult.libraries) detectedKeys.add(lib.
|
|
7937
|
+
for (const lang of discoveryResult.languages) detectedKeys.add((0, import_tooling7.languageKey)(lang));
|
|
7938
|
+
for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.key);
|
|
7939
|
+
for (const lib of discoveryResult.libraries) detectedKeys.add(lib.key);
|
|
7334
7940
|
const repoLanguages = new Set(discoveryResult.languages);
|
|
7335
7941
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7336
|
-
const repoFiles = listRepoFiles(repositoryRoot);
|
|
7942
|
+
const repoFiles = listRepoFiles(repositoryRoot, excludes.global);
|
|
7337
7943
|
const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
|
|
7944
|
+
const perToolFiles = /* @__PURE__ */ new Map();
|
|
7945
|
+
const filesVisibleTo = (toolId) => {
|
|
7946
|
+
const toolExclude = excludes.perTool.get(toolId);
|
|
7947
|
+
if (!toolExclude || toolExclude.length === 0) return repoFiles;
|
|
7948
|
+
let files = perToolFiles.get(toolId);
|
|
7949
|
+
if (!files) {
|
|
7950
|
+
files = applyExclusions(repoFiles, [], [...toolExclude]);
|
|
7951
|
+
perToolFiles.set(toolId, files);
|
|
7952
|
+
}
|
|
7953
|
+
return files;
|
|
7954
|
+
};
|
|
7338
7955
|
const candidates = [];
|
|
7339
7956
|
for (const adapter of adapters) {
|
|
7340
7957
|
if (!adapter.languages.includes("*") && adapter.languages.length > 0 && repoLanguages.size > 0) {
|
|
7341
7958
|
const hasOverlap = adapter.languages.some((lang) => repoLanguages.has(lang));
|
|
7342
7959
|
if (!hasOverlap) continue;
|
|
7343
7960
|
}
|
|
7344
|
-
if (!hasMatchingFiles(
|
|
7961
|
+
if (!hasMatchingFiles(filesVisibleTo(adapter.id), adapter.filePatterns)) continue;
|
|
7345
7962
|
candidates.push(adapter);
|
|
7346
7963
|
}
|
|
7347
7964
|
const deduped = deduplicateToolFamilies(candidates, localConfigs);
|
|
@@ -7374,7 +7991,12 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7374
7991
|
patterns: selectedPatterns.map((p) => ({ patternId: p.id }))
|
|
7375
7992
|
};
|
|
7376
7993
|
tools.push(toolEntry);
|
|
7377
|
-
await applyAdapterParameterSuggestions(
|
|
7994
|
+
await applyAdapterParameterSuggestions(
|
|
7995
|
+
adapter,
|
|
7996
|
+
toolEntry,
|
|
7997
|
+
repositoryRoot,
|
|
7998
|
+
filesVisibleTo(adapter.id)
|
|
7999
|
+
);
|
|
7378
8000
|
}
|
|
7379
8001
|
}
|
|
7380
8002
|
let selectedUnsupported = 0;
|
|
@@ -7389,7 +8011,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7389
8011
|
}
|
|
7390
8012
|
const family = toolGroupKey(desc.id, desc.alternativeGroup);
|
|
7391
8013
|
if (selectedFamilies.has(family)) continue;
|
|
7392
|
-
if (!hasMatchingFiles(
|
|
8014
|
+
if (!hasMatchingFiles(filesVisibleTo(desc.id), desc.filePatterns)) continue;
|
|
7393
8015
|
const patternsMetadata = loadUnsupportedPatterns(desc.id);
|
|
7394
8016
|
if (patternsMetadata.length === 0) continue;
|
|
7395
8017
|
const selectedPatterns = filterAutoPatterns(
|
|
@@ -7414,11 +8036,7 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7414
8036
|
tools,
|
|
7415
8037
|
autoFilters: rawFilterString || void 0
|
|
7416
8038
|
});
|
|
7417
|
-
const { config, codacyYamlApplied } =
|
|
7418
|
-
baseConfig,
|
|
7419
|
-
repositoryRoot,
|
|
7420
|
-
adapters
|
|
7421
|
-
);
|
|
8039
|
+
const { config, codacyYamlApplied } = enrichWithCodacyYaml(baseConfig, adapters, excludes);
|
|
7422
8040
|
return {
|
|
7423
8041
|
config,
|
|
7424
8042
|
stats: {
|
|
@@ -7663,7 +8281,7 @@ function parseAutoFilters(raw) {
|
|
|
7663
8281
|
}
|
|
7664
8282
|
|
|
7665
8283
|
// src/index.ts
|
|
7666
|
-
var
|
|
8284
|
+
var import_tooling8 = require("@codacy/tooling");
|
|
7667
8285
|
|
|
7668
8286
|
// src/credentials.ts
|
|
7669
8287
|
var import_crypto = __toESM(require("crypto"));
|
|
@@ -7728,6 +8346,17 @@ async function resolveApiToken() {
|
|
|
7728
8346
|
if (stored) return stored;
|
|
7729
8347
|
throw new Error("No API token found. Set CODACY_API_TOKEN or run 'codacy-analysis login'.");
|
|
7730
8348
|
}
|
|
8349
|
+
async function resolveRemoteAuth(repositoryToken) {
|
|
8350
|
+
const projectToken = repositoryToken || process.env.CODACY_PROJECT_TOKEN || void 0;
|
|
8351
|
+
if (projectToken) return { kind: "repository-token", token: projectToken };
|
|
8352
|
+
const apiToken = process.env.CODACY_API_TOKEN || await loadCredentials() || void 0;
|
|
8353
|
+
if (!apiToken) {
|
|
8354
|
+
throw new Error(
|
|
8355
|
+
"No API token found. Provide --repository-token, set CODACY_PROJECT_TOKEN, set CODACY_API_TOKEN, or run 'codacy-analysis login'."
|
|
8356
|
+
);
|
|
8357
|
+
}
|
|
8358
|
+
return { kind: "account-token", token: apiToken };
|
|
8359
|
+
}
|
|
7731
8360
|
|
|
7732
8361
|
// src/git-info.ts
|
|
7733
8362
|
var import_child_process5 = require("child_process");
|
|
@@ -7809,11 +8438,12 @@ function parseSarif(runs) {
|
|
|
7809
8438
|
if (!patternId) continue;
|
|
7810
8439
|
const level = mapSarifLevelToCodacy(result.level);
|
|
7811
8440
|
const message = result.message?.text ?? "";
|
|
8441
|
+
const sourceId = result.properties?.sourceId;
|
|
7812
8442
|
for (const loc of result.locations ?? []) {
|
|
7813
8443
|
const filePath = loc.physicalLocation?.artifactLocation?.uri;
|
|
7814
8444
|
const line = loc.physicalLocation?.region?.startLine;
|
|
7815
8445
|
if (!filePath || !line) continue;
|
|
7816
|
-
issues.push({ toolId, patternId, filePath, line, message, level });
|
|
8446
|
+
issues.push({ toolId, patternId, filePath, line, message, level, sourceId });
|
|
7817
8447
|
}
|
|
7818
8448
|
}
|
|
7819
8449
|
}
|
|
@@ -7827,10 +8457,11 @@ function parseAnalysisResult(result) {
|
|
|
7827
8457
|
line: issue.line,
|
|
7828
8458
|
message: issue.message,
|
|
7829
8459
|
// JSON carries the authoritative Codacy severity; pass it through unchanged.
|
|
7830
|
-
level: issue.severity
|
|
8460
|
+
level: issue.severity,
|
|
8461
|
+
sourceId: issue.sourceId
|
|
7831
8462
|
}));
|
|
7832
8463
|
}
|
|
7833
|
-
function buildPayloads(issues, resolveShortName) {
|
|
8464
|
+
function buildPayloads(issues, resolveShortName, needsSourceId = () => false) {
|
|
7834
8465
|
const byTool = /* @__PURE__ */ new Map();
|
|
7835
8466
|
for (const issue of issues) {
|
|
7836
8467
|
const list = byTool.get(issue.toolId);
|
|
@@ -7839,6 +8470,7 @@ function buildPayloads(issues, resolveShortName) {
|
|
|
7839
8470
|
}
|
|
7840
8471
|
const payloads = [];
|
|
7841
8472
|
for (const [toolId, toolIssues] of byTool) {
|
|
8473
|
+
const withSourceId = needsSourceId(toolId);
|
|
7842
8474
|
const byFile = /* @__PURE__ */ new Map();
|
|
7843
8475
|
for (const issue of toolIssues) {
|
|
7844
8476
|
const entry = {
|
|
@@ -7847,7 +8479,8 @@ function buildPayloads(issues, resolveShortName) {
|
|
|
7847
8479
|
filename: issue.filePath,
|
|
7848
8480
|
message: { text: issue.message },
|
|
7849
8481
|
level: issue.level,
|
|
7850
|
-
location: { LineLocation: { line: issue.line } }
|
|
8482
|
+
location: { LineLocation: { line: issue.line } },
|
|
8483
|
+
...withSourceId && issue.sourceId ? { sourceId: issue.sourceId } : {}
|
|
7851
8484
|
}
|
|
7852
8485
|
};
|
|
7853
8486
|
const list = byFile.get(issue.filePath);
|
|
@@ -7879,6 +8512,13 @@ async function buildShortNameResolver(onProgress) {
|
|
|
7879
8512
|
}
|
|
7880
8513
|
return (toolId) => byName.get(toolId.toLowerCase()) ?? toolId.toLowerCase();
|
|
7881
8514
|
}
|
|
8515
|
+
function buildSourceIdResolver() {
|
|
8516
|
+
const needsIt = /* @__PURE__ */ new Set();
|
|
8517
|
+
for (const descriptor of getRegisteredDescriptors()) {
|
|
8518
|
+
if (descriptor.needsSourceIdUpload) needsIt.add(descriptor.id.toLowerCase());
|
|
8519
|
+
}
|
|
8520
|
+
return (toolId) => needsIt.has(toolId.toLowerCase());
|
|
8521
|
+
}
|
|
7882
8522
|
var COORDINATE_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
7883
8523
|
function assertValidCoordinate(kind, value) {
|
|
7884
8524
|
if (!COORDINATE_PATTERN.test(value)) {
|
|
@@ -7918,7 +8558,7 @@ async function uploadReport(options) {
|
|
|
7918
8558
|
onProgress?.("No issues found in the report; nothing to upload.");
|
|
7919
8559
|
}
|
|
7920
8560
|
const resolveShortName = await buildShortNameResolver(onProgress);
|
|
7921
|
-
const payloads = buildPayloads(issues, resolveShortName);
|
|
8561
|
+
const payloads = buildPayloads(issues, resolveShortName, buildSourceIdResolver());
|
|
7922
8562
|
const base = resolveResultsApiBaseUrl();
|
|
7923
8563
|
const urls = buildUrls(base, auth, commitUuid);
|
|
7924
8564
|
const headers = { "content-type": "application/json", ...authHeader(auth) };
|
|
@@ -8019,9 +8659,11 @@ async function safeReadBody(res) {
|
|
|
8019
8659
|
resolveApiToken,
|
|
8020
8660
|
resolveBaselinePath,
|
|
8021
8661
|
resolveConfigPath,
|
|
8662
|
+
resolveDiscoveryExcludes,
|
|
8022
8663
|
resolveEngineToAdapter,
|
|
8023
8664
|
resolveHeadCommit,
|
|
8024
8665
|
resolveProxyConfig,
|
|
8666
|
+
resolveRemoteAuth,
|
|
8025
8667
|
resolveRepositoryRoot,
|
|
8026
8668
|
resolveResultsApiBaseUrl,
|
|
8027
8669
|
rotateLogFiles,
|