From 7d9448571ce728e058b4db9a15fad13706b303ba Mon Sep 17 00:00:00 2001 From: Thomas <27960254+thomiceli@users.noreply.github.com> Date: Sat, 27 Jun 2026 04:19:45 +0700 Subject: [PATCH] Disable file upload (#737) --- config.yml | 3 +++ docs/configuration/cheat-sheet.md | 1 + internal/config/config.go | 2 ++ internal/i18n/locales/en-US.yml | 1 + internal/web/handlers/gist/create.go | 4 ++-- internal/web/server/middlewares.go | 9 +++++++++ internal/web/server/router.go | 4 ++-- public/ts/editor.ts | 12 +++++++++--- templates/pages/create.html | 2 ++ templates/pages/edit.html | 2 ++ 10 files changed, 33 insertions(+), 7 deletions(-) diff --git a/config.yml b/config.yml index d769a64..a1dc63c 100644 --- a/config.yml +++ b/config.yml @@ -59,6 +59,9 @@ http.git-enabled: true # Enable or disable the REST API (either `true` or `false`). Default: true api.enabled: true +# Disable file uploads when creating or editing gists (either `true` or `false`). Default: false +disable-file-upload: false + # File permissions for Unix socket (octal format). Default: 0666 unix-socket-permissions: 0666 diff --git a/docs/configuration/cheat-sheet.md b/docs/configuration/cheat-sheet.md index 5e802d8..d4b7534 100644 --- a/docs/configuration/cheat-sheet.md +++ b/docs/configuration/cheat-sheet.md @@ -22,6 +22,7 @@ aside: false | http.port | OG_HTTP_PORT | `6157` | The port on which the HTTP server should listen. | | http.git-enabled | OG_HTTP_GIT_ENABLED | `true` | Enable or disable git operations (clone, pull, push) via HTTP. (`true` or `false`) | | api.enabled | OG_API_ENABLED | `true` | Enable or disable the REST API. (`true` or `false`) | +| disable-file-upload | OG_DISABLE_FILE_UPLOAD | `false` | Disable file uploads when creating or editing gists. (`true` or `false`) | | unix-socket-permissions | OG_UNIX_SOCKET_PERMISSIONS | `0666` | File permissions for Unix socket (octal format). | | metrics.enabled | OG_METRICS_ENABLED | `false` | Enable or disable Prometheus metrics server (`true` or `false`) | | metrics.host | OG_METRICS_HOST | `0.0.0.0` | The host on which the metrics server should bind. | diff --git a/internal/config/config.go b/internal/config/config.go index b7f39a0..7f20b8f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -64,6 +64,8 @@ type config struct { ApiEnabled bool `yaml:"api.enabled" env:"OG_API_ENABLED"` + DisableFileUpload bool `yaml:"disable-file-upload" env:"OG_DISABLE_FILE_UPLOAD"` + UnixSocketPermissions string `yaml:"unix-socket-permissions" env:"OG_UNIX_SOCKET_PERMISSIONS"` SshGit string `yaml:"ssh.git-enabled" env:"OG_SSH_GIT_ENABLED"` // builtin | host | disabled (true → builtin, false → disabled) diff --git a/internal/i18n/locales/en-US.yml b/internal/i18n/locales/en-US.yml index 7428415..fbd7813 100644 --- a/internal/i18n/locales/en-US.yml +++ b/internal/i18n/locales/en-US.yml @@ -272,6 +272,7 @@ error.invalid-number: Invalid number error.invalid-character-unescaped: Invalid character unescaped error.not-in-mfa-session: User is not in a MFA session error.no-file-uploaded: No file uploaded +error.file-upload-disabled: File upload is disabled error.cannot-open-file: Cannot open uploaded file header.menu.all: All diff --git a/internal/web/handlers/gist/create.go b/internal/web/handlers/gist/create.go index 0e7c676..108ddbd 100644 --- a/internal/web/handlers/gist/create.go +++ b/internal/web/handlers/gist/create.go @@ -65,10 +65,10 @@ func ProcessCreate(ctx *context.Context) error { }) } - // Process uploaded files from UUID arrays + // Process uploaded files from UUID arrays, unless file upload is disabled instance-wide fileUUIDs := dto.UploadedFilesUUID fileFilenames := dto.UploadedFilesNames - if len(fileUUIDs) == len(fileFilenames) { + if !config.C.DisableFileUpload && len(fileUUIDs) == len(fileFilenames) { for i, fileUUID := range fileUUIDs { if !uuidRegex.MatchString(filepath.Base(fileUUID)) { continue diff --git a/internal/web/server/middlewares.go b/internal/web/server/middlewares.go index 7ebbc00..902d952 100644 --- a/internal/web/server/middlewares.go +++ b/internal/web/server/middlewares.go @@ -251,6 +251,15 @@ func checkRequireLogin(next Handler) Handler { return makeCheckRequireLogin(false)(next) } +func checkFileUploadEnabled(next Handler) Handler { + return func(ctx *context.Context) error { + if config.C.DisableFileUpload { + return ctx.ErrorRes(403, ctx.Tr("error.file-upload-disabled"), nil) + } + return next(ctx) + } +} + // makeApiCheckRequireLogin is the /api/v1 counterpart of makeCheckRequireLogin: // it enforces the instance's RequireLogin / AllowGistsWithoutLogin settings on // anonymous gist reads, but responds with a JSON 401 instead of redirecting to diff --git a/internal/web/server/router.go b/internal/web/server/router.go index 9b850a0..6f03a93 100644 --- a/internal/web/server/router.go +++ b/internal/web/server/router.go @@ -32,8 +32,8 @@ func (s *Server) registerRoutes() { r.GET("/", gist.Create, logged) r.POST("/", gist.ProcessCreate, logged) r.POST("/preview", gist.Preview, logged) - r.POST("/upload", gist.Upload, logged) - r.DELETE("/upload/:uuid", gist.DeleteUpload, logged) + r.POST("/upload", gist.Upload, logged, checkFileUploadEnabled) + r.DELETE("/upload/:uuid", gist.DeleteUpload, logged, checkFileUploadEnabled) r.GET("/healthcheck", health.Healthcheck) diff --git a/public/ts/editor.ts b/public/ts/editor.ts index 638f4ef..5660a6d 100644 --- a/public/ts/editor.ts +++ b/public/ts/editor.ts @@ -324,9 +324,15 @@ document.addEventListener("DOMContentLoaded", () => { // File upload functionality let uploadedFileUUIDs: {uuid: string, filename: string}[] = []; - const fileUploadInput = document.getElementById("file-upload") as HTMLInputElement; - const uploadedFilesContainer = document.getElementById("uploaded-files")!; - const fileUploadZone = document.getElementById("file-upload-zone")!.querySelector('.border-dashed') as HTMLElement; + const fileUploadInput = document.getElementById("file-upload") as HTMLInputElement | null; + const uploadedFilesContainer = document.getElementById("uploaded-files"); + const fileUploadZoneEl = document.getElementById("file-upload-zone"); + + // File upload may be disabled instance-wide, in which case the upload zone is absent + if (!fileUploadInput || !uploadedFilesContainer || !fileUploadZoneEl) { + return; + } + const fileUploadZone = fileUploadZoneEl.querySelector('.border-dashed') as HTMLElement; // Handle file selection const handleFiles = (files: FileList) => { diff --git a/templates/pages/create.html b/templates/pages/create.html index 272321c..8478f6b 100644 --- a/templates/pages/create.html +++ b/templates/pages/create.html @@ -41,6 +41,7 @@ {{ end }} + {{ if not .c.DisableFileUpload }}