#142·hetty

Scope Header Value Rules Are Ignored

Author: Ray0x01Created Apr 27, 2026Updated Apr 27, 2026

Security Report: Scope Header Value Rules Are Ignored

Summary

Hetty's scope rule handling contains a logic bug that causes header value expressions to be ignored when scope rules are created through the GraphQL API.

In SetScope, the code compiles the header key regex twice and never compiles the user-supplied header value regex. As a result, scope rules that are intended to match a specific header value instead reuse the header key pattern for both fields.

This can cause scope enforcement to behave differently from the operator's configuration, potentially including traffic that should be out of scope or excluding traffic that should be in scope.

Severity

Medium

This is a valid security-impacting logic issue in a proxy tool where scope is used to control what traffic is captured and acted on. The impact depends on how heavily the operator relies on scope for limiting collection, interception, and analysis.

Affected Component

  • Scope rule creation via GraphQL mutation
  • Scope matching behavior

Relevant code:

Technical Details

The SetScope mutation converts API input into internal scope.Rule objects:

go
func (r *mutationResolver) SetScope(ctx context.Context, input []ScopeRuleInput) ([]ScopeRule, error) {
    rules := make([]scope.Rule, len(input))

    for i, rule := range input {
        u, err := stringPtrToRegexp(rule.URL)
        if err != nil {
            return nil, fmt.Errorf("invalid URL in scope rule: %w", err)
        }

        var headerKey, headerValue *regexp.Regexp

        if rule.Header != nil {
            headerKey, err = stringPtrToRegexp(rule.Header.Key)
            if err != nil {
                return nil, fmt.Errorf("invalid header key in scope rule: %w", err)
            }

            headerValue, err = stringPtrToRegexp(rule.Header.Key)
            if err != nil {
                return nil, fmt.Errorf("invalid header value in scope rule: %w", err)
            }
        }

The second assignment should clearly use rule.Header.Value, but instead it uses rule.Header.Key again:

go
headerValue, err = stringPtrToRegexp(rule.Header.Key)

Because of that:

  • rule.Header.Value is ignored
  • headerKey and headerValue become the same regex
  • header-based scope rules can silently behave incorrectly

The internal scope matching logic expects key and value to be separate:

go
case r.Header.Key != nil && r.Header.Value != nil && keyMatches && valueMatches:
    return true

So when the stored Header.Value is actually derived from the key regex, the matching semantics become wrong.

Impact

This issue can undermine operator expectations about what is in scope.

Possible effects include:

  • traffic being treated as in-scope even though its header values should not match
  • traffic being treated as out-of-scope even though its header values should match
  • scope filters becoming unreliable for sensitive testing workflows
  • unintended collection, interception, or retention of traffic

For a proxy and interception tool, scope is often used as a safety boundary. If header-value-based scoping is silently broken, an operator may make decisions based on an incorrect understanding of what the tool is capturing or processing.

Reproduction

Example intention

An operator wants to scope traffic based on:

  • header key: Authorization
  • header value: ^Bearer internal-