mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-14 05:18:54 +00:00
fix(webui): defuse scaffold config traps, tighten inline guards
Micro-fix from re-review: 1. webui/components.json: deleted the font/iconLibrary keys instead of picking a value. Re-review checked shadcn-vue's actual parse schema (node_modules/shadcn-vue/dist/schema/index.js's rawConfigSchema): both fields are z.string().optional(), not an enum - the five-choice enum lives only on the init CLI's --font/--icon-library flags, not on the components.json schema. The previous commit's "schema forbids a fix" rationale was wrong; corrected here. Verified empirically, not just by reading the schema: ran a real `npx shadcn-vue add badge -o -y` against components.json with both keys removed - src/assets/ main.css came back byte-for-byte unchanged (previously this exact class of call re-added a Google Fonts import and the .dark-class toggle). That same run did still re-add @lucide/vue to package.json's dependencies (some components' registry entries list an icon package regardless of iconLibrary) - reverted before committing, and webui/README.md's warning section rewritten to explain the omission (so nobody "helpfully" restores the keys), fix the schema claim, and note the narrower remaining package.json trap. 2. admin.go:27-28: adminCSP's doc comment now names all three set-sites (GET /admin, every GET /admin/assets/*, the three JSON routes) - previously only mentioned the page and JSON routes, dropped after the asset route gained the same header in the prior fix round. 3. Both inline-script guards (webui/generate.mjs's assertNoInlineAssets, admin_test.go's hasInlineScript) tightened from a bare \bsrc\s*= pattern to (^|\s)src\s*= - \b alone matches inside "data-src=" too (the "-" before "src" is a non-word character, so \b sits right before "src"), which would wrongly treat a data-src/nosrc attribute as the real src= that makes a <script> external. New Go test TestHasInlineScript_DataSrcIsNotExternal: <script data-src="x"> is still flagged as inline. No webui/src/ change this round - components.json isn't bundled, and generate.mjs's edit only touches its own build-time guard, not the Go-source-generation path. admin_assets_gen.go NOT regenerated (confirmed via a throwaway `node generate.mjs` + gofmt -w run: output is byte-identical to what's already committed).
This commit is contained in:
+43
-7
@@ -696,10 +696,33 @@ func TestAdminAssets_GeneratedFilePresent(t *testing.T) {
|
||||
}
|
||||
|
||||
// adminInlineScriptRe matches every <script ...> opening tag, capturing its
|
||||
// attribute text — TestAdminIndexHTML_NoInlineAssets checks each capture for
|
||||
// a src= attribute.
|
||||
// attribute text — hasInlineScript checks each capture against
|
||||
// adminSrcAttrRe for a real src= attribute.
|
||||
var adminInlineScriptRe = regexp.MustCompile(`(?i)<script\b([^>]*)>`)
|
||||
|
||||
// adminSrcAttrRe matches a real src= attribute, anchored to the start of an
|
||||
// attribute (start-of-string or preceding whitespace) rather than a bare
|
||||
// \b word boundary: \b alone matches inside "data-src=" too (the "-" before
|
||||
// "src" is itself a non-word character, so \b sits right before "src"),
|
||||
// which would wrongly treat a data-src/nosrc/whatever-src attribute as the
|
||||
// real src= that makes a <script> external. Mirrors
|
||||
// webui/generate.mjs's assertNoInlineAssets, which guards the identical
|
||||
// invariant at build time.
|
||||
var adminSrcAttrRe = regexp.MustCompile(`(?i)(^|\s)src\s*=`)
|
||||
|
||||
// hasInlineScript returns every <script> opening tag in html with no real
|
||||
// src= attribute — an inline script, forbidden under the admin CSP's
|
||||
// script-src 'self' (no 'unsafe-inline').
|
||||
func hasInlineScript(html string) []string {
|
||||
var offenders []string
|
||||
for _, m := range adminInlineScriptRe.FindAllStringSubmatch(html, -1) {
|
||||
if !adminSrcAttrRe.MatchString(m[1]) {
|
||||
offenders = append(offenders, m[0])
|
||||
}
|
||||
}
|
||||
return offenders
|
||||
}
|
||||
|
||||
// TestAdminIndexHTML_NoInlineAssets asserts, server-side, the exact CSP
|
||||
// invariant generate.mjs's own assertNoInlineAssets checks at build time
|
||||
// (webui/generate.mjs): the decoded adminIndexHTML — what admin.go's
|
||||
@@ -714,17 +737,30 @@ func TestAdminIndexHTML_NoInlineAssets(t *testing.T) {
|
||||
t.Parallel()
|
||||
html := string(adminIndexHTML)
|
||||
|
||||
for _, m := range adminInlineScriptRe.FindAllStringSubmatch(html, -1) {
|
||||
attrs := m[1]
|
||||
if !strings.Contains(strings.ToLower(attrs), "src=") {
|
||||
t.Errorf("adminIndexHTML contains an inline <script> (no src= attribute): %q", m[0])
|
||||
}
|
||||
for _, tag := range hasInlineScript(html) {
|
||||
t.Errorf("adminIndexHTML contains an inline <script> (no src= attribute): %q", tag)
|
||||
}
|
||||
if strings.Contains(strings.ToLower(html), "<style") {
|
||||
t.Error("adminIndexHTML contains a <style> tag — every style must be an external <link rel=\"stylesheet\">")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHasInlineScript_DataSrcIsNotExternal is the negative case
|
||||
// hasInlineScript's adminSrcAttrRe anchoring exists for: a <script
|
||||
// data-src="..."> tag has no REAL src= attribute — data-src is a
|
||||
// distinct, non-standard attribute name, not the src that makes a
|
||||
// <script> external — and must still be flagged as inline. A naive
|
||||
// strings.Contains(attrs, "src=") or a bare \bsrc\s*=\b regexp would both
|
||||
// wrongly treat this tag as external, since "src=" appears as a literal
|
||||
// substring of "data-src=".
|
||||
func TestHasInlineScript_DataSrcIsNotExternal(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := hasInlineScript(`<script data-src="x">console.log(1)</script>`)
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("hasInlineScript = %v, want exactly 1 offender (data-src is not src=)", got)
|
||||
}
|
||||
}
|
||||
|
||||
func truncateForTest(b []byte, n int) string {
|
||||
if len(b) <= n {
|
||||
return string(b)
|
||||
|
||||
Reference in New Issue
Block a user