kgretzky/evilginx2 · error

sub_filters: missing `search` field

Error message

sub_filters: missing `search` field

What it means

Each `sub_filters` entry needs a `search` regular expression identifying the text to replace in matched responses. The parser found a nil `search` field and aborts validation. Without it the substitution engine has no pattern to act on.

Source

Thrown at core/phishlet.go:451

		p.proxyHosts[0].is_landing = true
	}

	if fp.SubFilters != nil {
		for _, sf := range *fp.SubFilters {
			if sf.Hostname == nil {
				return fmt.Errorf("sub_filters: missing `triggers_on` field")
			}
			if sf.Sub == nil {
				return fmt.Errorf("sub_filters: missing `orig_sub` field")
			}
			if sf.Domain == nil {
				return fmt.Errorf("sub_filters: missing `domain` field")
			}
			if sf.Mimes == nil {
				return fmt.Errorf("sub_filters: missing `mimes` field")
			}
			if sf.Search == nil {
				return fmt.Errorf("sub_filters: missing `search` field")
			}
			if sf.Replace == nil {
				return fmt.Errorf("sub_filters: missing `replace` field")
			}
			if sf.WithParams == nil {
				sf.WithParams = &[]string{}
			}

			for n := range *sf.Mimes {
				(*sf.Mimes)[n] = p.paramVal((*sf.Mimes)[n])
			}
			p.addSubFilter(p.paramVal(*sf.Hostname), p.paramVal(*sf.Sub), p.paramVal(*sf.Domain), *sf.Mimes, p.paramVal(*sf.Search), p.paramVal(*sf.Replace), sf.RedirectOnly, *sf.WithParams)
		}
	}
	if fp.JsInject != nil {
		for _, js := range *fp.JsInject {
			if js.TriggerDomains == nil {
				return fmt.Errorf("js_inject: missing `trigger_domains` field")

View on GitHub (pinned to 4c0988a1d9)

Solutions

  1. Add `search: '<escaped regex>'` (e.g. `search: 'accounts\\.example\\.com'`) to the entry
  2. Escape regex dots and remember the string is matched against response content, not the URL
  3. Reload and fix any subsequently reported missing fields (replace comes next)

Example fix

// before
sub_filters:
  - triggers_on: 'auth.example.com'
    orig_sub: 'accounts'
    domain: 'example.com'
    mimes: ['text/html']
    replace: 'accounts.{domain}'
// after
sub_filters:
  - triggers_on: 'auth.example.com'
    orig_sub: 'accounts'
    domain: 'example.com'
    mimes: ['text/html']
    search: 'accounts\\.example\\.com'
    replace: 'accounts.{domain}'
Defensive patterns

Strategy: validation

Validate before calling

import re
for i, sf in enumerate(cfg.get('sub_filters') or []):
    pat = sf.get('search')
    if pat is None:
        raise ValueError(f"sub_filters[{i}]: missing `search` field")
    re.compile(pat)

Type guard

func validSubFilter(sf SubFilter) bool { return sf.Search != nil }

Prevention

When it happens

Trigger: A `sub_filters:` entry omits `search:`; core/phishlet.go's `if sf.Search == nil` check fires before p.addSubFilter is called.

Common situations: Writing a filter that only has replace but forgetting search; YAML multiline regex formatting errors that silently dropped the key; schema confusion with js_inject which has no search field.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of kgretzky/evilginx2@4c0988a1d9 (2026-09-05). Data as JSON: /api/errors/8aeca9ced1016a14. Report an issue: GitHub.