function update

This commit is contained in:
2026-05-19 04:30:14 +00:00
parent 1293bafffa
commit 3f82dfd9e9
13 changed files with 8080 additions and 152 deletions
+77 -13
View File
@@ -268,6 +268,16 @@ func (c *CLIClient) ListPostoverflows(ctx context.Context) ([]HubItem, error) {
return c.listHubItems(ctx, "postoverflows")
}
// InstallPostoverflow installs a postoverflow.
func (c *CLIClient) InstallPostoverflow(ctx context.Context, name string) error {
return c.hubAction(ctx, "postoverflows", "install", name)
}
// RemovePostoverflow removes a postoverflow.
func (c *CLIClient) RemovePostoverflow(ctx context.Context, name string) error {
return c.hubAction(ctx, "postoverflows", "remove", name)
}
// HubUpdate runs cscli hub update && cscli hub upgrade.
func (c *CLIClient) HubUpdate(ctx context.Context) error {
if _, err := c.run(ctx, "hub", "update"); err != nil {
@@ -352,15 +362,55 @@ func (c *CLIClient) InspectAllowlist(ctx context.Context, name string) (*Allowli
return &al, nil
}
// AddAllowlistEntry adds a value to a named allowlist.
func (c *CLIClient) AddAllowlistEntry(ctx context.Context, listName, value string) error {
// CreateAllowlist creates a new allowlist. Ignores "already exists" errors.
// cscli v1.7 requires the -d (description) flag.
func (c *CLIClient) CreateAllowlist(ctx context.Context, name string) error {
if !safeArg.MatchString(name) {
return fmt.Errorf("invalid allowlist name: %q", name)
}
_, err := c.run(ctx, "allowlists", "create", name, "-d", "crowdsec-dashy")
if err != nil {
msg := err.Error()
if strings.Contains(msg, "already exists") || strings.Contains(msg, "already exist") {
return nil
}
return err
}
return nil
}
// AddAllowlistEntries adds one or more values to a named allowlist in a single call.
// comment is optional; if non-empty it is passed as -d to cscli.
func (c *CLIClient) AddAllowlistEntries(ctx context.Context, listName, comment string, values []string) error {
if !safeArg.MatchString(listName) {
return fmt.Errorf("invalid allowlist name: %q", listName)
}
if !safeArg.MatchString(value) {
return fmt.Errorf("invalid allowlist value: %q", value)
if len(values) == 0 {
return fmt.Errorf("no values provided")
}
_, err := c.run(ctx, "allowlists", "items", "add", listName, value)
args := []string{"allowlists", "add", listName}
for _, v := range values {
if !safeArg.MatchString(v) {
return fmt.Errorf("invalid allowlist value: %q", v)
}
args = append(args, v)
}
if comment != "" {
if !safeFlagValue.MatchString(comment) {
return fmt.Errorf("invalid comment: only letters, digits, spaces, and common punctuation allowed")
}
args = append(args, "-d", comment)
}
_, err := c.run(ctx, args...)
return err
}
// DeleteAllowlist removes an entire allowlist.
func (c *CLIClient) DeleteAllowlist(ctx context.Context, name string) error {
if !safeArg.MatchString(name) {
return fmt.Errorf("invalid allowlist name: %q", name)
}
_, err := c.run(ctx, "allowlists", "delete", name)
return err
}
@@ -372,7 +422,7 @@ func (c *CLIClient) RemoveAllowlistEntry(ctx context.Context, listName, value st
if !safeArg.MatchString(value) {
return fmt.Errorf("invalid allowlist value: %q", value)
}
_, err := c.run(ctx, "allowlists", "items", "del", listName, value)
_, err := c.run(ctx, "allowlists", "remove", listName, value)
return err
}
@@ -430,13 +480,16 @@ var allowedActions = map[string]bool{
"validate": true,
"create": true,
"inspect": true,
"items": true,
"del": true,
}
// safeArg matches strings that are safe to pass as arguments (no shell metacharacters).
// safeArg matches strings that are safe to pass as positional cscli arguments.
var safeArg = regexp.MustCompile(`^[a-zA-Z0-9_./:@\-]+$`)
// safeFlagValue matches flag values (after --flag). More permissive: allows spaces and
// common punctuation, but blocks null bytes, control chars, and shell metacharacters.
var safeFlagValue = regexp.MustCompile(`^[a-zA-Z0-9 _./:@\-,!?()']+$`)
func validateArgs(args []string) error {
if len(args) == 0 {
return fmt.Errorf("no subcommand provided")
@@ -444,19 +497,30 @@ func validateArgs(args []string) error {
if !allowedSubcommands[args[0]] {
return fmt.Errorf("disallowed cscli subcommand: %q", args[0])
}
// Skip flag-value pairs like "-o json" and "--machine-id <id>" — validate values
// Iterate remaining args. Flag names (start with -) are allowed as-is.
// The arg immediately after a flag is treated as its value and validated with
// the more permissive safeFlagValue rather than safeArg.
prevWasFlag := false
for i := 1; i < len(args); i++ {
arg := args[i]
if strings.HasPrefix(arg, "-") {
continue // flags are fine
prevWasFlag = true
continue
}
if prevWasFlag {
prevWasFlag = false
if !safeFlagValue.MatchString(arg) {
return fmt.Errorf("unsafe cscli flag value: %q", arg)
}
continue
}
prevWasFlag = false
if allowedActions[arg] {
continue // known action words
continue
}
if arg == "raw" || arg == "json" || arg == "human" {
continue // output format values
continue
}
// Everything else (names, IDs) must match safe pattern
if !safeArg.MatchString(arg) {
return fmt.Errorf("unsafe cscli argument: %q", arg)
}
+13 -12
View File
@@ -116,18 +116,19 @@ type Machine struct {
}
type HubItem struct {
Author string `json:"author"`
Description string `json:"description"`
Downloaded bool `json:"downloaded"`
Installed bool `json:"installed"`
Local bool `json:"local"`
Name string `json:"name"`
Path string `json:"path"`
Stage string `json:"stage"`
Status string `json:"status"`
Tainted bool `json:"tainted"`
UpToDate bool `json:"up_to_date"`
Version string `json:"version"`
Author string `json:"author"`
Description string `json:"description"`
Downloaded bool `json:"downloaded"`
Installed bool `json:"installed"`
Local bool `json:"local"`
LocalVersion string `json:"local_version"`
Name string `json:"name"`
Path string `json:"path"`
Stage string `json:"stage"`
Status string `json:"status"`
Tainted bool `json:"tainted"`
UpToDate bool `json:"up_to_date"`
Version string `json:"version"`
}
type MetricsSection struct {