Compare commits

..

11 Commits

Author SHA1 Message Date
Henrique Dias
da03728cd7 chore(release): 2.33.10 2025-06-26 21:23:35 +02:00
Henrique Dias
e735491c57 fix: ignore linting error 2025-06-26 21:12:24 +02:00
Henrique Dias
4d830f707f fix: correctly check if command is allowed when using shell 2025-06-26 21:09:16 +02:00
Henrique Dias
f84a6db680 fix: correctly split shell 2025-06-26 21:07:45 +02:00
Henrique Dias
a430eb2e60 chore(release): 2.33.9 2025-06-26 19:45:36 +02:00
Henrique Dias
c232d41f90 fix: remove unused import 2025-06-26 19:43:20 +02:00
Henrique Dias
c1e4fd648b docs: add warning regarding the custom commands feature 2025-06-26 19:42:13 +02:00
Henrique Dias
d5b39a14fd fix: remove auth token from /api/command 2025-06-26 19:42:13 +02:00
Henrique Dias
e2e1e49130 fix: check exact match on command allow list 2025-06-26 19:42:12 +02:00
Henrique Dias
b0f92dd2d7 chore(release): 2.33.8 2025-06-25 20:53:47 +02:00
Henrique Dias
21b0827808 Merge commit from fork 2025-06-25 20:50:38 +02:00
9 changed files with 58 additions and 43 deletions

View File

@@ -2,6 +2,26 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [2.33.10](https://github.com/filebrowser/filebrowser/compare/v2.33.9...v2.33.10) (2025-06-26)
### Bug Fixes
* correctly check if command is allowed when using shell ([4d830f7](https://github.com/filebrowser/filebrowser/commit/4d830f707fc4314741fd431e70c2ce50cd5a3108))
* correctly split shell ([f84a6db](https://github.com/filebrowser/filebrowser/commit/f84a6db680b6df1c7c8f06f1816f7e4c9e963668))
* ignore linting error ([e735491](https://github.com/filebrowser/filebrowser/commit/e735491c57b12c3b19dd2e4b570723df78f4eb44))
### [2.33.9](https://github.com/filebrowser/filebrowser/compare/v2.33.8...v2.33.9) (2025-06-26)
### Bug Fixes
* check exact match on command allow list ([e2e1e49](https://github.com/filebrowser/filebrowser/commit/e2e1e4913085cca8917e0f69171dc28d3c6af1b6))
* remove auth token from /api/command ([d5b39a1](https://github.com/filebrowser/filebrowser/commit/d5b39a14fd3fc0d1c364116b41289484df7c27b2))
* remove unused import ([c232d41](https://github.com/filebrowser/filebrowser/commit/c232d41f903d3026ec290bbe819b6c59a933048e))
### [2.33.8](https://github.com/filebrowser/filebrowser/compare/v2.33.7...v2.33.8) (2025-06-25)
### [2.33.7](https://github.com/filebrowser/filebrowser/compare/v2.33.6...v2.33.7) (2025-06-25)

View File

@@ -38,6 +38,9 @@ File Browser is a **create-your-own-cloud-kind** of software where you can insta
| :----------------------: | :----------------------: | :----------------------: |
| ![](./docs/assets/4.jpg) | ![](./docs/assets/5.jpg) | ![](./docs/assets/6.jpg) |
> [!CAUTION]
>
> The **command execution** functionality has been disabled for all existent and new installations by default from version v2.33.8 and onwards, due to continuous and known security vulnerabilities. You should only use this feature if you are aware of all of the security risks involved. For more up to date information, consult issue [#5199](https://github.com/filebrowser/filebrowser/issues/5199).
## Install

View File

@@ -68,7 +68,7 @@ func addServerFlags(flags *pflag.FlagSet) {
flags.Int("img-processors", 4, "image processors count") //nolint:gomnd
flags.Bool("disable-thumbnails", false, "disable image thumbnails")
flags.Bool("disable-preview-resize", false, "disable resize of image previews")
flags.Bool("disable-exec", false, "disables Command Runner feature")
flags.Bool("disable-exec", true, "disables Command Runner feature")
flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers")
}
@@ -262,6 +262,13 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
disableExec := getBoolParam(flags, "disable-exec")
server.EnableExec = !disableExec
if server.EnableExec {
log.Println("WARNING: Command Runner feature enabled!")
log.Println("WARNING: This feature has known security vulnerabilities and should not")
log.Println("WARNING: you fully understand the risks involved. For more information")
log.Println("WARNING: read https://github.com/filebrowser/filebrowser/issues/5199")
}
if val, set := getStringParamB(flags, "token-expiration-time"); set {
server.TokenExpirationTime = val
}

View File

@@ -1,6 +1,5 @@
import { removePrefix } from "./utils";
import { baseURL } from "@/utils/constants";
import { useAuthStore } from "@/stores/auth";
import { removePrefix } from "./utils";
const ssl = window.location.protocol === "https:";
const protocol = ssl ? "wss:" : "ws:";
@@ -11,10 +10,8 @@ export default function command(
onmessage: WebSocket["onmessage"],
onclose: WebSocket["onclose"]
) {
const authStore = useAuthStore();
url = removePrefix(url);
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${authStore.jwt}`;
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}`;
const conn = new window.WebSocket(url);
conn.onopen = () => conn.send(command);

View File

@@ -321,7 +321,10 @@ const save = async () => {
.filter((cmd: string) => cmd !== "");
}
}
newSettings.shell = shellValue.value.split("\n");
newSettings.shell = shellValue.value
.trim()
.split(" ")
.filter((s) => s !== "");
if (newSettings.branding.theme !== getTheme()) {
setTheme(newSettings.branding.theme);

View File

@@ -6,6 +6,7 @@ import (
"log"
"net/http"
"os/exec"
"slices"
"strings"
"time"
@@ -60,7 +61,16 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
}
}
command, err := runner.ParseCommand(d.settings, raw)
// Fail fast
if !d.server.EnableExec || !d.user.Perm.Execute {
if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet
wsErr(conn, r, http.StatusInternalServerError, err)
}
return 0, nil
}
command, name, err := runner.ParseCommand(d.settings, raw)
if err != nil {
if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:govet
wsErr(conn, r, http.StatusInternalServerError, err)
@@ -68,7 +78,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
return 0, nil
}
if !d.server.EnableExec || !d.user.CanExecute(command[0]) {
if !slices.Contains(d.user.Commands, name) {
if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:govet
wsErr(conn, r, http.StatusInternalServerError, err)
}

View File

@@ -1,33 +1,24 @@
package runner
import (
"os/exec"
"github.com/filebrowser/filebrowser/v2/settings"
)
// ParseCommand parses the command taking in account if the current
// instance uses a shell to run the commands or just calls the binary
// directyly.
func ParseCommand(s *settings.Settings, raw string) ([]string, error) {
var command []string
// directly.
func ParseCommand(s *settings.Settings, raw string) (command []string, name string, err error) {
name, args, err := SplitCommandAndArgs(raw)
if err != nil {
return
}
if len(s.Shell) == 0 || s.Shell[0] == "" {
cmd, args, err := SplitCommandAndArgs(raw)
if err != nil {
return nil, err
}
_, err = exec.LookPath(cmd)
if err != nil {
return nil, err
}
command = append(command, cmd)
command = append(command, name)
command = append(command, args...)
} else {
command = append(s.Shell, raw) //nolint:gocritic
}
return command, nil
return command, name, nil
}

View File

@@ -60,7 +60,7 @@ func (r *Runner) exec(raw, evt, path, dst string, user *users.User) error {
raw = strings.TrimSpace(strings.TrimSuffix(raw, "&"))
}
command, err := ParseCommand(r.Settings, raw)
command, _, err := ParseCommand(r.Settings, raw)
if err != nil {
return err
}

View File

@@ -2,7 +2,6 @@ package users
import (
"path/filepath"
"regexp"
"github.com/spf13/afero"
@@ -104,18 +103,3 @@ func (u *User) Clean(baseScope string, fields ...string) error {
func (u *User) FullPath(path string) string {
return afero.FullBaseFsPath(u.Fs.(*afero.BasePathFs), path)
}
// CanExecute checks if an user can execute a specific command.
func (u *User) CanExecute(command string) bool {
if !u.Perm.Execute {
return false
}
for _, cmd := range u.Commands {
if regexp.MustCompile(cmd).MatchString(command) {
return true
}
}
return false
}