Add files via upload

This commit is contained in:
公明
2026-07-22 17:34:24 +08:00
committed by GitHub
parent 2d7f5322b3
commit d9cb0b11c5
2 changed files with 57 additions and 13 deletions
+22 -10
View File
@@ -721,15 +721,22 @@ func (h *WebShellHandler) Exec(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "url and command are required"})
return
}
conn, allowed := h.authorizedWebshellConnection(c, req.ConnectionID, req.URL)
if !allowed {
// Pre-save connectivity tests send form credentials without connection_id.
// Saved connections must go through resource ACL; DB credentials are authoritative.
if cid := strings.TrimSpace(req.ConnectionID); cid != "" {
conn, allowed := h.authorizedWebshellConnection(c, cid, req.URL)
if !allowed {
c.JSON(http.StatusForbidden, gin.H{"error": "无权访问该资源"})
return
}
// Never let a caller pair an authorized ID with attacker-controlled
// transport credentials or a URL.
req.URL, req.Password, req.Type = conn.URL, conn.Password, conn.Type
req.Method, req.CmdParam, req.Encoding = conn.Method, conn.CmdParam, conn.Encoding
} else if !security.SessionHasPermission(c, "webshell:write") {
c.JSON(http.StatusForbidden, gin.H{"error": "无权访问该资源"})
return
}
// The database record is authoritative. Never let a caller pair an
// authorized ID with attacker-controlled transport credentials or a URL.
req.URL, req.Password, req.Type = conn.URL, conn.Password, conn.Type
req.Method, req.CmdParam, req.Encoding = conn.Method, conn.CmdParam, conn.Encoding
parsed, err := url.Parse(req.URL)
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") {
@@ -823,13 +830,18 @@ func (h *WebShellHandler) FileOp(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "url and action are required"})
return
}
conn, allowed := h.authorizedWebshellConnection(c, req.ConnectionID, req.URL)
if !allowed {
if cid := strings.TrimSpace(req.ConnectionID); cid != "" {
conn, allowed := h.authorizedWebshellConnection(c, cid, req.URL)
if !allowed {
c.JSON(http.StatusForbidden, gin.H{"error": "无权访问该资源"})
return
}
req.URL, req.Password, req.Type = conn.URL, conn.Password, conn.Type
req.Method, req.CmdParam, req.Encoding, req.OS = conn.Method, conn.CmdParam, conn.Encoding, conn.OS
} else if !security.SessionHasPermission(c, "webshell:write") {
c.JSON(http.StatusForbidden, gin.H{"error": "无权访问该资源"})
return
}
req.URL, req.Password, req.Type = conn.URL, conn.Password, conn.Type
req.Method, req.CmdParam, req.Encoding, req.OS = conn.Method, conn.CmdParam, conn.Encoding, conn.OS
parsed, err := url.Parse(req.URL)
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") {
+35 -3
View File
@@ -40,15 +40,47 @@ func TestWebshellExecRequiresConnectionAccessWhenConnectionIDProvided(t *testing
}
}
func TestWebshellExecRejectsAdHocURLWithoutConnectionID(t *testing.T) {
func TestWebshellExecAllowsAdHocURLWithoutConnectionID(t *testing.T) {
gin.SetMode(gin.TestMode)
_, user, _, _ := setupWebshellRBACTest(t)
handler := NewWebShellHandler(zap.NewNop(), nil)
// Ad-hoc probe (connectivity test before save) must not be rejected as "无权访问".
// The target URL will fail to connect; we only assert auth allows the request through.
w := performWebshellJSON(user, http.MethodPost, "/api/webshell/exec", map[string]interface{}{
"url": "http://127.0.0.1/admin", "command": "id",
"url": "http://127.0.0.1:1/admin", "command": "id",
}, handler.Exec)
if w.Code == http.StatusForbidden {
t.Fatalf("ad-hoc URL status = %d, want non-403: %s", w.Code, w.Body.String())
}
var resp ExecResponse
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode response: %v body=%s", err, w.Body.String())
}
if resp.OK {
t.Fatalf("expected connection failure for closed port, got ok=true")
}
}
func TestWebshellExecRejectsAdHocWithoutWritePermission(t *testing.T) {
gin.SetMode(gin.TestMode)
user := &database.RBACUser{ID: "u_ro", Username: "readonly"}
handler := NewWebShellHandler(zap.NewNop(), nil)
payload, _ := json.Marshal(map[string]interface{}{
"url": "http://127.0.0.1/admin", "command": "id",
})
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest(http.MethodPost, "/api/webshell/exec", bytes.NewReader(payload))
c.Request.Header.Set("Content-Type", "application/json")
c.Set(security.ContextSessionKey, security.Session{
UserID: user.ID,
Username: user.Username,
Permissions: map[string]bool{"webshell:read": true},
Scope: database.RBACScopeAssigned,
})
handler.Exec(c)
if w.Code != http.StatusForbidden {
t.Fatalf("ad-hoc URL status = %d, want %d: %s", w.Code, http.StatusForbidden, w.Body.String())
t.Fatalf("status = %d, want %d: %s", w.Code, http.StatusForbidden, w.Body.String())
}
}