+
${iconHTML}${escapeHTML(file.name)}
${formatSize(file.size)}
@@ -179,10 +179,10 @@
Select a file
container.innerHTML = `
Binary file (${formatSize(content.length)})
-
+
`;
}
@@ -207,6 +207,21 @@
Select a file
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
+// Entries carry their path in data-path rather than in inline handlers: the
+// browser decodes entities in a handler attribute before running it, so an
+// escaped quote in a file name would end the handler's string.
+document.getElementById('file-tree').addEventListener('click', (event) => {
+ const entry = event.target.closest('[data-action]');
+ if (!entry) return;
+ const path = entry.dataset.path;
+ if (entry.dataset.action === 'open-dir') {
+ loadFileTree(path);
+ currentPath = path;
+ } else if (entry.dataset.action === 'open-file') {
+ loadFile(path);
+ }
+});
+
// Load root directory on page load
loadFileTree();
diff --git a/internal/server/templates_handlers_test.go b/internal/server/templates_handlers_test.go
new file mode 100644
index 00000000..1e3f1f21
--- /dev/null
+++ b/internal/server/templates_handlers_test.go
@@ -0,0 +1,32 @@
+package server
+
+import (
+ "io/fs"
+ "regexp"
+ "testing"
+)
+
+// inlineHandlerWithData matches an inline event handler attribute whose value
+// interpolates a JavaScript template expression. The browser decodes HTML
+// entities in the attribute before running the handler, so escapeHTML cannot
+// keep a quote in the interpolated value from ending the handler's string.
+var inlineHandlerWithData = regexp.MustCompile(`\son[a-z]+\s*=\s*"[^"]*\$\{`)
+
+func TestTemplatesInterpolateNoDataIntoInlineHandlers(t *testing.T) {
+ err := fs.WalkDir(templatesFS, "templates", func(path string, d fs.DirEntry, err error) error {
+ if err != nil || d.IsDir() {
+ return err
+ }
+ source, err := fs.ReadFile(templatesFS, path)
+ if err != nil {
+ return err
+ }
+ for _, match := range inlineHandlerWithData.FindAll(source, -1) {
+ t.Errorf("%s: inline handler interpolates data: %s", path, match)
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+}