@rankcli/agent-runtime 0.0.30 → 0.0.32
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.mts +98 -17
- package/dist/index.d.ts +98 -17
- package/dist/index.js +228 -289
- package/dist/index.mjs +227 -289
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -3562,6 +3562,7 @@ __export(index_exports, {
|
|
|
3562
3562
|
sendAlerts: () => sendAlerts,
|
|
3563
3563
|
sendDiscordAlert: () => sendDiscordAlert,
|
|
3564
3564
|
sendSlackAlert: () => sendSlackAlert,
|
|
3565
|
+
servedDir: () => servedDir,
|
|
3565
3566
|
shouldRunAudit: () => shouldRunAudit,
|
|
3566
3567
|
shouldSendAlert: () => shouldSendAlert,
|
|
3567
3568
|
startWizardSession: () => startWizardSession,
|
|
@@ -3622,6 +3623,8 @@ var RECIPES = {
|
|
|
3622
3623
|
jekyll: null,
|
|
3623
3624
|
eleventy: null,
|
|
3624
3625
|
pelican: null,
|
|
3626
|
+
sphinx: null,
|
|
3627
|
+
docusaurus: null,
|
|
3625
3628
|
// Unknown
|
|
3626
3629
|
unknown: null
|
|
3627
3630
|
};
|
|
@@ -7741,234 +7744,161 @@ var ISSUE_DEFINITIONS = {
|
|
|
7741
7744
|
// src/audit/engine.ts
|
|
7742
7745
|
init_http();
|
|
7743
7746
|
|
|
7744
|
-
// src/frameworks/
|
|
7745
|
-
var
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
},
|
|
7754
|
-
|
|
7755
|
-
{
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
},
|
|
7761
|
-
|
|
7762
|
-
{
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
},
|
|
7768
|
-
//
|
|
7769
|
-
{
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
},
|
|
7775
|
-
|
|
7776
|
-
{
|
|
7777
|
-
framework: "sveltekit",
|
|
7778
|
-
language: "typescript",
|
|
7779
|
-
patterns: [/__sveltekit/, /_app\/immutable/, /svelte-kit/],
|
|
7780
|
-
confidence: "high"
|
|
7781
|
-
},
|
|
7782
|
-
// Svelte (standalone)
|
|
7783
|
-
{
|
|
7784
|
-
framework: "svelte",
|
|
7785
|
-
language: "javascript",
|
|
7786
|
-
patterns: [/svelte-\w+/, /__svelte/],
|
|
7787
|
-
confidence: "medium"
|
|
7788
|
-
},
|
|
7789
|
-
// Remix
|
|
7790
|
-
{
|
|
7791
|
-
framework: "remix",
|
|
7792
|
-
language: "typescript",
|
|
7793
|
-
patterns: [/__remix/, /data-remix/],
|
|
7794
|
-
confidence: "high"
|
|
7795
|
-
},
|
|
7796
|
-
// Solid.js
|
|
7797
|
-
{
|
|
7798
|
-
framework: "solidjs",
|
|
7799
|
-
language: "typescript",
|
|
7800
|
-
patterns: [/data-solid/, /_solid/],
|
|
7801
|
-
confidence: "high"
|
|
7802
|
-
},
|
|
7803
|
-
// Qwik
|
|
7804
|
-
{
|
|
7805
|
-
framework: "qwik",
|
|
7806
|
-
language: "typescript",
|
|
7807
|
-
patterns: [/q:container/, /q:base/, /q:version/, /q:id=/, /qwik/],
|
|
7808
|
-
confidence: "high"
|
|
7809
|
-
},
|
|
7810
|
-
// Fresh (Deno)
|
|
7811
|
-
{
|
|
7812
|
-
framework: "fresh",
|
|
7813
|
-
language: "typescript",
|
|
7814
|
-
patterns: [/_frsh\//, /data-fresh/],
|
|
7815
|
-
confidence: "high"
|
|
7816
|
-
},
|
|
7817
|
-
// React (generic - lower priority)
|
|
7747
|
+
// src/frameworks/signals.ts
|
|
7748
|
+
var WEIGHT = { DECISIVE: 100, STRONG: 45, WEAK: 15 };
|
|
7749
|
+
var SCORE_FLOOR = 45;
|
|
7750
|
+
var SIGNALS = [
|
|
7751
|
+
// ---- Meta-frameworks: JavaScript ------------------------------------------------
|
|
7752
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-build-dir", html: /\/_next\/(static|image|data)\// },
|
|
7753
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-data", html: /__NEXT_DATA__|self\.__next_f/ },
|
|
7754
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-mount", html: /<div id=["']__next["']/ },
|
|
7755
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-powered-by", header: /^x-powered-by: next\.js/ },
|
|
7756
|
+
{ framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-build-dir", html: /\/_nuxt\// },
|
|
7757
|
+
{ framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-payload", html: /__NUXT__|__NUXT_DATA__|<div id="__nuxt"/ },
|
|
7758
|
+
{ framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-powered-by", header: /^x-powered-by: nuxt/ },
|
|
7759
|
+
{ framework: "astro", language: "typescript", weight: WEIGHT.DECISIVE, name: "astro-build-dir", html: /\/_astro\// },
|
|
7760
|
+
{ framework: "astro", language: "typescript", weight: WEIGHT.DECISIVE, name: "astro-island", html: /<astro-island|astro-slot|data-astro-(cid|source-file|transition)/ },
|
|
7761
|
+
{ framework: "astro", language: "typescript", weight: WEIGHT.STRONG, name: "astro-generator", html: /name=["']?generator["']?[^>]*content=["']?Astro/i },
|
|
7762
|
+
{ framework: "sveltekit", language: "typescript", weight: WEIGHT.DECISIVE, name: "sveltekit-immutable", html: /\/_app\/immutable\// },
|
|
7763
|
+
{ framework: "sveltekit", language: "typescript", weight: WEIGHT.DECISIVE, name: "sveltekit-runtime", html: /__sveltekit|kit\.start\(/ },
|
|
7764
|
+
{ framework: "remix", language: "typescript", weight: WEIGHT.DECISIVE, name: "remix-context", html: /__remixContext|__remixManifest|__remixRouteModules/ },
|
|
7765
|
+
{ framework: "remix", language: "typescript", weight: WEIGHT.STRONG, name: "remix-build-dir", html: /\/build\/_shared\/|\/build\/entry\.client-/ },
|
|
7766
|
+
{ framework: "gatsby", language: "javascript", weight: WEIGHT.DECISIVE, name: "gatsby-runtime", html: /___gatsby|window\.___chunkMapping/ },
|
|
7767
|
+
{ framework: "gatsby", language: "javascript", weight: WEIGHT.STRONG, name: "gatsby-page-data", html: /\/page-data\/(app-data\.json|sq\/)/ },
|
|
7768
|
+
{ framework: "qwik", language: "typescript", weight: WEIGHT.DECISIVE, name: "qwik-container", html: /q:container|q:base|q:version|qwikloader/ },
|
|
7769
|
+
{ framework: "fresh", language: "typescript", weight: WEIGHT.DECISIVE, name: "fresh-build-dir", html: /\/_frsh\// },
|
|
7770
|
+
{ framework: "solidjs", language: "typescript", weight: WEIGHT.STRONG, name: "solid-hydration", html: /_\$HY\b|data-hk=["']/ },
|
|
7771
|
+
// ---- Base UI libraries: weaker by nature, and that is the point -----------------
|
|
7772
|
+
{ framework: "angular", language: "typescript", weight: WEIGHT.DECISIVE, name: "angular-version", html: /ng-version=["']/ },
|
|
7773
|
+
{ framework: "angular", language: "typescript", weight: WEIGHT.DECISIVE, name: "angular-content-attr", html: /_ngcontent-|_nghost-/ },
|
|
7774
|
+
{ framework: "react", language: "javascript", weight: WEIGHT.STRONG, name: "react-root-marker", html: /data-reactroot|__reactContainer\$|__REACT_DEVTOOLS/ },
|
|
7775
|
+
// A Vite-built SPA: an empty mount point plus Vite's hashed entry bundle. Two weak facts
|
|
7776
|
+
// that together mean something; either one alone does not.
|
|
7777
|
+
{ framework: "react", language: "javascript", weight: WEIGHT.WEAK, name: "react-empty-root", html: /<div id="root"><\/div>/ },
|
|
7778
|
+
{ framework: "react", language: "javascript", weight: WEIGHT.WEAK, name: "react-refresh", html: /RefreshRuntime|react-refresh/ },
|
|
7818
7779
|
{
|
|
7819
7780
|
framework: "react",
|
|
7820
7781
|
language: "javascript",
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
//
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
{
|
|
7829
|
-
framework: "vue",
|
|
7830
|
-
language: "javascript",
|
|
7831
|
-
patterns: [/data-v-[a-f0-9]+/, /v-cloak/, /__vue/],
|
|
7832
|
-
confidence: "medium"
|
|
7833
|
-
},
|
|
7834
|
-
// Angular
|
|
7835
|
-
{
|
|
7836
|
-
framework: "angular",
|
|
7837
|
-
language: "typescript",
|
|
7838
|
-
patterns: [/ng-version/, /_ngcontent/, /ng-\w+=/, /\[ng/],
|
|
7839
|
-
confidence: "high"
|
|
7840
|
-
},
|
|
7841
|
-
// ============ Hypermedia Frameworks ============
|
|
7842
|
-
// HTMX
|
|
7843
|
-
{
|
|
7844
|
-
framework: "htmx",
|
|
7845
|
-
language: "unknown",
|
|
7846
|
-
patterns: [/hx-get/, /hx-post/, /hx-trigger/, /hx-swap/, /htmx\.org/],
|
|
7847
|
-
confidence: "high"
|
|
7848
|
-
},
|
|
7849
|
-
// Hotwire/Turbo
|
|
7850
|
-
{
|
|
7851
|
-
framework: "hotwire",
|
|
7852
|
-
language: "ruby",
|
|
7853
|
-
patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/],
|
|
7854
|
-
confidence: "high"
|
|
7855
|
-
},
|
|
7856
|
-
// ============ PHP Frameworks ============
|
|
7857
|
-
// WordPress
|
|
7858
|
-
{
|
|
7859
|
-
framework: "wordpress",
|
|
7860
|
-
language: "php",
|
|
7861
|
-
// Technical signatures only. These patterns run over the whole page, so the old
|
|
7862
|
-
// /wordpress/i matched the word anywhere in the copy: rankcli.dev's own "detects
|
|
7863
|
-
// Rails, Django, Laravel, and WordPress" line was enough to detect its React SPA as
|
|
7864
|
-
// WordPress and then tell it to install an SEO plugin. A generator meta tag is the
|
|
7865
|
-
// one place the page says it about itself.
|
|
7866
|
-
patterns: [/wp-content/, /wp-includes/, /wp-json/, /name=["']generator["'][^>]*content=["']WordPress/i],
|
|
7867
|
-
confidence: "high"
|
|
7868
|
-
},
|
|
7869
|
-
// Laravel
|
|
7870
|
-
{
|
|
7871
|
-
framework: "laravel",
|
|
7872
|
-
language: "php",
|
|
7873
|
-
// /laravel/i had the same problem as WordPress above - it matched the word in prose.
|
|
7874
|
-
patterns: [/laravel_session/, /XSRF-TOKEN/, /@csrf/],
|
|
7875
|
-
confidence: "medium"
|
|
7876
|
-
},
|
|
7877
|
-
// Symfony
|
|
7878
|
-
{
|
|
7879
|
-
framework: "symfony",
|
|
7880
|
-
language: "php",
|
|
7881
|
-
// Likewise /symfony/, which matched any lowercase mention.
|
|
7882
|
-
patterns: [/sf2/, /_sf_/, /profiler\.css/],
|
|
7883
|
-
confidence: "medium"
|
|
7884
|
-
},
|
|
7885
|
-
// ============ Ruby Frameworks ============
|
|
7886
|
-
// Rails
|
|
7887
|
-
{
|
|
7888
|
-
framework: "rails",
|
|
7889
|
-
language: "ruby",
|
|
7890
|
-
patterns: [/csrf-token/, /turbolinks/, /data-turbo/, /rails-ujs/, /action-cable/],
|
|
7891
|
-
confidence: "medium"
|
|
7892
|
-
},
|
|
7893
|
-
// ============ Python Frameworks ============
|
|
7894
|
-
// Django
|
|
7895
|
-
{
|
|
7896
|
-
framework: "django",
|
|
7897
|
-
language: "python",
|
|
7898
|
-
patterns: [/csrfmiddlewaretoken/, /django/, /__debug__\//],
|
|
7899
|
-
confidence: "medium"
|
|
7900
|
-
},
|
|
7901
|
-
// Flask
|
|
7902
|
-
{
|
|
7903
|
-
framework: "flask",
|
|
7904
|
-
language: "python",
|
|
7905
|
-
patterns: [/flask/, /werkzeug/i],
|
|
7906
|
-
confidence: "low"
|
|
7907
|
-
},
|
|
7908
|
-
// FastAPI
|
|
7909
|
-
{
|
|
7910
|
-
framework: "fastapi",
|
|
7911
|
-
language: "python",
|
|
7912
|
-
patterns: [/fastapi/, /openapi\.json/],
|
|
7913
|
-
confidence: "low"
|
|
7914
|
-
},
|
|
7915
|
-
// ============ Java Frameworks ============
|
|
7916
|
-
// Spring Boot
|
|
7917
|
-
{
|
|
7918
|
-
framework: "spring",
|
|
7919
|
-
language: "java",
|
|
7920
|
-
// /th:/ matched "width:" and "height:" in inline CSS, which made almost every styled
|
|
7921
|
-
// page on the web a Spring Boot app - rankcli.dev included, once WordPress stopped
|
|
7922
|
-
// matching first. Thymeleaf attributes are what th: actually means.
|
|
7923
|
-
patterns: [/\sth:[a-z-]+=/, /thymeleaf/, /_csrf/],
|
|
7924
|
-
confidence: "medium"
|
|
7925
|
-
},
|
|
7926
|
-
// ============ C# Frameworks ============
|
|
7927
|
-
// ASP.NET Core
|
|
7928
|
-
{
|
|
7929
|
-
framework: "aspnet",
|
|
7930
|
-
language: "csharp",
|
|
7931
|
-
patterns: [/aspnetcore/, /__RequestVerificationToken/, /_framework\/blazor/],
|
|
7932
|
-
confidence: "medium"
|
|
7933
|
-
},
|
|
7934
|
-
// ============ Elixir Frameworks ============
|
|
7935
|
-
// Phoenix
|
|
7936
|
-
{
|
|
7937
|
-
framework: "phoenix",
|
|
7938
|
-
language: "elixir",
|
|
7939
|
-
patterns: [/phx-/, /live-socket/, /data-phx/],
|
|
7940
|
-
confidence: "high"
|
|
7941
|
-
},
|
|
7942
|
-
// ============ Static Site Generators ============
|
|
7943
|
-
// Hugo
|
|
7944
|
-
{
|
|
7945
|
-
framework: "hugo",
|
|
7946
|
-
language: "go",
|
|
7947
|
-
patterns: [/name=["']generator["'][^>]*content=["']Hugo/i],
|
|
7948
|
-
confidence: "medium"
|
|
7949
|
-
},
|
|
7950
|
-
// Jekyll
|
|
7951
|
-
{
|
|
7952
|
-
framework: "jekyll",
|
|
7953
|
-
language: "ruby",
|
|
7954
|
-
patterns: [/name=["']generator["'][^>]*content=["']Jekyll/i],
|
|
7955
|
-
confidence: "medium"
|
|
7782
|
+
weight: WEIGHT.STRONG,
|
|
7783
|
+
name: "react-vite-spa",
|
|
7784
|
+
// Vite's entry chunk plus React's conventional mount point. Medium confidence by
|
|
7785
|
+
// construction: #app instead of #root stays unknown, because Vue, Solid and others use it.
|
|
7786
|
+
// Not `<div id="root"></div>`: a prerendered SPA has content inside its mount point, which
|
|
7787
|
+
// is what RankCLI's own site does and what the first version of this signal missed.
|
|
7788
|
+
allHtml: [/<script[^>]+src=["'][^"']+\.js["']/, /<div id="root"[\s>]/]
|
|
7956
7789
|
},
|
|
7957
|
-
|
|
7958
|
-
{
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
},
|
|
7964
|
-
|
|
7965
|
-
{
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
}
|
|
7790
|
+
{ framework: "vue", language: "javascript", weight: WEIGHT.DECISIVE, name: "vitepress-runtime", html: /__VP_HASH_MAP__|__VP_SITE_DATA__/ },
|
|
7791
|
+
{ framework: "vue", language: "javascript", weight: WEIGHT.DECISIVE, name: "vue-scoped-style", html: /data-v-[0-9a-f]{6,10}\b/ },
|
|
7792
|
+
{ framework: "vue", language: "javascript", weight: WEIGHT.WEAK, name: "vue-cloak", html: /v-cloak|<div id="app"><\/div>/ },
|
|
7793
|
+
{ framework: "svelte", language: "javascript", weight: WEIGHT.WEAK, name: "svelte-class", html: /class=["'][^"']*svelte-[0-9a-z]{6}/ },
|
|
7794
|
+
// ---- Ruby -----------------------------------------------------------------------
|
|
7795
|
+
// csrf-token alone is emitted by Rails, Laravel, Phoenix and Django. csrf-param is Rails.
|
|
7796
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.DECISIVE, name: "rails-csrf-param", html: /name=["']csrf-param["']/ },
|
|
7797
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-ujs", html: /rails-ujs|turbolinks|data-remote=["']true["']|action_cable|actioncable/ },
|
|
7798
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-session-cookie", header: /^set-cookie: [^;]*_session=/ },
|
|
7799
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-app-server", header: /^(server|x-powered-by): (puma|passenger|unicorn|phusion)/ },
|
|
7800
|
+
{ framework: "hotwire", language: "ruby", weight: WEIGHT.STRONG, name: "turbo-elements", html: /<turbo-frame|<turbo-stream|data-turbo-track/ },
|
|
7801
|
+
// ---- Python ---------------------------------------------------------------------
|
|
7802
|
+
{ framework: "django", language: "python", weight: WEIGHT.DECISIVE, name: "django-csrf-input", html: /name=["']csrfmiddlewaretoken["']/ },
|
|
7803
|
+
{ framework: "django", language: "python", weight: WEIGHT.DECISIVE, name: "django-csrf-cookie", header: /^set-cookie: csrftoken=/ },
|
|
7804
|
+
{ framework: "django", language: "python", weight: WEIGHT.STRONG, name: "django-admin-static", html: /\/static\/admin\/(css|js)\// },
|
|
7805
|
+
{ framework: "django", language: "python", weight: WEIGHT.WEAK, name: "django-wsgi-server", header: /^(server|x-powered-by): (gunicorn|uvicorn|daphne)/ },
|
|
7806
|
+
{ framework: "flask", language: "python", weight: WEIGHT.STRONG, name: "flask-server", header: /^server: werkzeug/ },
|
|
7807
|
+
{ framework: "fastapi", language: "python", weight: WEIGHT.STRONG, name: "fastapi-docs", html: /swagger-ui-bundle\.js[\s\S]{0,200}openapi\.json/ },
|
|
7808
|
+
// ---- PHP ------------------------------------------------------------------------
|
|
7809
|
+
{ framework: "wordpress", language: "php", weight: WEIGHT.DECISIVE, name: "wp-paths", html: /\/wp-content\/|\/wp-includes\/|\/wp-json\// },
|
|
7810
|
+
{ framework: "wordpress", language: "php", weight: WEIGHT.DECISIVE, name: "wp-generator", html: /name=["']?generator["']?[^>]*content=["']?WordPress/i },
|
|
7811
|
+
{ framework: "wordpress", language: "php", weight: WEIGHT.STRONG, name: "wp-powered-by", header: /^x-powered-by: wordpress/ },
|
|
7812
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.DECISIVE, name: "laravel-session-cookie", header: /^set-cookie: laravel_session=/ },
|
|
7813
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.STRONG, name: "laravel-xsrf-cookie", header: /^set-cookie: XSRF-TOKEN=/i },
|
|
7814
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.STRONG, name: "laravel-livewire", html: /livewire|wire:(click|model|submit)/i },
|
|
7815
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.WEAK, name: "laravel-vite-build", html: /\/build\/assets\/app-[A-Za-z0-9_-]{8}\.js/ },
|
|
7816
|
+
{ framework: "symfony", language: "php", weight: WEIGHT.DECISIVE, name: "symfony-profiler", html: /\/_profiler\/|sf-toolbar|sf-dump/ },
|
|
7817
|
+
{ framework: "symfony", language: "php", weight: WEIGHT.STRONG, name: "symfony-cookie", header: /^set-cookie: (sfsession|symfony)/i },
|
|
7818
|
+
// ---- Elixir ---------------------------------------------------------------------
|
|
7819
|
+
{ framework: "phoenix", language: "elixir", weight: WEIGHT.DECISIVE, name: "phoenix-liveview", html: /phx-(click|submit|change|value|hook|socket|main|track-static)/ },
|
|
7820
|
+
{ framework: "phoenix", language: "elixir", weight: WEIGHT.STRONG, name: "phoenix-cowboy", header: /^server: cowboy/ },
|
|
7821
|
+
// ---- Java / C# / Go -------------------------------------------------------------
|
|
7822
|
+
{ framework: "spring", language: "java", weight: WEIGHT.STRONG, name: "thymeleaf-attr", html: /\sth:[a-z-]+=/ },
|
|
7823
|
+
{ framework: "spring", language: "java", weight: WEIGHT.STRONG, name: "spring-server", header: /^server: (apache-coyote|tomcat|jetty)/ },
|
|
7824
|
+
{ framework: "aspnet", language: "csharp", weight: WEIGHT.DECISIVE, name: "aspnet-verification-token", html: /__RequestVerificationToken|__VIEWSTATE/ },
|
|
7825
|
+
{ framework: "aspnet", language: "csharp", weight: WEIGHT.STRONG, name: "aspnet-server", header: /^(server: kestrel|x-powered-by: asp\.net|x-aspnet-version:)/ },
|
|
7826
|
+
// "gin" used to be tested as a substring of the Server header, which matches nginx - the
|
|
7827
|
+
// single most common web server there is. Anchored to the whole value now.
|
|
7828
|
+
{ framework: "go", language: "go", weight: WEIGHT.STRONG, name: "go-server", header: /^server: (gin|echo|fiber|gorilla)(\/|$| )/ },
|
|
7829
|
+
// ---- Hypermedia -----------------------------------------------------------------
|
|
7830
|
+
{ framework: "htmx", language: "unknown", weight: WEIGHT.STRONG, name: "htmx-attrs", html: /\shx-(get|post|put|delete|trigger|swap|target)=/ },
|
|
7831
|
+
{ framework: "htmx", language: "unknown", weight: WEIGHT.DECISIVE, name: "htmx-script", html: /htmx(\.min)?\.js|unpkg\.com\/htmx/ },
|
|
7832
|
+
// ---- Static site generators: they announce themselves -----------------------------
|
|
7833
|
+
{ framework: "hugo", language: "go", weight: WEIGHT.DECISIVE, name: "hugo-generator", html: /name=["']?generator["']?[^>]*content=["']?Hugo/i },
|
|
7834
|
+
{ framework: "hugo", language: "go", weight: WEIGHT.STRONG, name: "hugo-generator-header", header: /^x-generator: hugo/ },
|
|
7835
|
+
{ framework: "jekyll", language: "ruby", weight: WEIGHT.DECISIVE, name: "jekyll-generator", html: /name=["']?generator["']?[^>]*content=["']?Jekyll/i },
|
|
7836
|
+
{ framework: "eleventy", language: "javascript", weight: WEIGHT.DECISIVE, name: "eleventy-generator", html: /name=["']?generator["']?[^>]*content=["']?Eleventy/i },
|
|
7837
|
+
{ framework: "pelican", language: "python", weight: WEIGHT.DECISIVE, name: "pelican-generator", html: /name=["']?generator["']?[^>]*content=["']?Pelican/i },
|
|
7838
|
+
// Sphinx: what nearly every Python project's documentation is built with, and the shape
|
|
7839
|
+
// flask.palletsprojects.com turned out to be. Structural markers only - not the word
|
|
7840
|
+
// "sphinx", which appears in plenty of prose.
|
|
7841
|
+
// Validated against docusaurus.io, jestjs.io and redux.js.org - all three carry the mount
|
|
7842
|
+
// point and the skip-to-content id. Deliberately not the word "docusaurus", which appears on
|
|
7843
|
+
// deno.com purely as a link in a logo list; that near-miss is why this needed a real marker.
|
|
7844
|
+
{ framework: "docusaurus", language: "typescript", weight: WEIGHT.DECISIVE, name: "docusaurus-root", html: /id=["']__docusaurus/ },
|
|
7845
|
+
{ framework: "docusaurus", language: "typescript", weight: WEIGHT.STRONG, name: "docusaurus-skip-link", html: /__docusaurus_skipToContent/ },
|
|
7846
|
+
{ framework: "sphinx", language: "python", weight: WEIGHT.DECISIVE, name: "sphinx-doc-options", html: /_static\/documentation_options\.js/ },
|
|
7847
|
+
{ framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "sphinx-theme-markup", html: /class=["'][^"']*sphinxsidebar|sphinx-highlight|_static\/searchtools\.js/ },
|
|
7848
|
+
{ framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "docutils-generator", html: /name=["']?generator["']?[^>]*content=["']?Docutils/i }
|
|
7971
7849
|
];
|
|
7850
|
+
function headerLines(headers) {
|
|
7851
|
+
if (!headers) return "";
|
|
7852
|
+
return Object.entries(headers).map(([k, v]) => `${k.toLowerCase()}: ${String(v).toLowerCase()}`).join("\n");
|
|
7853
|
+
}
|
|
7854
|
+
function detectFromEvidence(input) {
|
|
7855
|
+
const html = input.html ?? "";
|
|
7856
|
+
const headers = headerLines(input.headers);
|
|
7857
|
+
const byFramework = /* @__PURE__ */ new Map();
|
|
7858
|
+
for (const signal of SIGNALS) {
|
|
7859
|
+
const headerHit = signal.header && headers && new RegExp(
|
|
7860
|
+
signal.header.source,
|
|
7861
|
+
signal.header.flags.includes("m") ? signal.header.flags : `${signal.header.flags}m`
|
|
7862
|
+
).test(headers);
|
|
7863
|
+
const allHit = signal.allHtml && html && signal.allHtml.every((re) => re.test(html));
|
|
7864
|
+
const hit = signal.html && html && signal.html.test(html) || headerHit || allHit;
|
|
7865
|
+
if (!hit) continue;
|
|
7866
|
+
const existing = byFramework.get(signal.framework);
|
|
7867
|
+
if (existing) {
|
|
7868
|
+
existing.score += signal.weight;
|
|
7869
|
+
existing.matched.push(signal.name);
|
|
7870
|
+
} else {
|
|
7871
|
+
byFramework.set(signal.framework, {
|
|
7872
|
+
framework: signal.framework,
|
|
7873
|
+
language: signal.language,
|
|
7874
|
+
score: signal.weight,
|
|
7875
|
+
matched: [signal.name]
|
|
7876
|
+
});
|
|
7877
|
+
}
|
|
7878
|
+
}
|
|
7879
|
+
const ranked = [...byFramework.values()].sort((a, b) => b.score - a.score || a.framework.localeCompare(b.framework));
|
|
7880
|
+
const best = ranked[0];
|
|
7881
|
+
if (!best || best.score < SCORE_FLOOR) {
|
|
7882
|
+
return {
|
|
7883
|
+
framework: "unknown",
|
|
7884
|
+
language: "unknown",
|
|
7885
|
+
confidence: "low",
|
|
7886
|
+
score: best?.score ?? 0,
|
|
7887
|
+
matched: best?.matched ?? [],
|
|
7888
|
+
alternatives: ranked
|
|
7889
|
+
};
|
|
7890
|
+
}
|
|
7891
|
+
return {
|
|
7892
|
+
framework: best.framework,
|
|
7893
|
+
language: best.language,
|
|
7894
|
+
confidence: best.score >= WEIGHT.DECISIVE ? "high" : "medium",
|
|
7895
|
+
score: best.score,
|
|
7896
|
+
matched: best.matched,
|
|
7897
|
+
alternatives: ranked.slice(1)
|
|
7898
|
+
};
|
|
7899
|
+
}
|
|
7900
|
+
|
|
7901
|
+
// src/frameworks/detector.ts
|
|
7972
7902
|
var PACKAGE_DEPENDENCIES = [
|
|
7973
7903
|
// Meta-frameworks (check first - more specific)
|
|
7974
7904
|
{ framework: "nextjs", language: "typescript", packages: ["next"], meta_framework: "react" },
|
|
@@ -8018,19 +7948,14 @@ var ELIXIR_DEPENDENCIES = [
|
|
|
8018
7948
|
{ framework: "phoenix", language: "elixir", packages: [":phoenix", "phoenix"] }
|
|
8019
7949
|
];
|
|
8020
7950
|
function detectFromHtml(html) {
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
};
|
|
8030
|
-
}
|
|
8031
|
-
}
|
|
8032
|
-
}
|
|
8033
|
-
return null;
|
|
7951
|
+
const result = detectFromEvidence({ html });
|
|
7952
|
+
if (result.framework === "unknown") return null;
|
|
7953
|
+
return {
|
|
7954
|
+
framework: result.framework,
|
|
7955
|
+
language: result.language,
|
|
7956
|
+
confidence: result.confidence,
|
|
7957
|
+
detected_from: "html"
|
|
7958
|
+
};
|
|
8034
7959
|
}
|
|
8035
7960
|
function detectFromPackageJson(packageJson) {
|
|
8036
7961
|
const allDeps = {
|
|
@@ -8172,52 +8097,14 @@ function detectFromMixExs(mixContent) {
|
|
|
8172
8097
|
return null;
|
|
8173
8098
|
}
|
|
8174
8099
|
function detectFromHeaders(headers) {
|
|
8175
|
-
const
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
}
|
|
8184
|
-
if (poweredBy.includes("remix")) {
|
|
8185
|
-
return { framework: "remix", language: "typescript", confidence: "high", detected_from: "headers" };
|
|
8186
|
-
}
|
|
8187
|
-
if (poweredBy.includes("phusion") || poweredBy.includes("passenger") || poweredBy.includes("puma") || poweredBy.includes("unicorn")) {
|
|
8188
|
-
return { framework: "rails", language: "ruby", confidence: "medium", detected_from: "headers" };
|
|
8189
|
-
}
|
|
8190
|
-
if (poweredBy.includes("gunicorn") || poweredBy.includes("uvicorn") || poweredBy.includes("daphne")) {
|
|
8191
|
-
return null;
|
|
8192
|
-
}
|
|
8193
|
-
if (poweredBy.includes("php")) {
|
|
8194
|
-
return null;
|
|
8195
|
-
}
|
|
8196
|
-
if (server.includes("tomcat") || server.includes("jetty") || poweredBy.includes("spring")) {
|
|
8197
|
-
return { framework: "spring", language: "java", confidence: "medium", detected_from: "headers" };
|
|
8198
|
-
}
|
|
8199
|
-
if (poweredBy.includes("asp.net") || server.includes("kestrel")) {
|
|
8200
|
-
return { framework: "aspnet", language: "csharp", confidence: "high", detected_from: "headers" };
|
|
8201
|
-
}
|
|
8202
|
-
if (poweredBy.includes("phoenix") || poweredBy.includes("cowboy")) {
|
|
8203
|
-
return { framework: "phoenix", language: "elixir", confidence: "medium", detected_from: "headers" };
|
|
8204
|
-
}
|
|
8205
|
-
if (server.includes("gin") || server.includes("echo") || server.includes("fiber")) {
|
|
8206
|
-
return { framework: "go", language: "go", confidence: "medium", detected_from: "headers" };
|
|
8207
|
-
}
|
|
8208
|
-
if (generator.includes("hugo")) {
|
|
8209
|
-
return { framework: "hugo", language: "go", confidence: "high", detected_from: "headers" };
|
|
8210
|
-
}
|
|
8211
|
-
if (generator.includes("jekyll")) {
|
|
8212
|
-
return { framework: "jekyll", language: "ruby", confidence: "high", detected_from: "headers" };
|
|
8213
|
-
}
|
|
8214
|
-
if (generator.includes("eleventy") || generator.includes("11ty")) {
|
|
8215
|
-
return { framework: "eleventy", language: "javascript", confidence: "high", detected_from: "headers" };
|
|
8216
|
-
}
|
|
8217
|
-
if (generator.includes("pelican")) {
|
|
8218
|
-
return { framework: "pelican", language: "python", confidence: "high", detected_from: "headers" };
|
|
8219
|
-
}
|
|
8220
|
-
return null;
|
|
8100
|
+
const result = detectFromEvidence({ headers });
|
|
8101
|
+
if (result.framework === "unknown") return null;
|
|
8102
|
+
return {
|
|
8103
|
+
framework: result.framework,
|
|
8104
|
+
language: result.language,
|
|
8105
|
+
confidence: result.confidence,
|
|
8106
|
+
detected_from: "headers"
|
|
8107
|
+
};
|
|
8221
8108
|
}
|
|
8222
8109
|
function detectFramework(options) {
|
|
8223
8110
|
if (options.packageJson) {
|
|
@@ -8252,13 +8139,16 @@ function detectFramework(options) {
|
|
|
8252
8139
|
const result = detectFromMixExs(options.mixExsContent);
|
|
8253
8140
|
if (result) return result;
|
|
8254
8141
|
}
|
|
8255
|
-
if (options.headers) {
|
|
8256
|
-
const result =
|
|
8257
|
-
if (result)
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8142
|
+
if (options.html || options.headers) {
|
|
8143
|
+
const result = detectFromEvidence({ html: options.html, headers: options.headers });
|
|
8144
|
+
if (result.framework !== "unknown") {
|
|
8145
|
+
return {
|
|
8146
|
+
framework: result.framework,
|
|
8147
|
+
language: result.language,
|
|
8148
|
+
confidence: result.confidence,
|
|
8149
|
+
detected_from: options.html ? "html" : "headers"
|
|
8150
|
+
};
|
|
8151
|
+
}
|
|
8262
8152
|
}
|
|
8263
8153
|
if (options.files) {
|
|
8264
8154
|
if (options.files.some((f) => f.includes("next.config"))) {
|
|
@@ -8365,6 +8255,8 @@ function getFrameworkDisplayName(framework) {
|
|
|
8365
8255
|
jekyll: "Jekyll",
|
|
8366
8256
|
eleventy: "Eleventy (11ty)",
|
|
8367
8257
|
pelican: "Pelican",
|
|
8258
|
+
sphinx: "Sphinx",
|
|
8259
|
+
docusaurus: "Docusaurus",
|
|
8368
8260
|
unknown: "Unknown"
|
|
8369
8261
|
};
|
|
8370
8262
|
return names[framework];
|
|
@@ -33052,6 +32944,48 @@ function findAppRoot(files) {
|
|
|
33052
32944
|
return `${nonRoot[0]}/`;
|
|
33053
32945
|
}
|
|
33054
32946
|
|
|
32947
|
+
// src/fixer/served-dir.ts
|
|
32948
|
+
var SERVED_DIR = {
|
|
32949
|
+
// Bundlers and meta-frameworks that copy a public/ directory verbatim.
|
|
32950
|
+
react: "public/",
|
|
32951
|
+
vue: "public/",
|
|
32952
|
+
svelte: "public/",
|
|
32953
|
+
angular: "public/",
|
|
32954
|
+
solidjs: "public/",
|
|
32955
|
+
qwik: "public/",
|
|
32956
|
+
nextjs: "public/",
|
|
32957
|
+
nuxt: "public/",
|
|
32958
|
+
astro: "public/",
|
|
32959
|
+
sveltekit: "static/",
|
|
32960
|
+
// SvelteKit's is static/, not public/
|
|
32961
|
+
remix: "public/",
|
|
32962
|
+
fresh: "static/",
|
|
32963
|
+
// Gatsby, Hugo, Eleventy and Docusaurus all name theirs static/.
|
|
32964
|
+
gatsby: "static/",
|
|
32965
|
+
hugo: "static/",
|
|
32966
|
+
eleventy: "public/",
|
|
32967
|
+
docusaurus: "static/",
|
|
32968
|
+
// Server frameworks with a public document root.
|
|
32969
|
+
rails: "public/",
|
|
32970
|
+
laravel: "public/",
|
|
32971
|
+
symfony: "public/",
|
|
32972
|
+
phoenix: "priv/static/",
|
|
32973
|
+
spring: "src/main/resources/static/",
|
|
32974
|
+
aspnet: "wwwroot/",
|
|
32975
|
+
// Jekyll and Pelican copy their source tree, so the app root is the served root.
|
|
32976
|
+
jekyll: "",
|
|
32977
|
+
pelican: ""
|
|
32978
|
+
// Python web frameworks have no single convention; the tree decides below.
|
|
32979
|
+
};
|
|
32980
|
+
function servedDir(framework, files, appRoot = "") {
|
|
32981
|
+
const known = framework ? SERVED_DIR[framework] : void 0;
|
|
32982
|
+
if (known !== void 0) return known;
|
|
32983
|
+
for (const candidate of ["public/", "static/", "assets/"]) {
|
|
32984
|
+
if (files.some((f) => f.startsWith(`${appRoot}${candidate}`))) return candidate;
|
|
32985
|
+
}
|
|
32986
|
+
return "";
|
|
32987
|
+
}
|
|
32988
|
+
|
|
33055
32989
|
// src/fixer/safety.ts
|
|
33056
32990
|
var HTML_EXT = /\.html?$/i;
|
|
33057
32991
|
var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/i;
|
|
@@ -35740,8 +35674,12 @@ async function analyzeComprehensive(html, url, options = {}) {
|
|
|
35740
35674
|
// src/frameworks/index.ts
|
|
35741
35675
|
var frameworks_exports = {};
|
|
35742
35676
|
__export(frameworks_exports, {
|
|
35677
|
+
SCORE_FLOOR: () => SCORE_FLOOR,
|
|
35678
|
+
SIGNALS: () => SIGNALS,
|
|
35743
35679
|
SUPPORTED_FRAMEWORKS: () => SUPPORTED_FRAMEWORKS,
|
|
35680
|
+
WEIGHT: () => WEIGHT,
|
|
35744
35681
|
detectFramework: () => detectFramework,
|
|
35682
|
+
detectFromEvidence: () => detectFromEvidence,
|
|
35745
35683
|
detectFromHeaders: () => detectFromHeaders,
|
|
35746
35684
|
detectFromHtml: () => detectFromHtml,
|
|
35747
35685
|
detectFromPackageJson: () => detectFromPackageJson,
|
|
@@ -36027,6 +35965,7 @@ var SUPPORTED_FRAMEWORKS = [
|
|
|
36027
35965
|
sendAlerts,
|
|
36028
35966
|
sendDiscordAlert,
|
|
36029
35967
|
sendSlackAlert,
|
|
35968
|
+
servedDir,
|
|
36030
35969
|
shouldRunAudit,
|
|
36031
35970
|
shouldSendAlert,
|
|
36032
35971
|
startWizardSession,
|