Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da03728cd7 | ||
|
|
e735491c57 | ||
|
|
4d830f707f | ||
|
|
f84a6db680 | ||
|
|
a430eb2e60 | ||
|
|
c232d41f90 | ||
|
|
c1e4fd648b | ||
|
|
d5b39a14fd | ||
|
|
e2e1e49130 | ||
|
|
b0f92dd2d7 | ||
|
|
21b0827808 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ File Browser is a **create-your-own-cloud-kind** of software where you can insta
|
||||
| :----------------------: | :----------------------: | :----------------------: |
|
||||
|  |  |  |
|
||||
|
||||
> [!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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user