@codacy/analysis-runner 0.18.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 +96 -31
- package/dist/index.js +1360 -1017
- 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,
|
|
@@ -79,7 +79,8 @@ __export(index_exports, {
|
|
|
79
79
|
resolveDiscoveryExcludes: () => resolveDiscoveryExcludes,
|
|
80
80
|
resolveEngineToAdapter: () => resolveEngineToAdapter,
|
|
81
81
|
resolveHeadCommit: () => resolveHeadCommit,
|
|
82
|
-
resolveProxyConfig: () =>
|
|
82
|
+
resolveProxyConfig: () => import_tooling8.resolveProxyConfig,
|
|
83
|
+
resolveRemoteAuth: () => resolveRemoteAuth,
|
|
83
84
|
resolveRepositoryRoot: () => resolveRepositoryRoot,
|
|
84
85
|
resolveResultsApiBaseUrl: () => resolveResultsApiBaseUrl,
|
|
85
86
|
rotateLogFiles: () => rotateLogFiles,
|
|
@@ -2331,750 +2332,16 @@ var import_promises4 = __toESM(require("fs/promises"));
|
|
|
2331
2332
|
var import_os3 = __toESM(require("os"));
|
|
2332
2333
|
var import_path8 = __toESM(require("path"));
|
|
2333
2334
|
var import_child_process4 = require("child_process");
|
|
2334
|
-
var
|
|
2335
|
+
var import_tooling7 = require("@codacy/tooling");
|
|
2335
2336
|
|
|
2336
|
-
// src/discover.ts
|
|
2337
|
+
// src/discover/index.ts
|
|
2337
2338
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
2338
2339
|
var import_path6 = __toESM(require("path"));
|
|
2339
2340
|
|
|
2340
|
-
// src/discover
|
|
2341
|
-
var
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
["react-dom", { displayName: "React DOM", kind: "framework" }],
|
|
2345
|
-
["react-native", { displayName: "React Native", kind: "framework" }],
|
|
2346
|
-
["vue", { displayName: "Vue.js", kind: "framework" }],
|
|
2347
|
-
["nuxt", { displayName: "Nuxt", kind: "framework" }],
|
|
2348
|
-
["@angular/core", { displayName: "Angular", kind: "framework" }],
|
|
2349
|
-
["svelte", { displayName: "Svelte", kind: "framework" }],
|
|
2350
|
-
["@sveltejs/kit", { displayName: "SvelteKit", kind: "framework" }],
|
|
2351
|
-
["next", { displayName: "Next.js", kind: "framework" }],
|
|
2352
|
-
["ember-source", { displayName: "Ember.js", kind: "framework" }],
|
|
2353
|
-
["lit", { displayName: "Lit", kind: "framework" }],
|
|
2354
|
-
["solid-js", { displayName: "Solid", kind: "framework" }],
|
|
2355
|
-
["astro", { displayName: "Astro", kind: "framework" }],
|
|
2356
|
-
["@builder.io/qwik", { displayName: "Qwik", kind: "framework" }],
|
|
2357
|
-
["@builder.io/qwik-city", { displayName: "Qwik", kind: "framework" }],
|
|
2358
|
-
["@remix-run/react", { displayName: "Remix", kind: "framework" }],
|
|
2359
|
-
["gatsby", { displayName: "Gatsby", kind: "framework" }],
|
|
2360
|
-
["preact", { displayName: "Preact", kind: "framework" }],
|
|
2361
|
-
["express", { displayName: "Express", kind: "framework" }],
|
|
2362
|
-
["@nestjs/core", { displayName: "NestJS", kind: "framework" }],
|
|
2363
|
-
["fastify", { displayName: "Fastify", kind: "framework" }],
|
|
2364
|
-
["koa", { displayName: "Koa", kind: "framework" }],
|
|
2365
|
-
["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
|
|
2366
|
-
["hono", { displayName: "Hono", kind: "framework" }],
|
|
2367
|
-
["electron", { displayName: "Electron", kind: "framework" }],
|
|
2368
|
-
// AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
|
|
2369
|
-
// and the v1 `@aws-cdk/*` construct packages (prefix rule below).
|
|
2370
|
-
["aws-cdk-lib", { displayName: "AWS CDK", kind: "framework" }],
|
|
2371
|
-
["aws-cdk", { displayName: "AWS CDK", kind: "framework" }],
|
|
2372
|
-
// --- JavaScript / TypeScript — Libraries ---
|
|
2373
|
-
["lodash", { displayName: "Lodash", kind: "library" }],
|
|
2374
|
-
["underscore", { displayName: "Underscore.js", kind: "library" }],
|
|
2375
|
-
["ramda", { displayName: "Ramda", kind: "library" }],
|
|
2376
|
-
["jest", { displayName: "Jest", kind: "library" }],
|
|
2377
|
-
["vitest", { displayName: "Vitest", kind: "library" }],
|
|
2378
|
-
["@playwright/test", { displayName: "Playwright", kind: "library" }],
|
|
2379
|
-
["playwright", { displayName: "Playwright", kind: "library" }],
|
|
2380
|
-
["cypress", { displayName: "Cypress", kind: "library" }],
|
|
2381
|
-
["mocha", { displayName: "Mocha", kind: "library" }],
|
|
2382
|
-
["ava", { displayName: "AVA", kind: "library" }],
|
|
2383
|
-
["jasmine", { displayName: "Jasmine", kind: "library" }],
|
|
2384
|
-
["jasmine-core", { displayName: "Jasmine", kind: "library" }],
|
|
2385
|
-
["@testing-library/react", { displayName: "Testing Library", kind: "library" }],
|
|
2386
|
-
["@testing-library/vue", { displayName: "Testing Library", kind: "library" }],
|
|
2387
|
-
["@testing-library/dom", { displayName: "Testing Library", kind: "library" }],
|
|
2388
|
-
["tailwindcss", { displayName: "Tailwind CSS", kind: "library" }],
|
|
2389
|
-
["@storybook/react", { displayName: "Storybook", kind: "library" }],
|
|
2390
|
-
["@storybook/vue3", { displayName: "Storybook", kind: "library" }],
|
|
2391
|
-
["@storybook/svelte", { displayName: "Storybook", kind: "library" }],
|
|
2392
|
-
["redux", { displayName: "Redux", kind: "library" }],
|
|
2393
|
-
["@reduxjs/toolkit", { displayName: "Redux Toolkit", kind: "library" }],
|
|
2394
|
-
["mobx", { displayName: "MobX", kind: "library" }],
|
|
2395
|
-
["zustand", { displayName: "Zustand", kind: "library" }],
|
|
2396
|
-
["axios", { displayName: "Axios", kind: "library" }],
|
|
2397
|
-
["date-fns", { displayName: "date-fns", kind: "library" }],
|
|
2398
|
-
["dayjs", { displayName: "Day.js", kind: "library" }],
|
|
2399
|
-
["moment", { displayName: "Moment.js", kind: "library" }],
|
|
2400
|
-
["rxjs", { displayName: "RxJS", kind: "library" }],
|
|
2401
|
-
["zod", { displayName: "Zod", kind: "library" }],
|
|
2402
|
-
["joi", { displayName: "Joi", kind: "library" }],
|
|
2403
|
-
["yup", { displayName: "Yup", kind: "library" }],
|
|
2404
|
-
["prisma", { displayName: "Prisma", kind: "library" }],
|
|
2405
|
-
["@prisma/client", { displayName: "Prisma", kind: "library" }],
|
|
2406
|
-
["typeorm", { displayName: "TypeORM", kind: "library" }],
|
|
2407
|
-
["sequelize", { displayName: "Sequelize", kind: "library" }],
|
|
2408
|
-
["drizzle-orm", { displayName: "Drizzle ORM", kind: "library" }],
|
|
2409
|
-
["mongoose", { displayName: "Mongoose", kind: "library" }],
|
|
2410
|
-
["graphql", { displayName: "GraphQL", kind: "library" }],
|
|
2411
|
-
["@apollo/client", { displayName: "Apollo Client", kind: "library" }],
|
|
2412
|
-
["socket.io", { displayName: "Socket.IO", kind: "library" }],
|
|
2413
|
-
["jquery", { displayName: "jQuery", kind: "library" }],
|
|
2414
|
-
["mongodb", { displayName: "MongoDB", kind: "library" }],
|
|
2415
|
-
["three", { displayName: "Three.js", kind: "library" }],
|
|
2416
|
-
["d3", { displayName: "D3.js", kind: "library" }],
|
|
2417
|
-
["chart.js", { displayName: "Chart.js", kind: "library" }],
|
|
2418
|
-
["sass", { displayName: "Sass", kind: "library" }],
|
|
2419
|
-
["node-sass", { displayName: "Sass", kind: "library" }],
|
|
2420
|
-
["less", { displayName: "Less", kind: "library" }],
|
|
2421
|
-
["styled-components", { displayName: "styled-components", kind: "library" }],
|
|
2422
|
-
["@emotion/react", { displayName: "Emotion", kind: "library" }],
|
|
2423
|
-
["pino", { displayName: "Pino", kind: "library" }],
|
|
2424
|
-
["winston", { displayName: "Winston", kind: "library" }],
|
|
2425
|
-
["i18next", { displayName: "i18next", kind: "library" }],
|
|
2426
|
-
// react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
|
|
2427
|
-
// lints both, so we report the umbrella name "formatjs" (matches the mapping key).
|
|
2428
|
-
["react-intl", { displayName: "formatjs", kind: "library" }],
|
|
2429
|
-
["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
|
|
2430
|
-
["next-intl", { displayName: "next-intl", kind: "library" }],
|
|
2431
|
-
// Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
|
|
2432
|
-
["flow-bin", { displayName: "Flow", kind: "library" }],
|
|
2433
|
-
// Legacy JS libraries with dedicated ESLint plugins.
|
|
2434
|
-
["backbone", { displayName: "Backbone.js", kind: "library" }],
|
|
2435
|
-
["chai", { displayName: "Chai", kind: "library" }],
|
|
2436
|
-
["codeceptjs", { displayName: "CodeceptJS", kind: "library" }],
|
|
2437
|
-
["webdriverio", { displayName: "WebdriverIO", kind: "library" }],
|
|
2438
|
-
["react-relay", { displayName: "Relay", kind: "library" }],
|
|
2439
|
-
["relay-runtime", { displayName: "Relay", kind: "library" }],
|
|
2440
|
-
// --- JavaScript / TypeScript — Tools ---
|
|
2441
|
-
["typescript", { displayName: "TypeScript", kind: "tool" }],
|
|
2442
|
-
["eslint", { displayName: "ESLint", kind: "tool" }],
|
|
2443
|
-
["prettier", { displayName: "Prettier", kind: "tool" }],
|
|
2444
|
-
["webpack", { displayName: "Webpack", kind: "tool" }],
|
|
2445
|
-
["vite", { displayName: "Vite", kind: "tool" }],
|
|
2446
|
-
["esbuild", { displayName: "esbuild", kind: "tool" }],
|
|
2447
|
-
["rollup", { displayName: "Rollup", kind: "tool" }],
|
|
2448
|
-
["turbo", { displayName: "Turborepo", kind: "tool" }],
|
|
2449
|
-
["tsup", { displayName: "tsup", kind: "tool" }],
|
|
2450
|
-
["@biomejs/biome", { displayName: "Biome", kind: "tool" }],
|
|
2451
|
-
["stylelint", { displayName: "Stylelint", kind: "tool" }],
|
|
2452
|
-
// --- JavaScript / TypeScript — Platforms ---
|
|
2453
|
-
["@lwc/engine-dom", { displayName: "Lightning Web Components", kind: "platform" }],
|
|
2454
|
-
["@salesforce/core", { displayName: "Salesforce", kind: "platform" }],
|
|
2455
|
-
["@shopify/polaris", { displayName: "Shopify Polaris", kind: "platform" }],
|
|
2456
|
-
// --- Python — Frameworks ---
|
|
2457
|
-
["django", { displayName: "Django", kind: "framework" }],
|
|
2458
|
-
["flask", { displayName: "Flask", kind: "framework" }],
|
|
2459
|
-
["fastapi", { displayName: "FastAPI", kind: "framework" }],
|
|
2460
|
-
["tornado", { displayName: "Tornado", kind: "framework" }],
|
|
2461
|
-
["pyramid", { displayName: "Pyramid", kind: "framework" }],
|
|
2462
|
-
["starlette", { displayName: "Starlette", kind: "framework" }],
|
|
2463
|
-
["sanic", { displayName: "Sanic", kind: "framework" }],
|
|
2464
|
-
["bottle", { displayName: "Bottle", kind: "framework" }],
|
|
2465
|
-
["streamlit", { displayName: "Streamlit", kind: "framework" }],
|
|
2466
|
-
["gradio", { displayName: "Gradio", kind: "framework" }],
|
|
2467
|
-
["apache-airflow", { displayName: "Airflow", kind: "framework" }],
|
|
2468
|
-
// --- Python — Libraries ---
|
|
2469
|
-
["flask-babel", { displayName: "Flask-Babel", kind: "library" }],
|
|
2470
|
-
["pytest", { displayName: "pytest", kind: "library" }],
|
|
2471
|
-
["numpy", { displayName: "NumPy", kind: "library" }],
|
|
2472
|
-
["pandas", { displayName: "pandas", kind: "library" }],
|
|
2473
|
-
["requests", { displayName: "Requests", kind: "library" }],
|
|
2474
|
-
["sqlalchemy", { displayName: "SQLAlchemy", kind: "library" }],
|
|
2475
|
-
["celery", { displayName: "Celery", kind: "library" }],
|
|
2476
|
-
["pydantic", { displayName: "Pydantic", kind: "library" }],
|
|
2477
|
-
["boto3", { displayName: "Boto3 (AWS SDK)", kind: "library" }],
|
|
2478
|
-
["scipy", { displayName: "SciPy", kind: "library" }],
|
|
2479
|
-
["scikit-learn", { displayName: "scikit-learn", kind: "library" }],
|
|
2480
|
-
["tensorflow", { displayName: "TensorFlow", kind: "library" }],
|
|
2481
|
-
["torch", { displayName: "PyTorch", kind: "library" }],
|
|
2482
|
-
["httpx", { displayName: "HTTPX", kind: "library" }],
|
|
2483
|
-
["aiohttp", { displayName: "aiohttp", kind: "library" }],
|
|
2484
|
-
["pillow", { displayName: "Pillow", kind: "library" }],
|
|
2485
|
-
["beautifulsoup4", { displayName: "Beautiful Soup", kind: "library" }],
|
|
2486
|
-
["scrapy", { displayName: "Scrapy", kind: "library" }],
|
|
2487
|
-
["alembic", { displayName: "Alembic", kind: "library" }],
|
|
2488
|
-
["marshmallow", { displayName: "Marshmallow", kind: "library" }],
|
|
2489
|
-
["click", { displayName: "Click", kind: "library" }],
|
|
2490
|
-
["typer", { displayName: "Typer", kind: "library" }],
|
|
2491
|
-
["rich", { displayName: "Rich", kind: "library" }],
|
|
2492
|
-
["polars", { displayName: "Polars", kind: "library" }],
|
|
2493
|
-
["dask", { displayName: "Dask", kind: "library" }],
|
|
2494
|
-
["matplotlib", { displayName: "Matplotlib", kind: "library" }],
|
|
2495
|
-
["seaborn", { displayName: "Seaborn", kind: "library" }],
|
|
2496
|
-
["plotly", { displayName: "Plotly", kind: "library" }],
|
|
2497
|
-
// MongoDB drivers, one entry per ecosystem — they all gate the same
|
|
2498
|
-
// MongoDB-specific patterns (Opengrep's `java.mongodb.`/`python.pymongo.`).
|
|
2499
|
-
["pymongo", { displayName: "MongoDB", kind: "library" }],
|
|
2500
|
-
["motor", { displayName: "MongoDB", kind: "library" }],
|
|
2501
|
-
// --- Python — Tools ---
|
|
2502
|
-
["black", { displayName: "Black", kind: "tool" }],
|
|
2503
|
-
["ruff", { displayName: "Ruff", kind: "tool" }],
|
|
2504
|
-
["mypy", { displayName: "mypy", kind: "tool" }],
|
|
2505
|
-
["pylint", { displayName: "Pylint", kind: "tool" }],
|
|
2506
|
-
["flake8", { displayName: "Flake8", kind: "tool" }],
|
|
2507
|
-
["bandit", { displayName: "Bandit", kind: "tool" }],
|
|
2508
|
-
["isort", { displayName: "isort", kind: "tool" }],
|
|
2509
|
-
// --- Ruby — Frameworks ---
|
|
2510
|
-
["rails", { displayName: "Ruby on Rails", kind: "framework" }],
|
|
2511
|
-
["sinatra", { displayName: "Sinatra", kind: "framework" }],
|
|
2512
|
-
["hanami", { displayName: "Hanami", kind: "framework" }],
|
|
2513
|
-
["grape", { displayName: "Grape", kind: "framework" }],
|
|
2514
|
-
["padrino", { displayName: "Padrino", kind: "framework" }],
|
|
2515
|
-
// --- Ruby — Libraries ---
|
|
2516
|
-
["rspec", { displayName: "RSpec", kind: "library" }],
|
|
2517
|
-
["rspec-core", { displayName: "RSpec", kind: "library" }],
|
|
2518
|
-
["rspec-rails", { displayName: "RSpec Rails", kind: "library" }],
|
|
2519
|
-
["capybara", { displayName: "Capybara", kind: "library" }],
|
|
2520
|
-
["sidekiq", { displayName: "Sidekiq", kind: "library" }],
|
|
2521
|
-
["devise", { displayName: "Devise", kind: "library" }],
|
|
2522
|
-
["pundit", { displayName: "Pundit", kind: "library" }],
|
|
2523
|
-
["activeadmin", { displayName: "ActiveAdmin", kind: "library" }],
|
|
2524
|
-
["puma", { displayName: "Puma", kind: "library" }],
|
|
2525
|
-
["nokogiri", { displayName: "Nokogiri", kind: "library" }],
|
|
2526
|
-
["faraday", { displayName: "Faraday", kind: "library" }],
|
|
2527
|
-
["httparty", { displayName: "HTTParty", kind: "library" }],
|
|
2528
|
-
["minitest", { displayName: "Minitest", kind: "library" }],
|
|
2529
|
-
["factory_bot", { displayName: "FactoryBot", kind: "library" }],
|
|
2530
|
-
["dry-validation", { displayName: "dry-validation", kind: "library" }],
|
|
2531
|
-
// NOTE: `faker` and `rake` are Gemfile-scoped (MANIFEST_SCOPED_CATALOG), not
|
|
2532
|
-
// global — they are bare words that also name npm/PyPI packages.
|
|
2533
|
-
// --- Ruby — Tools ---
|
|
2534
|
-
["rubocop", { displayName: "RuboCop", kind: "tool" }],
|
|
2535
|
-
["bundler", { displayName: "Bundler", kind: "tool" }],
|
|
2536
|
-
// Sorbet ships as several gems; any of them means the project is Sorbet-typed.
|
|
2537
|
-
["sorbet", { displayName: "Sorbet", kind: "tool" }],
|
|
2538
|
-
["sorbet-runtime", { displayName: "Sorbet", kind: "tool" }],
|
|
2539
|
-
["sorbet-static", { displayName: "Sorbet", kind: "tool" }],
|
|
2540
|
-
["sorbet-static-and-runtime", { displayName: "Sorbet", kind: "tool" }],
|
|
2541
|
-
["solargraph", { displayName: "Solargraph", kind: "tool" }],
|
|
2542
|
-
// --- Java / Kotlin — Frameworks ---
|
|
2543
|
-
["spring-boot-starter", { displayName: "Spring Boot", kind: "framework" }],
|
|
2544
|
-
["spring-boot-starter-web", { displayName: "Spring Boot", kind: "framework" }],
|
|
2545
|
-
["spring-core", { displayName: "Spring Framework", kind: "framework" }],
|
|
2546
|
-
["quarkus-core", { displayName: "Quarkus", kind: "framework" }],
|
|
2547
|
-
["micronaut-core", { displayName: "Micronaut", kind: "framework" }],
|
|
2548
|
-
["ktor-server-core", { displayName: "Ktor", kind: "framework" }],
|
|
2549
|
-
["android", { displayName: "Android SDK", kind: "framework" }],
|
|
2550
|
-
// --- Java / Kotlin — Libraries ---
|
|
2551
|
-
["junit-jupiter", { displayName: "JUnit 5", kind: "library" }],
|
|
2552
|
-
["junit", { displayName: "JUnit", kind: "library" }],
|
|
2553
|
-
["junit-jupiter-api", { displayName: "JUnit 5", kind: "library" }],
|
|
2554
|
-
["mockito-core", { displayName: "Mockito", kind: "library" }],
|
|
2555
|
-
["lombok", { displayName: "Lombok", kind: "library" }],
|
|
2556
|
-
["jackson-databind", { displayName: "Jackson", kind: "library" }],
|
|
2557
|
-
["jackson-core", { displayName: "Jackson", kind: "library" }],
|
|
2558
|
-
["hibernate-core", { displayName: "Hibernate", kind: "library" }],
|
|
2559
|
-
["guava", { displayName: "Guava", kind: "library" }],
|
|
2560
|
-
["commons-lang3", { displayName: "Apache Commons Lang", kind: "library" }],
|
|
2561
|
-
["commons-io", { displayName: "Apache Commons IO", kind: "library" }],
|
|
2562
|
-
["slf4j-api", { displayName: "SLF4J", kind: "library" }],
|
|
2563
|
-
["log4j-core", { displayName: "Log4j", kind: "library" }],
|
|
2564
|
-
["logback-classic", { displayName: "Logback", kind: "library" }],
|
|
2565
|
-
["testng", { displayName: "TestNG", kind: "library" }],
|
|
2566
|
-
["assertj-core", { displayName: "AssertJ", kind: "library" }],
|
|
2567
|
-
["gson", { displayName: "Gson", kind: "library" }],
|
|
2568
|
-
["okhttp", { displayName: "OkHttp", kind: "library" }],
|
|
2569
|
-
["retrofit", { displayName: "Retrofit", kind: "library" }],
|
|
2570
|
-
["spock-core", { displayName: "Spock", kind: "library" }],
|
|
2571
|
-
["mongodb-driver-sync", { displayName: "MongoDB", kind: "library" }],
|
|
2572
|
-
["mongodb-driver-core", { displayName: "MongoDB", kind: "library" }],
|
|
2573
|
-
["mongodb-driver-reactivestreams", { displayName: "MongoDB", kind: "library" }],
|
|
2574
|
-
["mongo-java-driver", { displayName: "MongoDB", kind: "library" }],
|
|
2575
|
-
// --- Groovy — Frameworks ---
|
|
2576
|
-
// Grails/GORM artifact IDs vary (grails-core, grails-web, gorm-hibernate5, …);
|
|
2577
|
-
// the `grails-`/`gorm-` prefix rules below catch the rest.
|
|
2578
|
-
["grails-core", { displayName: "Grails", kind: "framework" }],
|
|
2579
|
-
["gorm-core", { displayName: "GORM", kind: "library" }],
|
|
2580
|
-
// --- Go — Frameworks ---
|
|
2581
|
-
["github.com/gin-gonic/gin", { displayName: "Gin", kind: "framework" }],
|
|
2582
|
-
["github.com/labstack/echo", { displayName: "Echo", kind: "framework" }],
|
|
2583
|
-
["github.com/gofiber/fiber", { displayName: "Fiber", kind: "framework" }],
|
|
2584
|
-
["github.com/go-chi/chi", { displayName: "Chi", kind: "framework" }],
|
|
2585
|
-
["github.com/gorilla/mux", { displayName: "Gorilla Mux", kind: "framework" }],
|
|
2586
|
-
["github.com/beego/beego", { displayName: "Beego", kind: "framework" }],
|
|
2587
|
-
// --- Go — Libraries ---
|
|
2588
|
-
["github.com/stretchr/testify", { displayName: "Testify", kind: "library" }],
|
|
2589
|
-
["github.com/spf13/cobra", { displayName: "Cobra", kind: "library" }],
|
|
2590
|
-
["github.com/spf13/viper", { displayName: "Viper", kind: "library" }],
|
|
2591
|
-
["go.uber.org/zap", { displayName: "Zap", kind: "library" }],
|
|
2592
|
-
["github.com/sirupsen/logrus", { displayName: "Logrus", kind: "library" }],
|
|
2593
|
-
["gorm.io/gorm", { displayName: "GORM", kind: "library" }],
|
|
2594
|
-
["github.com/go-kit/kit", { displayName: "Go Kit", kind: "library" }],
|
|
2595
|
-
["google.golang.org/protobuf", { displayName: "Protocol Buffers", kind: "library" }],
|
|
2596
|
-
["google.golang.org/grpc", { displayName: "gRPC", kind: "library" }],
|
|
2597
|
-
["github.com/jackc/pgx", { displayName: "pgx", kind: "library" }],
|
|
2598
|
-
["github.com/redis/go-redis", { displayName: "go-redis", kind: "library" }],
|
|
2599
|
-
["github.com/aws/aws-sdk-go-v2", { displayName: "AWS SDK for Go", kind: "library" }],
|
|
2600
|
-
["github.com/prometheus/client_golang", { displayName: "Prometheus Client", kind: "library" }],
|
|
2601
|
-
// --- Rust — Frameworks ---
|
|
2602
|
-
["actix-web", { displayName: "Actix Web", kind: "framework" }],
|
|
2603
|
-
["axum", { displayName: "Axum", kind: "framework" }],
|
|
2604
|
-
["rocket", { displayName: "Rocket", kind: "framework" }],
|
|
2605
|
-
["warp", { displayName: "Warp", kind: "framework" }],
|
|
2606
|
-
["tauri", { displayName: "Tauri", kind: "framework" }],
|
|
2607
|
-
// --- Rust — Libraries ---
|
|
2608
|
-
["tokio", { displayName: "Tokio", kind: "library" }],
|
|
2609
|
-
["serde", { displayName: "Serde", kind: "library" }],
|
|
2610
|
-
["clap", { displayName: "clap", kind: "library" }],
|
|
2611
|
-
["reqwest", { displayName: "reqwest", kind: "library" }],
|
|
2612
|
-
["diesel", { displayName: "Diesel", kind: "library" }],
|
|
2613
|
-
["sqlx", { displayName: "SQLx", kind: "library" }],
|
|
2614
|
-
["tracing", { displayName: "Tracing", kind: "library" }],
|
|
2615
|
-
["anyhow", { displayName: "anyhow", kind: "library" }],
|
|
2616
|
-
["thiserror", { displayName: "thiserror", kind: "library" }],
|
|
2617
|
-
["rayon", { displayName: "Rayon", kind: "library" }],
|
|
2618
|
-
// --- PHP — Frameworks ---
|
|
2619
|
-
["laravel/framework", { displayName: "Laravel", kind: "framework" }],
|
|
2620
|
-
["symfony/framework-bundle", { displayName: "Symfony", kind: "framework" }],
|
|
2621
|
-
["symfony/symfony", { displayName: "Symfony", kind: "framework" }],
|
|
2622
|
-
["codeigniter4/framework", { displayName: "CodeIgniter", kind: "framework" }],
|
|
2623
|
-
["slim/slim", { displayName: "Slim", kind: "framework" }],
|
|
2624
|
-
["cakephp/cakephp", { displayName: "CakePHP", kind: "framework" }],
|
|
2625
|
-
// WordPress is rarely a real Composer dependency; these are the common
|
|
2626
|
-
// "WordPress as a dependency" packages used by Bedrock/Composer-based sites.
|
|
2627
|
-
["johnpbloch/wordpress-core", { displayName: "WordPress", kind: "framework" }],
|
|
2628
|
-
["johnpbloch/wordpress", { displayName: "WordPress", kind: "framework" }],
|
|
2629
|
-
["roots/wordpress", { displayName: "WordPress", kind: "framework" }],
|
|
2630
|
-
["drupal/core", { displayName: "Drupal", kind: "framework" }],
|
|
2631
|
-
["drupal/core-recommended", { displayName: "Drupal", kind: "framework" }],
|
|
2632
|
-
["magento/product-community-edition", { displayName: "Magento", kind: "framework" }],
|
|
2633
|
-
["magento/product-enterprise-edition", { displayName: "Magento", kind: "framework" }],
|
|
2634
|
-
["magento/framework", { displayName: "Magento", kind: "framework" }],
|
|
2635
|
-
// --- PHP — Libraries ---
|
|
2636
|
-
["phpunit/phpunit", { displayName: "PHPUnit", kind: "library" }],
|
|
2637
|
-
["guzzlehttp/guzzle", { displayName: "Guzzle", kind: "library" }],
|
|
2638
|
-
["monolog/monolog", { displayName: "Monolog", kind: "library" }],
|
|
2639
|
-
["nesbot/carbon", { displayName: "Carbon", kind: "library" }],
|
|
2640
|
-
["doctrine/orm", { displayName: "Doctrine ORM", kind: "library" }],
|
|
2641
|
-
["doctrine/dbal", { displayName: "Doctrine DBAL", kind: "library" }],
|
|
2642
|
-
["league/flysystem", { displayName: "Flysystem", kind: "library" }],
|
|
2643
|
-
["php-amqplib/php-amqplib", { displayName: "php-amqplib", kind: "library" }],
|
|
2644
|
-
["predis/predis", { displayName: "Predis", kind: "library" }],
|
|
2645
|
-
// --- PHP — Tools ---
|
|
2646
|
-
["phpstan/phpstan", { displayName: "PHPStan", kind: "tool" }],
|
|
2647
|
-
["squizlabs/php_codesniffer", { displayName: "PHP_CodeSniffer", kind: "tool" }],
|
|
2648
|
-
// --- Swift — Frameworks ---
|
|
2649
|
-
["vapor", { displayName: "Vapor", kind: "framework" }],
|
|
2650
|
-
// --- Swift — Libraries ---
|
|
2651
|
-
["Alamofire", { displayName: "Alamofire", kind: "library" }],
|
|
2652
|
-
["SnapKit", { displayName: "SnapKit", kind: "library" }],
|
|
2653
|
-
["Kingfisher", { displayName: "Kingfisher", kind: "library" }],
|
|
2654
|
-
["Moya", { displayName: "Moya", kind: "library" }],
|
|
2655
|
-
["RxSwift", { displayName: "RxSwift", kind: "library" }],
|
|
2656
|
-
["RealmSwift", { displayName: "Realm", kind: "library" }],
|
|
2657
|
-
// --- Scala — Frameworks ---
|
|
2658
|
-
// Play is declared as an sbt *plugin*, so `group:artifact` (from
|
|
2659
|
-
// `addSbtPlugin`) is the reliable signal; the library artifacts below only
|
|
2660
|
-
// appear in some builds. `play-json` is deliberately absent — it is a
|
|
2661
|
-
// standalone JSON library used widely outside Play.
|
|
2662
|
-
["com.typesafe.play:sbt-plugin", { displayName: "Play Framework", kind: "framework" }],
|
|
2663
|
-
["org.playframework:sbt-plugin", { displayName: "Play Framework", kind: "framework" }],
|
|
2664
|
-
["play", { displayName: "Play Framework", kind: "framework" }],
|
|
2665
|
-
["play-server", { displayName: "Play Framework", kind: "framework" }],
|
|
2666
|
-
["play-ws", { displayName: "Play Framework", kind: "framework" }],
|
|
2667
|
-
["play-test", { displayName: "Play Framework", kind: "framework" }],
|
|
2668
|
-
["play-slick", { displayName: "Play Framework", kind: "framework" }],
|
|
2669
|
-
["twirl-api", { displayName: "Play Framework", kind: "framework" }],
|
|
2670
|
-
["scalatestplus-play", { displayName: "Play Framework", kind: "framework" }],
|
|
2671
|
-
["akka-actor", { displayName: "Akka", kind: "framework" }],
|
|
2672
|
-
["spray-can", { displayName: "Spray", kind: "framework" }],
|
|
2673
|
-
// --- Scala — Libraries ---
|
|
2674
|
-
["scalatest", { displayName: "ScalaTest", kind: "library" }],
|
|
2675
|
-
["specs2-core", { displayName: "Specs2", kind: "library" }],
|
|
2676
|
-
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
2677
|
-
["circe-core", { displayName: "Circe", kind: "library" }],
|
|
2678
|
-
// Slick gates Opengrep's `scala.slick.` SQL-injection patterns.
|
|
2679
|
-
["slick", { displayName: "Slick", kind: "library" }],
|
|
2680
|
-
["slick-hikaricp", { displayName: "Slick", kind: "library" }],
|
|
2681
|
-
// jwt-scala publishes one artifact per JSON backend; all gate the same
|
|
2682
|
-
// hardcoded-secret patterns.
|
|
2683
|
-
["jwt-core", { displayName: "jwt-scala", kind: "library" }],
|
|
2684
|
-
["jwt-circe", { displayName: "jwt-scala", kind: "library" }],
|
|
2685
|
-
["jwt-play-json", { displayName: "jwt-scala", kind: "library" }],
|
|
2686
|
-
["jwt-json4s", { displayName: "jwt-scala", kind: "library" }],
|
|
2687
|
-
["jwt-spray-json", { displayName: "jwt-scala", kind: "library" }],
|
|
2688
|
-
["jwt-upickle", { displayName: "jwt-scala", kind: "library" }],
|
|
2689
|
-
["jwt-argonaut", { displayName: "jwt-scala", kind: "library" }],
|
|
2690
|
-
["munit", { displayName: "MUnit", kind: "library" }],
|
|
2691
|
-
["scalacheck", { displayName: "ScalaCheck", kind: "library" }],
|
|
2692
|
-
["scala-logging", { displayName: "scala-logging", kind: "library" }],
|
|
2693
|
-
["chimney", { displayName: "Chimney", kind: "library" }],
|
|
2694
|
-
// --- Elixir — Frameworks ---
|
|
2695
|
-
["phoenix", { displayName: "Phoenix", kind: "framework" }],
|
|
2696
|
-
["plug", { displayName: "Plug", kind: "framework" }],
|
|
2697
|
-
["plug_cowboy", { displayName: "Plug", kind: "framework" }],
|
|
2698
|
-
// --- Elixir — Libraries ---
|
|
2699
|
-
["ecto", { displayName: "Ecto", kind: "library" }],
|
|
2700
|
-
["ecto_sql", { displayName: "Ecto", kind: "library" }],
|
|
2701
|
-
["phoenix_ecto", { displayName: "Ecto", kind: "library" }],
|
|
2702
|
-
// --- Crystal — Frameworks ---
|
|
2703
|
-
["amber", { displayName: "Amber", kind: "framework" }],
|
|
2704
|
-
["lucky", { displayName: "Lucky", kind: "framework" }],
|
|
2705
|
-
["kemal", { displayName: "Kemal", kind: "framework" }],
|
|
2706
|
-
// --- Dart — Frameworks ---
|
|
2707
|
-
// `flutter` and `angular` appear as plain keys in pubspec.yaml. NOTE: `angular`
|
|
2708
|
-
// here is Dart's AngularDart. The npm package `angular` is AngularJS (1.x) — a
|
|
2709
|
-
// different framework — so package.json overrides this via MANIFEST_SCOPED_CATALOG.
|
|
2710
|
-
["flutter", { displayName: "Flutter", kind: "framework" }],
|
|
2711
|
-
["flutter_test", { displayName: "Flutter", kind: "framework" }],
|
|
2712
|
-
["angular", { displayName: "Angular", kind: "framework" }],
|
|
2713
|
-
// --- Dart — Libraries ---
|
|
2714
|
-
// Kept to Dart-specific names with negligible cross-ecosystem collision risk
|
|
2715
|
-
// (generic names like "get"/"provider" are intentionally omitted).
|
|
2716
|
-
["riverpod", { displayName: "Riverpod", kind: "library" }],
|
|
2717
|
-
["flutter_bloc", { displayName: "Bloc", kind: "library" }],
|
|
2718
|
-
["dio", { displayName: "Dio", kind: "library" }],
|
|
2719
|
-
// --- .NET (C# / VB) — Libraries ---
|
|
2720
|
-
// NuGet package IDs. Microsoft.* families are also covered by prefix rules below.
|
|
2721
|
-
["EntityFramework", { displayName: "Entity Framework", kind: "library" }],
|
|
2722
|
-
["NUnit", { displayName: "NUnit", kind: "library" }],
|
|
2723
|
-
["Moq", { displayName: "Moq", kind: "library" }],
|
|
2724
|
-
["NSubstitute", { displayName: "NSubstitute", kind: "library" }],
|
|
2725
|
-
["FluentAssertions", { displayName: "FluentAssertions", kind: "library" }],
|
|
2726
|
-
["Shouldly", { displayName: "Shouldly", kind: "library" }],
|
|
2727
|
-
["NLog", { displayName: "NLog", kind: "library" }],
|
|
2728
|
-
["log4net", { displayName: "log4net", kind: "library" }],
|
|
2729
|
-
["Newtonsoft.Json", { displayName: "Json.NET", kind: "library" }],
|
|
2730
|
-
["AutoMapper", { displayName: "AutoMapper", kind: "library" }],
|
|
2731
|
-
["Dapper", { displayName: "Dapper", kind: "library" }],
|
|
2732
|
-
["MediatR", { displayName: "MediatR", kind: "library" }]
|
|
2733
|
-
]);
|
|
2734
|
-
var DEPENDENCY_PREFIX_RULES = [
|
|
2735
|
-
{ prefix: "@angular/", classification: { displayName: "Angular", kind: "framework" } },
|
|
2736
|
-
{ prefix: "@nestjs/", classification: { displayName: "NestJS", kind: "framework" } },
|
|
2737
|
-
{ prefix: "@remix-run/", classification: { displayName: "Remix", kind: "framework" } },
|
|
2738
|
-
{ prefix: "@sveltejs/", classification: { displayName: "SvelteKit", kind: "framework" } },
|
|
2739
|
-
{ prefix: "@nuxt/", classification: { displayName: "Nuxt", kind: "framework" } },
|
|
2740
|
-
{ prefix: "@storybook/", classification: { displayName: "Storybook", kind: "library" } },
|
|
2741
|
-
{
|
|
2742
|
-
prefix: "@testing-library/",
|
|
2743
|
-
classification: { displayName: "Testing Library", kind: "library" }
|
|
2744
|
-
},
|
|
2745
|
-
{ prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
|
|
2746
|
-
{ prefix: "@wdio/", classification: { displayName: "WebdriverIO", kind: "library" } },
|
|
2747
|
-
// AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
|
|
2748
|
-
{ prefix: "@aws-cdk/", classification: { displayName: "AWS CDK", kind: "framework" } },
|
|
2749
|
-
{ prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
|
|
2750
|
-
{ prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
|
|
2751
|
-
{ prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
|
|
2752
|
-
{ prefix: "@formatjs/", classification: { displayName: "formatjs", kind: "library" } },
|
|
2753
|
-
{ prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
|
|
2754
|
-
{ prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
|
|
2755
|
-
{ prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
|
|
2756
|
-
{ prefix: "@chakra-ui/", classification: { displayName: "Chakra UI", kind: "library" } },
|
|
2757
|
-
{
|
|
2758
|
-
prefix: "@lwc/",
|
|
2759
|
-
classification: { displayName: "Lightning Web Components", kind: "platform" }
|
|
2760
|
-
},
|
|
2761
|
-
{ prefix: "@salesforce/", classification: { displayName: "Salesforce", kind: "platform" } },
|
|
2762
|
-
{ prefix: "@shopify/", classification: { displayName: "Shopify", kind: "platform" } },
|
|
2763
|
-
{ prefix: "@aws-sdk/", classification: { displayName: "AWS SDK", kind: "library" } },
|
|
2764
|
-
{ prefix: "@google-cloud/", classification: { displayName: "Google Cloud", kind: "library" } },
|
|
2765
|
-
{ prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } },
|
|
2766
|
-
// Groovy — Grails/GORM artifact families (build.gradle artifactIds).
|
|
2767
|
-
{ prefix: "grails-", classification: { displayName: "Grails", kind: "framework" } },
|
|
2768
|
-
{ prefix: "gorm-", classification: { displayName: "GORM", kind: "library" } },
|
|
2769
|
-
// Scala — artifact families published one module per feature (sbt artifactIds).
|
|
2770
|
-
// Enumerating members would go stale on every release; the prefixes are
|
|
2771
|
-
// distinctive enough not to collide with other ecosystems.
|
|
2772
|
-
{ prefix: "http4s-", classification: { displayName: "http4s", kind: "framework" } },
|
|
2773
|
-
{ prefix: "akka-", classification: { displayName: "Akka", kind: "framework" } },
|
|
2774
|
-
{ prefix: "pekko-", classification: { displayName: "Pekko", kind: "framework" } },
|
|
2775
|
-
{ prefix: "zio-", classification: { displayName: "ZIO", kind: "library" } },
|
|
2776
|
-
// Must precede any broader `cats-` rule: cats-effect is its own project.
|
|
2777
|
-
{ prefix: "cats-effect", classification: { displayName: "Cats Effect", kind: "library" } },
|
|
2778
|
-
{ prefix: "doobie-", classification: { displayName: "Doobie", kind: "library" } },
|
|
2779
|
-
{ prefix: "fs2-", classification: { displayName: "FS2", kind: "library" } },
|
|
2780
|
-
{ prefix: "circe-", classification: { displayName: "Circe", kind: "library" } },
|
|
2781
|
-
{ prefix: "pureconfig", classification: { displayName: "PureConfig", kind: "library" } },
|
|
2782
|
-
{ prefix: "kamon-", classification: { displayName: "Kamon", kind: "library" } },
|
|
2783
|
-
{ prefix: "flyway-", classification: { displayName: "Flyway", kind: "library" } },
|
|
2784
|
-
{
|
|
2785
|
-
prefix: "testcontainers-scala",
|
|
2786
|
-
classification: { displayName: "Testcontainers", kind: "library" }
|
|
2787
|
-
},
|
|
2788
|
-
// .NET — Microsoft package families and common test/logging libraries. More
|
|
2789
|
-
// specific prefixes (Blazor) must precede broader ones (ASP.NET Core).
|
|
2790
|
-
{
|
|
2791
|
-
prefix: "Microsoft.AspNetCore.Components.WebAssembly",
|
|
2792
|
-
classification: { displayName: "Blazor", kind: "framework" }
|
|
2793
|
-
},
|
|
2794
|
-
{
|
|
2795
|
-
prefix: "Microsoft.AspNetCore.",
|
|
2796
|
-
classification: { displayName: "ASP.NET Core", kind: "framework" }
|
|
2797
|
-
},
|
|
2798
|
-
{
|
|
2799
|
-
prefix: "Microsoft.EntityFrameworkCore",
|
|
2800
|
-
classification: { displayName: "Entity Framework", kind: "library" }
|
|
2801
|
-
},
|
|
2802
|
-
{
|
|
2803
|
-
prefix: "Microsoft.Azure.Functions",
|
|
2804
|
-
classification: { displayName: "Azure Functions", kind: "framework" }
|
|
2805
|
-
},
|
|
2806
|
-
{
|
|
2807
|
-
prefix: "Microsoft.Azure.WebJobs",
|
|
2808
|
-
classification: { displayName: "Azure Functions", kind: "framework" }
|
|
2809
|
-
},
|
|
2810
|
-
{ prefix: "xunit", classification: { displayName: "xUnit", kind: "library" } },
|
|
2811
|
-
{ prefix: "MSTest.", classification: { displayName: "MSTest", kind: "library" } },
|
|
2812
|
-
{ prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
|
|
2813
|
-
];
|
|
2814
|
-
var MANIFEST_SCOPED_CATALOG = /* @__PURE__ */ new Map([
|
|
2815
|
-
["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])],
|
|
2816
|
-
[
|
|
2817
|
-
"Gemfile",
|
|
2818
|
-
/* @__PURE__ */ new Map([
|
|
2819
|
-
["faker", { displayName: "Faker", kind: "library" }],
|
|
2820
|
-
["rake", { displayName: "Rake", kind: "tool" }]
|
|
2821
|
-
])
|
|
2822
|
-
]
|
|
2823
|
-
]);
|
|
2824
|
-
function classifyDependency(name, manifestBasename) {
|
|
2825
|
-
if (manifestBasename) {
|
|
2826
|
-
const scoped = MANIFEST_SCOPED_CATALOG.get(manifestBasename)?.get(name);
|
|
2827
|
-
if (scoped) return scoped;
|
|
2828
|
-
}
|
|
2829
|
-
const exact = DEPENDENCY_CATALOG.get(name);
|
|
2830
|
-
if (exact) return exact;
|
|
2831
|
-
for (const rule of DEPENDENCY_PREFIX_RULES) {
|
|
2832
|
-
if (name.startsWith(rule.prefix)) return rule.classification;
|
|
2833
|
-
}
|
|
2834
|
-
return void 0;
|
|
2835
|
-
}
|
|
2836
|
-
function pathBasename(filePath) {
|
|
2837
|
-
return filePath.split("/").pop() ?? filePath;
|
|
2838
|
-
}
|
|
2839
|
-
var FILE_SIGNAL_RULES = [
|
|
2840
|
-
{
|
|
2841
|
-
name: ".flowconfig",
|
|
2842
|
-
matches: (f) => pathBasename(f) === ".flowconfig",
|
|
2843
|
-
classification: { displayName: "Flow", kind: "library" }
|
|
2844
|
-
},
|
|
2845
|
-
{
|
|
2846
|
-
name: "cdk.json",
|
|
2847
|
-
matches: (f) => pathBasename(f) === "cdk.json",
|
|
2848
|
-
classification: { displayName: "AWS CDK", kind: "framework" }
|
|
2849
|
-
},
|
|
2850
|
-
{
|
|
2851
|
-
name: ".meteor/release",
|
|
2852
|
-
matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
|
|
2853
|
-
classification: { displayName: "Meteor", kind: "framework" }
|
|
2854
|
-
},
|
|
2855
|
-
{
|
|
2856
|
-
name: "sfdx-project.json",
|
|
2857
|
-
matches: (f) => pathBasename(f) === "sfdx-project.json",
|
|
2858
|
-
classification: { displayName: "Salesforce", kind: "platform" }
|
|
2859
|
-
},
|
|
2860
|
-
{
|
|
2861
|
-
name: "lwc.config.json",
|
|
2862
|
-
matches: (f) => pathBasename(f) === "lwc.config.json",
|
|
2863
|
-
classification: { displayName: "Lightning Web Components", kind: "platform" }
|
|
2864
|
-
},
|
|
2865
|
-
{
|
|
2866
|
-
name: "suitecloud.config.js",
|
|
2867
|
-
matches: (f) => pathBasename(f) === "suitecloud.config.js",
|
|
2868
|
-
classification: { displayName: "SuiteScript", kind: "platform" }
|
|
2869
|
-
},
|
|
2870
|
-
{
|
|
2871
|
-
name: ".github/workflows",
|
|
2872
|
-
matches: (f) => f.startsWith(".github/workflows/"),
|
|
2873
|
-
classification: { displayName: "GitHub Actions", kind: "framework" }
|
|
2874
|
-
},
|
|
2875
|
-
{
|
|
2876
|
-
name: ".circleci",
|
|
2877
|
-
matches: (f) => f.startsWith(".circleci/"),
|
|
2878
|
-
classification: { displayName: "CircleCI", kind: "framework" }
|
|
2879
|
-
},
|
|
2880
|
-
{
|
|
2881
|
-
name: ".gitlab-ci.yml",
|
|
2882
|
-
matches: (f) => f === ".gitlab-ci.yml" || f.startsWith(".gitlab/"),
|
|
2883
|
-
classification: { displayName: "GitLab CI", kind: "framework" }
|
|
2884
|
-
},
|
|
2885
|
-
{
|
|
2886
|
-
name: "bitbucket-pipelines.yml",
|
|
2887
|
-
matches: (f) => f === "bitbucket-pipelines.yml",
|
|
2888
|
-
classification: { displayName: "Bitbucket Pipelines", kind: "framework" }
|
|
2889
|
-
},
|
|
2890
|
-
{
|
|
2891
|
-
name: "azure-pipelines.yml",
|
|
2892
|
-
matches: (f) => f === "azure-pipelines.yml" || f === "azure-pipelines.yaml",
|
|
2893
|
-
classification: { displayName: "Azure Pipelines", kind: "framework" }
|
|
2894
|
-
},
|
|
2895
|
-
{
|
|
2896
|
-
// Jenkins pipelines are Groovy scripts with no manifest at all — the
|
|
2897
|
-
// Jenkinsfile *is* the declaration. Gates CodeNarc's `jenkins`/
|
|
2898
|
-
// `jenkinspipeline` tagged patterns.
|
|
2899
|
-
name: "Jenkinsfile",
|
|
2900
|
-
matches: (f) => {
|
|
2901
|
-
const base = pathBasename(f);
|
|
2902
|
-
return base === "Jenkinsfile" || base.startsWith("Jenkinsfile.");
|
|
2903
|
-
},
|
|
2904
|
-
classification: { displayName: "Jenkins", kind: "tool" }
|
|
2905
|
-
},
|
|
2906
|
-
{
|
|
2907
|
-
// A `sorbet/` directory (typically `sorbet/config` + RBI files) marks a
|
|
2908
|
-
// Sorbet-typed Ruby project even when the gem is pulled in transitively.
|
|
2909
|
-
name: "sorbet/config",
|
|
2910
|
-
matches: (f) => f === "sorbet/config" || f.endsWith("/sorbet/config"),
|
|
2911
|
-
classification: { displayName: "Sorbet", kind: "tool" }
|
|
2912
|
-
},
|
|
2913
|
-
{
|
|
2914
|
-
name: "Rakefile",
|
|
2915
|
-
matches: (f) => {
|
|
2916
|
-
const base = pathBasename(f);
|
|
2917
|
-
return base === "Rakefile" || base === "rakefile" || base.endsWith(".rake");
|
|
2918
|
-
},
|
|
2919
|
-
classification: { displayName: "Rake", kind: "tool" }
|
|
2920
|
-
},
|
|
2921
|
-
{
|
|
2922
|
-
// Play's routes file — `conf/routes`, plus the `conf/*.routes` sub-files a
|
|
2923
|
-
// multi-module app includes from it (Codacy's own website uses
|
|
2924
|
-
// `app/conf/website.routes`, `modules/legacy-api/conf/api.routes`). Nothing
|
|
2925
|
-
// but Play uses this layout, and it is version-independent, unlike the
|
|
2926
|
-
// artifact names. Note `conf/application.conf` is NOT a signal: plain
|
|
2927
|
-
// Typesafe Config uses the same filename without any Play involvement.
|
|
2928
|
-
name: "conf/routes",
|
|
2929
|
-
matches: (f) => /(^|\/)conf\/([^/]+\.)?routes$/.test(f),
|
|
2930
|
-
classification: { displayName: "Play Framework", kind: "framework" }
|
|
2931
|
-
}
|
|
2932
|
-
];
|
|
2933
|
-
function detectFileSignals(files) {
|
|
2934
|
-
const result = [];
|
|
2935
|
-
for (const rule of FILE_SIGNAL_RULES) {
|
|
2936
|
-
const hit = files.find((f) => rule.matches(f));
|
|
2937
|
-
if (!hit) continue;
|
|
2938
|
-
result.push({
|
|
2939
|
-
name: rule.name,
|
|
2940
|
-
displayName: rule.classification.displayName,
|
|
2941
|
-
kind: rule.classification.kind,
|
|
2942
|
-
source: "file",
|
|
2943
|
-
manifestFile: hit
|
|
2944
|
-
});
|
|
2945
|
-
}
|
|
2946
|
-
return result;
|
|
2947
|
-
}
|
|
2948
|
-
var PACKAGE_JSON_SIGNAL_DISPLAY_NAMES = ["ESLint Plugin"];
|
|
2949
|
-
function isEslintPluginName(name) {
|
|
2950
|
-
return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
|
|
2951
|
-
}
|
|
2952
|
-
function detectPackageJsonSignals(content) {
|
|
2953
|
-
let pkg;
|
|
2954
|
-
try {
|
|
2955
|
-
pkg = JSON.parse(content);
|
|
2956
|
-
} catch {
|
|
2957
|
-
return [];
|
|
2958
|
-
}
|
|
2959
|
-
const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
|
|
2960
|
-
if (name && isEslintPluginName(name)) {
|
|
2961
|
-
return [
|
|
2962
|
-
{
|
|
2963
|
-
name,
|
|
2964
|
-
displayName: "ESLint Plugin",
|
|
2965
|
-
kind: "tool",
|
|
2966
|
-
source: "manifest",
|
|
2967
|
-
manifestFile: "package.json"
|
|
2968
|
-
}
|
|
2969
|
-
];
|
|
2970
|
-
}
|
|
2971
|
-
return [];
|
|
2972
|
-
}
|
|
2973
|
-
function getKnownDisplayNames() {
|
|
2974
|
-
const names = /* @__PURE__ */ new Set();
|
|
2975
|
-
for (const { displayName } of DEPENDENCY_CATALOG.values()) names.add(displayName);
|
|
2976
|
-
for (const scoped of MANIFEST_SCOPED_CATALOG.values())
|
|
2977
|
-
for (const { displayName } of scoped.values()) names.add(displayName);
|
|
2978
|
-
for (const rule of DEPENDENCY_PREFIX_RULES) names.add(rule.classification.displayName);
|
|
2979
|
-
for (const rule of FILE_SIGNAL_RULES) names.add(rule.classification.displayName);
|
|
2980
|
-
for (const name of PACKAGE_JSON_SIGNAL_DISPLAY_NAMES) names.add(name);
|
|
2981
|
-
return names;
|
|
2982
|
-
}
|
|
2983
|
-
var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
|
|
2984
|
-
"Dockerfile",
|
|
2985
|
-
"docker-compose.yml",
|
|
2986
|
-
"docker-compose.yaml",
|
|
2987
|
-
"compose.yml",
|
|
2988
|
-
"compose.yaml",
|
|
2989
|
-
"Makefile",
|
|
2990
|
-
"Justfile",
|
|
2991
|
-
"pom.xml",
|
|
2992
|
-
"build.gradle",
|
|
2993
|
-
"build.gradle.kts",
|
|
2994
|
-
"build.sbt",
|
|
2995
|
-
"go.mod",
|
|
2996
|
-
"Cargo.toml",
|
|
2997
|
-
"Gemfile",
|
|
2998
|
-
"Podfile",
|
|
2999
|
-
"Package.swift",
|
|
3000
|
-
"Jenkinsfile",
|
|
3001
|
-
"Vagrantfile",
|
|
3002
|
-
"Procfile",
|
|
3003
|
-
"pubspec.yaml",
|
|
3004
|
-
"mix.exs",
|
|
3005
|
-
"shard.yml",
|
|
3006
|
-
"packages.config",
|
|
3007
|
-
"Directory.Packages.props"
|
|
3008
|
-
]);
|
|
3009
|
-
var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
|
|
3010
|
-
".dockerignore",
|
|
3011
|
-
"Taskfile.yml",
|
|
3012
|
-
"Rakefile",
|
|
3013
|
-
"Gruntfile.js",
|
|
3014
|
-
"Gulpfile.js",
|
|
3015
|
-
"settings.gradle",
|
|
3016
|
-
"settings.gradle.kts",
|
|
3017
|
-
"go.sum",
|
|
3018
|
-
"Cargo.lock",
|
|
3019
|
-
"CLAUDE.md",
|
|
3020
|
-
"AGENTS.md",
|
|
3021
|
-
"agents.md",
|
|
3022
|
-
".cursorrules",
|
|
3023
|
-
"copilot-instructions.md",
|
|
3024
|
-
".travis.yml",
|
|
3025
|
-
".gitlab-ci.yml",
|
|
3026
|
-
"azure-pipelines.yml",
|
|
3027
|
-
"bitbucket-pipelines.yml",
|
|
3028
|
-
"appveyor.yml",
|
|
3029
|
-
"package.json",
|
|
3030
|
-
"requirements.txt",
|
|
3031
|
-
"pyproject.toml",
|
|
3032
|
-
"Pipfile",
|
|
3033
|
-
"composer.json",
|
|
3034
|
-
"tsconfig.json",
|
|
3035
|
-
"jsconfig.json",
|
|
3036
|
-
"serverless.yml",
|
|
3037
|
-
"serverless.yaml",
|
|
3038
|
-
"cdk.json",
|
|
3039
|
-
"pulumi.yaml",
|
|
3040
|
-
"README.md",
|
|
3041
|
-
"CHANGELOG.md",
|
|
3042
|
-
"CONTRIBUTING.md",
|
|
3043
|
-
"LICENSE",
|
|
3044
|
-
"renovate.json",
|
|
3045
|
-
".nvmrc",
|
|
3046
|
-
".node-version",
|
|
3047
|
-
".python-version",
|
|
3048
|
-
".ruby-version",
|
|
3049
|
-
".tool-versions"
|
|
3050
|
-
]);
|
|
3051
|
-
var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
|
|
3052
|
-
var CONFIG_HEURISTIC_PATTERNS = [
|
|
3053
|
-
/^\..*rc$/,
|
|
3054
|
-
// .eslintrc, .prettierrc, etc.
|
|
3055
|
-
/^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
|
|
3056
|
-
// .eslintrc.json, etc.
|
|
3057
|
-
/^.*\.config\.(js|ts|mjs|cjs)$/,
|
|
3058
|
-
// vite.config.ts, jest.config.js, etc.
|
|
3059
|
-
/^\.env(\..+)?$/
|
|
3060
|
-
// .env, .env.local, .env.production
|
|
3061
|
-
];
|
|
3062
|
-
function isNotableFile(filePath) {
|
|
3063
|
-
const basename = filePath.split("/").pop() ?? filePath;
|
|
3064
|
-
const isRoot = !filePath.includes("/");
|
|
3065
|
-
if (NOTABLE_ANY_DEPTH.has(basename)) return true;
|
|
3066
|
-
if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
|
|
3067
|
-
if (/\.(csproj|vbproj|fsproj|sln)$/i.test(basename)) return true;
|
|
3068
|
-
for (const prefix of NOTABLE_PATH_PREFIXES) {
|
|
3069
|
-
if (filePath.startsWith(prefix)) return true;
|
|
3070
|
-
}
|
|
3071
|
-
if (!filePath.includes("/")) {
|
|
3072
|
-
for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
|
|
3073
|
-
if (pattern.test(basename)) return true;
|
|
3074
|
-
}
|
|
3075
|
-
}
|
|
3076
|
-
return false;
|
|
3077
|
-
}
|
|
2341
|
+
// src/discover/classify.ts
|
|
2342
|
+
var import_tooling6 = require("@codacy/tooling");
|
|
2343
|
+
|
|
2344
|
+
// src/discover/ecosystems/npm.ts
|
|
3078
2345
|
function parsePackageJson(content) {
|
|
3079
2346
|
try {
|
|
3080
2347
|
const pkg = JSON.parse(content);
|
|
@@ -3092,6 +2359,166 @@ function parsePackageJson(content) {
|
|
|
3092
2359
|
return [];
|
|
3093
2360
|
}
|
|
3094
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
|
|
3095
2522
|
function parseRequirementLine(rawLine) {
|
|
3096
2523
|
const line = rawLine.trim();
|
|
3097
2524
|
if (!line || line.startsWith("#") || line.startsWith("-")) return void 0;
|
|
@@ -3222,6 +2649,107 @@ function parsePipfile(content) {
|
|
|
3222
2649
|
}
|
|
3223
2650
|
return deps;
|
|
3224
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
|
|
3225
2753
|
function parseGemfile(content) {
|
|
3226
2754
|
const deps = [];
|
|
3227
2755
|
const gemRegex = /^\s*gem\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
|
|
@@ -3231,6 +2759,556 @@ function parseGemfile(content) {
|
|
|
3231
2759
|
}
|
|
3232
2760
|
return deps;
|
|
3233
2761
|
}
|
|
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) {
|
|
2816
|
+
const deps = [];
|
|
2817
|
+
const depRegex = /<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g;
|
|
2818
|
+
let match;
|
|
2819
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
2820
|
+
deps.push({ name: match[1].trim() });
|
|
2821
|
+
}
|
|
2822
|
+
return deps;
|
|
2823
|
+
}
|
|
2824
|
+
function parseBuildGradle(content) {
|
|
2825
|
+
const deps = [];
|
|
2826
|
+
const depRegex = /(?:implementation|testImplementation|testCompileOnly|testRuntimeOnly|api|compileOnly|runtimeOnly|kapt|annotationProcessor)\s*(?:\(\s*)?["']([^"':]+):([^"':]+)(?::([^"']+))?["']/g;
|
|
2827
|
+
let match;
|
|
2828
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
2829
|
+
deps.push({ name: match[2].trim(), version: match[3]?.trim() });
|
|
2830
|
+
}
|
|
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() });
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
return deps;
|
|
2860
|
+
}
|
|
2861
|
+
function parseBuildSbt(content) {
|
|
2862
|
+
const deps = [];
|
|
2863
|
+
const depRegex = /"([^"]+)"\s*%%?\s*"([^"]+)"\s*%(?!%)\s*(?:"([^"]+)")?/g;
|
|
2864
|
+
let match;
|
|
2865
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
2866
|
+
deps.push({ name: match[2], version: match[3] });
|
|
2867
|
+
}
|
|
2868
|
+
return deps;
|
|
2869
|
+
}
|
|
2870
|
+
function parseSbtBuildDefinition(content) {
|
|
2871
|
+
const deps = parseBuildSbt(content);
|
|
2872
|
+
const pluginRegex = /addSbtPlugin\s*\(\s*"([^"]+)"\s*%%?\s*"([^"]+)"/g;
|
|
2873
|
+
let match;
|
|
2874
|
+
while ((match = pluginRegex.exec(content)) !== null) {
|
|
2875
|
+
deps.push({ name: `${match[1]}:${match[2]}` });
|
|
2876
|
+
}
|
|
2877
|
+
return deps;
|
|
2878
|
+
}
|
|
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) {
|
|
3043
|
+
const deps = [];
|
|
3044
|
+
const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
|
|
3045
|
+
let match;
|
|
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
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
return deps;
|
|
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
|
|
3095
|
+
function parseCargoToml(content) {
|
|
3096
|
+
const deps = [];
|
|
3097
|
+
let inDepsSection = false;
|
|
3098
|
+
for (const rawLine of content.split("\n")) {
|
|
3099
|
+
const line = rawLine.trim();
|
|
3100
|
+
if (/^\[((?:dev-|build-)?dependencies)\]/.test(line)) {
|
|
3101
|
+
inDepsSection = true;
|
|
3102
|
+
continue;
|
|
3103
|
+
}
|
|
3104
|
+
if (line.startsWith("[") && !line.includes("dependencies.")) {
|
|
3105
|
+
inDepsSection = false;
|
|
3106
|
+
continue;
|
|
3107
|
+
}
|
|
3108
|
+
if (inDepsSection) {
|
|
3109
|
+
const m = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
|
|
3110
|
+
if (m) {
|
|
3111
|
+
const versionMatch = line.match(/["']([^"']+)["']/);
|
|
3112
|
+
deps.push({ name: m[1], version: versionMatch?.[1] });
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
return deps;
|
|
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
|
|
3144
|
+
function parseComposerJson(content) {
|
|
3145
|
+
try {
|
|
3146
|
+
const pkg = JSON.parse(content);
|
|
3147
|
+
const deps = [];
|
|
3148
|
+
for (const section of ["require", "require-dev"]) {
|
|
3149
|
+
const entries2 = pkg[section];
|
|
3150
|
+
if (entries2 && typeof entries2 === "object") {
|
|
3151
|
+
for (const [name, version] of Object.entries(entries2)) {
|
|
3152
|
+
if (name === "php" || name.startsWith("ext-")) continue;
|
|
3153
|
+
deps.push({ name, version: typeof version === "string" ? version : void 0 });
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
return deps;
|
|
3158
|
+
} catch {
|
|
3159
|
+
return [];
|
|
3160
|
+
}
|
|
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
|
|
3234
3312
|
function parseMixExs(content) {
|
|
3235
3313
|
const deps = [];
|
|
3236
3314
|
const depRegex = /\{:([a-zA-Z_][a-zA-Z0-9_]*)\s*,\s*"([^"]*)"/g;
|
|
@@ -3240,280 +3318,523 @@ function parseMixExs(content) {
|
|
|
3240
3318
|
}
|
|
3241
3319
|
return deps;
|
|
3242
3320
|
}
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
}
|
|
3262
|
-
return deps;
|
|
3263
|
-
}
|
|
3264
|
-
function parsePomXml(content) {
|
|
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
|
|
3338
|
+
function parsePodfile(content) {
|
|
3265
3339
|
const deps = [];
|
|
3266
|
-
const
|
|
3340
|
+
const podRegex = /^\s*pod\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
|
|
3267
3341
|
let match;
|
|
3268
|
-
while ((match =
|
|
3269
|
-
deps.push({ name: match[1]
|
|
3342
|
+
while ((match = podRegex.exec(content)) !== null) {
|
|
3343
|
+
deps.push({ name: match[1], version: match[2] });
|
|
3270
3344
|
}
|
|
3271
3345
|
return deps;
|
|
3272
3346
|
}
|
|
3273
|
-
function
|
|
3347
|
+
function parsePackageSwift(content) {
|
|
3274
3348
|
const deps = [];
|
|
3275
|
-
const
|
|
3349
|
+
const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
|
|
3276
3350
|
let match;
|
|
3277
|
-
while ((match =
|
|
3278
|
-
deps.push({ name: match[
|
|
3351
|
+
while ((match = pkgRegex.exec(content)) !== null) {
|
|
3352
|
+
deps.push({ name: match[1] });
|
|
3279
3353
|
}
|
|
3280
3354
|
return deps;
|
|
3281
3355
|
}
|
|
3282
|
-
|
|
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) {
|
|
3283
3385
|
const deps = [];
|
|
3284
|
-
let
|
|
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
|
+
};
|
|
3285
3393
|
for (const rawLine of content.split("\n")) {
|
|
3286
|
-
const
|
|
3287
|
-
if (
|
|
3288
|
-
|
|
3289
|
-
|
|
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;
|
|
3290
3401
|
continue;
|
|
3291
3402
|
}
|
|
3292
|
-
if (!
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
name: (moduleMatch?.[1] ?? nameMatch?.[1] ?? "").trim(),
|
|
3299
|
-
version: versionMatch?.[1]?.trim()
|
|
3300
|
-
});
|
|
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;
|
|
3301
3409
|
continue;
|
|
3302
3410
|
}
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3411
|
+
if (indent > depIndent && current) {
|
|
3412
|
+
const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
|
|
3413
|
+
if (v) current.version = v[1];
|
|
3306
3414
|
}
|
|
3307
3415
|
}
|
|
3416
|
+
flush();
|
|
3308
3417
|
return deps;
|
|
3309
3418
|
}
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
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;
|
|
3318
3461
|
}
|
|
3319
|
-
function
|
|
3320
|
-
const
|
|
3321
|
-
const
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
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;
|
|
3325
3469
|
}
|
|
3326
|
-
|
|
3470
|
+
for (const { matches, resolved } of BY_PATH) {
|
|
3471
|
+
if (matches(filePath, basename)) return resolved;
|
|
3472
|
+
}
|
|
3473
|
+
return void 0;
|
|
3327
3474
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
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;
|
|
3346
3655
|
}
|
|
3347
|
-
|
|
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
|
+
});
|
|
3348
3687
|
}
|
|
3349
|
-
return
|
|
3688
|
+
return result;
|
|
3350
3689
|
}
|
|
3351
|
-
function
|
|
3690
|
+
function isEslintPluginName(name) {
|
|
3691
|
+
return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
|
|
3692
|
+
}
|
|
3693
|
+
function detectPackageJsonSignals(content) {
|
|
3694
|
+
let pkg;
|
|
3352
3695
|
try {
|
|
3353
|
-
|
|
3354
|
-
const deps = [];
|
|
3355
|
-
for (const section of ["require", "require-dev"]) {
|
|
3356
|
-
const entries2 = pkg[section];
|
|
3357
|
-
if (entries2 && typeof entries2 === "object") {
|
|
3358
|
-
for (const [name, version] of Object.entries(entries2)) {
|
|
3359
|
-
if (name === "php" || name.startsWith("ext-")) continue;
|
|
3360
|
-
deps.push({ name, version: typeof version === "string" ? version : void 0 });
|
|
3361
|
-
}
|
|
3362
|
-
}
|
|
3363
|
-
}
|
|
3364
|
-
return deps;
|
|
3696
|
+
pkg = JSON.parse(content);
|
|
3365
3697
|
} catch {
|
|
3366
3698
|
return [];
|
|
3367
3699
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
while ((match = pkgRegex.exec(content)) !== null) {
|
|
3383
|
-
deps.push({ name: match[1] });
|
|
3384
|
-
}
|
|
3385
|
-
return deps;
|
|
3386
|
-
}
|
|
3387
|
-
function parsePubspecYaml(content) {
|
|
3388
|
-
const deps = [];
|
|
3389
|
-
let inSection = false;
|
|
3390
|
-
let childIndent = -1;
|
|
3391
|
-
for (const rawLine of content.split("\n")) {
|
|
3392
|
-
const trimmed = rawLine.trim();
|
|
3393
|
-
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
3394
|
-
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3395
|
-
if (indent === 0) {
|
|
3396
|
-
inSection = /^(dependencies|dev_dependencies|dependency_overrides):/.test(trimmed);
|
|
3397
|
-
childIndent = -1;
|
|
3398
|
-
continue;
|
|
3399
|
-
}
|
|
3400
|
-
if (!inSection) continue;
|
|
3401
|
-
if (childIndent === -1) childIndent = indent;
|
|
3402
|
-
if (indent !== childIndent) continue;
|
|
3403
|
-
const m = trimmed.match(/^([a-zA-Z0-9_.]+)\s*:(?:\s*["']?\^?([0-9][^"'#\s]*))?/);
|
|
3404
|
-
if (m) deps.push({ name: m[1], version: m[2] });
|
|
3405
|
-
}
|
|
3406
|
-
return deps;
|
|
3407
|
-
}
|
|
3408
|
-
function parseShardYml(content) {
|
|
3409
|
-
const deps = [];
|
|
3410
|
-
let inSection = false;
|
|
3411
|
-
let depIndent = -1;
|
|
3412
|
-
let current;
|
|
3413
|
-
const flush = () => {
|
|
3414
|
-
if (current) deps.push(current);
|
|
3415
|
-
current = void 0;
|
|
3416
|
-
};
|
|
3417
|
-
for (const rawLine of content.split("\n")) {
|
|
3418
|
-
const trimmed = rawLine.trim();
|
|
3419
|
-
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
3420
|
-
const indent = rawLine.length - rawLine.trimStart().length;
|
|
3421
|
-
if (indent === 0) {
|
|
3422
|
-
flush();
|
|
3423
|
-
inSection = /^(dependencies|development_dependencies):/.test(trimmed);
|
|
3424
|
-
depIndent = -1;
|
|
3425
|
-
continue;
|
|
3426
|
-
}
|
|
3427
|
-
if (!inSection) continue;
|
|
3428
|
-
if (depIndent === -1) depIndent = indent;
|
|
3429
|
-
if (indent === depIndent) {
|
|
3430
|
-
flush();
|
|
3431
|
-
const m = trimmed.match(/^([a-zA-Z0-9_-]+)\s*:/);
|
|
3432
|
-
current = m ? { name: m[1] } : void 0;
|
|
3433
|
-
continue;
|
|
3434
|
-
}
|
|
3435
|
-
if (indent > depIndent && current) {
|
|
3436
|
-
const v = trimmed.match(/^version\s*:\s*["']?(?:~>|[~^])?\s*([0-9][^"'\s]*)/);
|
|
3437
|
-
if (v) current.version = v[1];
|
|
3438
|
-
}
|
|
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
|
+
];
|
|
3439
3714
|
}
|
|
3440
|
-
|
|
3441
|
-
return deps;
|
|
3715
|
+
return [];
|
|
3442
3716
|
}
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
let
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
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];
|
|
3454
3733
|
}
|
|
3455
|
-
return
|
|
3456
|
-
}
|
|
3457
|
-
function parseCsproj(content) {
|
|
3458
|
-
return parseXmlDependencyElements(
|
|
3459
|
-
content,
|
|
3460
|
-
/<Package(?:Reference|Version)\s+([^>]+?)\/?>/gi,
|
|
3461
|
-
"Include"
|
|
3462
|
-
);
|
|
3734
|
+
return void 0;
|
|
3463
3735
|
}
|
|
3464
|
-
function
|
|
3465
|
-
return
|
|
3736
|
+
function getKnownDisplayNames() {
|
|
3737
|
+
return new Set(Object.values(import_tooling6.DEPENDENCY_ENTITIES).map((entity) => entity.displayName));
|
|
3466
3738
|
}
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
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"
|
|
3489
3766
|
]);
|
|
3490
|
-
var
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
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"
|
|
3494
3808
|
]);
|
|
3495
|
-
var
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
// than basename is what this rule exists for — the Scala file can be called
|
|
3506
|
-
// anything, and `*.scala` on its own is source code, not a manifest.
|
|
3507
|
-
//
|
|
3508
|
-
// Case-sensitive deliberately: sbt's directory is always lowercase `project`,
|
|
3509
|
-
// while `Project` is an ordinary domain-package name, and reading
|
|
3510
|
-
// `…/domain/Project/Project.scala` as a manifest would be wrong.
|
|
3511
|
-
matches: (filePath) => /(^|\/)project\/[^/]+\.(scala|sbt)$/.test(filePath),
|
|
3512
|
-
parser: parseSbtBuildDefinition
|
|
3513
|
-
}
|
|
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
|
|
3514
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
|
+
}
|
|
3515
3836
|
|
|
3516
|
-
// src/discover.ts
|
|
3837
|
+
// src/discover/index.ts
|
|
3517
3838
|
async function discover(repositoryRoot, options) {
|
|
3518
3839
|
const unfiltered = listRepoFiles(repositoryRoot);
|
|
3519
3840
|
const files = applyExclusions(unfiltered, [...options?.exclude ?? []], []);
|
|
@@ -3566,46 +3887,33 @@ function findNotableFiles(files) {
|
|
|
3566
3887
|
}
|
|
3567
3888
|
return Array.from(notable).sort();
|
|
3568
3889
|
}
|
|
3569
|
-
function resolveManifestParser(filePath) {
|
|
3570
|
-
const basename = filePath.split("/").pop() ?? filePath;
|
|
3571
|
-
const byName = MANIFEST_PARSERS.get(basename);
|
|
3572
|
-
if (byName) return byName;
|
|
3573
|
-
const lowerBasename = basename.toLowerCase();
|
|
3574
|
-
for (const [suffix, parser] of MANIFEST_SUFFIX_PARSERS) {
|
|
3575
|
-
if (lowerBasename.endsWith(suffix)) return parser;
|
|
3576
|
-
}
|
|
3577
|
-
for (const rule of MANIFEST_PATH_PARSERS) {
|
|
3578
|
-
if (rule.matches(filePath, basename)) return rule.parser;
|
|
3579
|
-
}
|
|
3580
|
-
return void 0;
|
|
3581
|
-
}
|
|
3582
3890
|
async function extractDependencies(repositoryRoot, files) {
|
|
3583
3891
|
const allDeps = [];
|
|
3584
3892
|
for (const filePath of files) {
|
|
3585
|
-
const
|
|
3586
|
-
if (!
|
|
3893
|
+
const manifest = resolveManifest(filePath);
|
|
3894
|
+
if (!manifest) continue;
|
|
3587
3895
|
let content;
|
|
3588
3896
|
try {
|
|
3589
3897
|
content = await import_promises2.default.readFile(import_path6.default.join(repositoryRoot, filePath), "utf-8");
|
|
3590
3898
|
} catch {
|
|
3591
3899
|
continue;
|
|
3592
3900
|
}
|
|
3593
|
-
const
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
const classification = classifyDependency(raw.name, basename);
|
|
3597
|
-
if (classification) {
|
|
3901
|
+
for (const raw of manifest.parser(content)) {
|
|
3902
|
+
const entity = classifyDependency(raw.name, manifest.ecosystem);
|
|
3903
|
+
if (entity) {
|
|
3598
3904
|
allDeps.push({
|
|
3599
3905
|
name: raw.name,
|
|
3600
|
-
|
|
3601
|
-
|
|
3906
|
+
key: entity.key,
|
|
3907
|
+
displayName: entity.displayName,
|
|
3908
|
+
kind: entity.kind,
|
|
3602
3909
|
source: "manifest",
|
|
3603
3910
|
manifestFile: filePath,
|
|
3604
|
-
version: raw.version
|
|
3911
|
+
version: raw.version,
|
|
3912
|
+
ecosystem: manifest.ecosystem
|
|
3605
3913
|
});
|
|
3606
3914
|
}
|
|
3607
3915
|
}
|
|
3608
|
-
if (
|
|
3916
|
+
if (filePath.split("/").pop() === "package.json") {
|
|
3609
3917
|
for (const signal of detectPackageJsonSignals(content)) {
|
|
3610
3918
|
allDeps.push({ ...signal, manifestFile: filePath });
|
|
3611
3919
|
}
|
|
@@ -3617,8 +3925,8 @@ function deduplicateDeps(deps) {
|
|
|
3617
3925
|
const seen = /* @__PURE__ */ new Set();
|
|
3618
3926
|
const result = [];
|
|
3619
3927
|
for (const dep of deps) {
|
|
3620
|
-
if (!seen.has(dep.
|
|
3621
|
-
seen.add(dep.
|
|
3928
|
+
if (!seen.has(dep.key)) {
|
|
3929
|
+
seen.add(dep.key);
|
|
3622
3930
|
result.push(dep);
|
|
3623
3931
|
}
|
|
3624
3932
|
}
|
|
@@ -7171,8 +7479,14 @@ var ToolsService = class {
|
|
|
7171
7479
|
|
|
7172
7480
|
// src/api/index.ts
|
|
7173
7481
|
OpenAPI.BASE = (process.env.CODACY_API_BASE_URL || "https://app.codacy.com").replace(/\/$/, "") + "/api/v3";
|
|
7174
|
-
function configureApiToken(
|
|
7175
|
-
|
|
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 };
|
|
7176
7490
|
}
|
|
7177
7491
|
async function fetchAllPages(fetcher) {
|
|
7178
7492
|
const results = [];
|
|
@@ -7265,6 +7579,10 @@ async function listIgnoredFiles(provider, org, repo) {
|
|
|
7265
7579
|
}
|
|
7266
7580
|
|
|
7267
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.";
|
|
7268
7586
|
function deriveApiToolId(tool) {
|
|
7269
7587
|
return tool.prefix ? tool.prefix.replace(/_$/, "") : tool.shortName;
|
|
7270
7588
|
}
|
|
@@ -7319,7 +7637,7 @@ async function detectLanguages(repositoryRoot, precomputedFiles, exclude) {
|
|
|
7319
7637
|
for (const file of files) {
|
|
7320
7638
|
const basename = import_path8.default.basename(file);
|
|
7321
7639
|
const ext = import_path8.default.extname(file).toLowerCase();
|
|
7322
|
-
for (const [lang, config] of Object.entries(
|
|
7640
|
+
for (const [lang, config] of Object.entries(import_tooling7.LANGUAGE_CONFIG)) {
|
|
7323
7641
|
if (config.extensions.includes(ext)) {
|
|
7324
7642
|
detected.add(lang);
|
|
7325
7643
|
} else if (config.files.includes(basename)) {
|
|
@@ -7437,14 +7755,21 @@ async function initDefaultConfig(repositoryRoot, adapters, options) {
|
|
|
7437
7755
|
stats: { totalApiTools: apiTools.length, addedTools, addedUnsupported, codacyYamlApplied }
|
|
7438
7756
|
};
|
|
7439
7757
|
}
|
|
7440
|
-
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters,
|
|
7441
|
-
configureApiToken(
|
|
7758
|
+
async function initRemoteConfig(repositoryRoot, provider, organization, repositoryName, adapters, auth, onRemoteToolsFound, onToolProcessed, onIgnoredFilesFetched) {
|
|
7759
|
+
configureApiToken(auth);
|
|
7760
|
+
const repositoryLabel = `${provider}/${organization}/${repositoryName}`;
|
|
7442
7761
|
const repoInfo = await getRepository(provider, organization, repositoryName);
|
|
7443
7762
|
const remoteTools = await listRepositoryTools(provider, organization, repositoryName);
|
|
7444
7763
|
let ignoredFilesResult = { files: [], totalFetched: 0, capped: false };
|
|
7445
7764
|
try {
|
|
7446
7765
|
ignoredFilesResult = await listIgnoredFiles(provider, organization, repositoryName);
|
|
7447
|
-
} 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
|
+
}
|
|
7448
7773
|
}
|
|
7449
7774
|
onIgnoredFilesFetched?.(ignoredFilesResult.totalFetched, ignoredFilesResult.capped);
|
|
7450
7775
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
@@ -7453,7 +7778,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7453
7778
|
const apiToolByUuid = new Map(allApiTools.map((t) => [t.uuid, t]));
|
|
7454
7779
|
const matchedAdapterCount = remoteTools.filter((t) => adapterByUuid.has(t.uuid)).length;
|
|
7455
7780
|
onRemoteToolsFound?.(remoteTools.length, matchedAdapterCount);
|
|
7456
|
-
const fetchPatterns = async (uuid) => {
|
|
7781
|
+
const fetchPatterns = async (uuid, toolLabel) => {
|
|
7457
7782
|
try {
|
|
7458
7783
|
const configuredPatterns = await listRepositoryToolPatterns(
|
|
7459
7784
|
provider,
|
|
@@ -7468,7 +7793,13 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7468
7793
|
...params ? { parameters: params } : {}
|
|
7469
7794
|
};
|
|
7470
7795
|
});
|
|
7471
|
-
} 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
|
+
}
|
|
7472
7803
|
return [];
|
|
7473
7804
|
}
|
|
7474
7805
|
};
|
|
@@ -7481,7 +7812,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7481
7812
|
if (adapter) {
|
|
7482
7813
|
matchedAdapters++;
|
|
7483
7814
|
const localConfigPath = localConfigs.get(adapter.id);
|
|
7484
|
-
const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
|
|
7815
|
+
const patterns2 = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid, adapter.displayName);
|
|
7485
7816
|
onToolProcessed?.(adapter.displayName, patterns2.length, usesLocalConfig);
|
|
7486
7817
|
tools.push({
|
|
7487
7818
|
toolId: adapter.id,
|
|
@@ -7496,7 +7827,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
7496
7827
|
const apiTool = apiToolByUuid.get(remoteTool.uuid);
|
|
7497
7828
|
if (!apiTool || !isTrackableTool(apiTool)) continue;
|
|
7498
7829
|
const toolId = deriveApiToolId(apiTool);
|
|
7499
|
-
const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid);
|
|
7830
|
+
const patterns = usesLocalConfig ? [] : await fetchPatterns(remoteTool.uuid, apiTool.name ?? toolId);
|
|
7500
7831
|
unsupportedTools++;
|
|
7501
7832
|
onToolProcessed?.(apiTool.name ?? toolId, patterns.length, usesLocalConfig);
|
|
7502
7833
|
tools.push({
|
|
@@ -7603,9 +7934,9 @@ async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, on
|
|
|
7603
7934
|
const discoveryResult = await discover(repositoryRoot, { exclude: excludes.global });
|
|
7604
7935
|
onDiscoveryComplete?.(discoveryResult);
|
|
7605
7936
|
const detectedKeys = /* @__PURE__ */ new Set();
|
|
7606
|
-
for (const lang of discoveryResult.languages) detectedKeys.add(lang);
|
|
7607
|
-
for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.
|
|
7608
|
-
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);
|
|
7609
7940
|
const repoLanguages = new Set(discoveryResult.languages);
|
|
7610
7941
|
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
7611
7942
|
const repoFiles = listRepoFiles(repositoryRoot, excludes.global);
|
|
@@ -7950,7 +8281,7 @@ function parseAutoFilters(raw) {
|
|
|
7950
8281
|
}
|
|
7951
8282
|
|
|
7952
8283
|
// src/index.ts
|
|
7953
|
-
var
|
|
8284
|
+
var import_tooling8 = require("@codacy/tooling");
|
|
7954
8285
|
|
|
7955
8286
|
// src/credentials.ts
|
|
7956
8287
|
var import_crypto = __toESM(require("crypto"));
|
|
@@ -8015,6 +8346,17 @@ async function resolveApiToken() {
|
|
|
8015
8346
|
if (stored) return stored;
|
|
8016
8347
|
throw new Error("No API token found. Set CODACY_API_TOKEN or run 'codacy-analysis login'.");
|
|
8017
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
|
+
}
|
|
8018
8360
|
|
|
8019
8361
|
// src/git-info.ts
|
|
8020
8362
|
var import_child_process5 = require("child_process");
|
|
@@ -8321,6 +8663,7 @@ async function safeReadBody(res) {
|
|
|
8321
8663
|
resolveEngineToAdapter,
|
|
8322
8664
|
resolveHeadCommit,
|
|
8323
8665
|
resolveProxyConfig,
|
|
8666
|
+
resolveRemoteAuth,
|
|
8324
8667
|
resolveRepositoryRoot,
|
|
8325
8668
|
resolveResultsApiBaseUrl,
|
|
8326
8669
|
rotateLogFiles,
|