mirror of
https://github.com/BaaRai/ARTEX.git
synced 2026-11-13 19:50:54 +08:00
Comments, doc comments, UI labels, log and error messages, LLM prompt strings, markdown docs, test data and assertions. Identifiers, enum keys, routes, placeholders and code structure are unchanged. Notes: - docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md - tests that exercised CJK specific handling (GBK zip names, Chinese full text search) now use ASCII equivalents; go build passes and the test suite matches the pre translation baseline (remaining failures need a PostgreSQL DSN and fail identically on the original) - frontend mention syntax now uses English kinds (finding, asset, ...) matching the backend, chat mentions tests pass
193 lines
4.7 KiB
Go
193 lines
4.7 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/Autumn-27/artex/db"
|
|
)
|
|
|
|
// CRUD for task-level asset intercept/allow rules. Rules belong to a task_id
|
|
// and only apply to that task: action=block intercepts (testing forbidden),
|
|
// action=allow allows (whitelist). Enforcement lives in db.EvaluateAssetGate.
|
|
|
|
type taskInterceptRuleReq struct {
|
|
Enabled bool `json:"enabled"`
|
|
Action string `json:"action"` // block | allow
|
|
Kind string `json:"kind"`
|
|
Pattern string `json:"pattern"`
|
|
Note string `json:"note"`
|
|
}
|
|
|
|
// validateTaskInterceptRuleReq normalizes and validates; it reuses the global
|
|
// rule's kind/pattern validators.
|
|
func validateTaskInterceptRuleReq(req *taskInterceptRuleReq) error {
|
|
if req.Action == "" {
|
|
req.Action = "block"
|
|
}
|
|
if req.Action != "block" && req.Action != "allow" {
|
|
return fmt.Errorf("action must be block or allow")
|
|
}
|
|
v := assetInterceptRuleReq{Enabled: req.Enabled, Kind: req.Kind, Pattern: req.Pattern, Note: req.Note}
|
|
if err := validateAssetInterceptRuleReq(&v); err != nil {
|
|
return err
|
|
}
|
|
req.Pattern = v.Pattern // already trimmed
|
|
return nil
|
|
}
|
|
|
|
// buildTaskInterceptRules validates the task-level rules entered at task
|
|
// creation and converts them to the db input shape.
|
|
func buildTaskInterceptRules(reqs []taskInterceptRuleReq) ([]db.TaskInterceptRuleInput, error) {
|
|
if len(reqs) == 0 {
|
|
return nil, nil
|
|
}
|
|
out := make([]db.TaskInterceptRuleInput, 0, len(reqs))
|
|
for i := range reqs {
|
|
rq := reqs[i]
|
|
if err := validateTaskInterceptRuleReq(&rq); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, db.TaskInterceptRuleInput{
|
|
Enabled: rq.Enabled,
|
|
Action: rq.Action,
|
|
Kind: rq.Kind,
|
|
Pattern: rq.Pattern,
|
|
Note: rq.Note,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Server) taskInterceptListRules(w http.ResponseWriter, r *http.Request) {
|
|
pg := s.pg(w)
|
|
if pg == nil {
|
|
return
|
|
}
|
|
taskID, ok := pathInt(r, "id")
|
|
if !ok || taskID <= 0 {
|
|
writeErr(w, 400, "bad task id")
|
|
return
|
|
}
|
|
rules, err := pg.Assets().ListTaskInterceptRules(taskID)
|
|
if err != nil {
|
|
writeErr(w, 500, err.Error())
|
|
return
|
|
}
|
|
if rules == nil {
|
|
rules = []db.AssetInterceptRule{}
|
|
}
|
|
writeJSON(w, 200, map[string]any{"rules": rules})
|
|
}
|
|
|
|
func (s *Server) taskInterceptCreateRule(w http.ResponseWriter, r *http.Request) {
|
|
pg := s.pg(w)
|
|
if pg == nil {
|
|
return
|
|
}
|
|
taskID, ok := pathInt(r, "id")
|
|
if !ok || taskID <= 0 {
|
|
writeErr(w, 400, "bad task id")
|
|
return
|
|
}
|
|
var req taskInterceptRuleReq
|
|
if err := decode(r, &req); err != nil {
|
|
writeErr(w, 400, err.Error())
|
|
return
|
|
}
|
|
if err := validateTaskInterceptRuleReq(&req); err != nil {
|
|
writeErr(w, 400, err.Error())
|
|
return
|
|
}
|
|
rule, err := pg.Assets().CreateTaskInterceptRule(taskID, req.Action, req.Kind, req.Pattern, req.Note, req.Enabled)
|
|
if err != nil {
|
|
writeErr(w, 500, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, 200, rule)
|
|
}
|
|
|
|
func (s *Server) taskInterceptUpdateRule(w http.ResponseWriter, r *http.Request) {
|
|
pg := s.pg(w)
|
|
if pg == nil {
|
|
return
|
|
}
|
|
taskID, ok := pathInt(r, "id")
|
|
if !ok || taskID <= 0 {
|
|
writeErr(w, 400, "bad task id")
|
|
return
|
|
}
|
|
ruleID, ok := pathInt(r, "rid")
|
|
if !ok || ruleID <= 0 {
|
|
writeErr(w, 400, "bad rule id")
|
|
return
|
|
}
|
|
var req taskInterceptRuleReq
|
|
if err := decode(r, &req); err != nil {
|
|
writeErr(w, 400, err.Error())
|
|
return
|
|
}
|
|
if err := validateTaskInterceptRuleReq(&req); err != nil {
|
|
writeErr(w, 400, err.Error())
|
|
return
|
|
}
|
|
rule, err := pg.Assets().UpdateTaskInterceptRule(taskID, ruleID, req.Action, req.Kind, req.Pattern, req.Note, req.Enabled)
|
|
if err != nil {
|
|
writeErr(w, 500, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, 200, rule)
|
|
}
|
|
|
|
func (s *Server) taskInterceptDeleteRule(w http.ResponseWriter, r *http.Request) {
|
|
pg := s.pg(w)
|
|
if pg == nil {
|
|
return
|
|
}
|
|
taskID, ok := pathInt(r, "id")
|
|
if !ok || taskID <= 0 {
|
|
writeErr(w, 400, "bad task id")
|
|
return
|
|
}
|
|
ruleID, ok := pathInt(r, "rid")
|
|
if !ok || ruleID <= 0 {
|
|
writeErr(w, 400, "bad rule id")
|
|
return
|
|
}
|
|
deleted, err := pg.Assets().DeleteTaskInterceptRule(taskID, ruleID)
|
|
if err != nil {
|
|
writeErr(w, 500, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, 200, map[string]any{"deleted": deleted})
|
|
}
|
|
|
|
func (s *Server) taskInterceptToggleRule(w http.ResponseWriter, r *http.Request) {
|
|
pg := s.pg(w)
|
|
if pg == nil {
|
|
return
|
|
}
|
|
taskID, ok := pathInt(r, "id")
|
|
if !ok || taskID <= 0 {
|
|
writeErr(w, 400, "bad task id")
|
|
return
|
|
}
|
|
ruleID, ok := pathInt(r, "rid")
|
|
if !ok || ruleID <= 0 {
|
|
writeErr(w, 400, "bad rule id")
|
|
return
|
|
}
|
|
var req struct {
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
if err := decode(r, &req); err != nil {
|
|
writeErr(w, 400, err.Error())
|
|
return
|
|
}
|
|
if err := pg.Assets().ToggleTaskInterceptRule(taskID, ruleID, req.Enabled); err != nil {
|
|
writeErr(w, 500, err.Error())
|
|
return
|
|
}
|
|
writeJSON(w, 200, map[string]any{"ok": true, "enabled": req.Enabled})
|
|
}
|