mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-07-18 05:50:27 +00:00
Compare commits
8 Commits
b0f92dd2d7
...
e735491c57
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e735491c57 | ||
![]() |
4d830f707f | ||
![]() |
f84a6db680 | ||
![]() |
a430eb2e60 | ||
![]() |
c232d41f90 | ||
![]() |
c1e4fd648b | ||
![]() |
d5b39a14fd | ||
![]() |
e2e1e49130 |
@ -2,6 +2,15 @@
|
||||
|
||||
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.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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user