kgretzky/evilginx2 · error

sub_filters: missing `replace` field

Error message

sub_filters: missing `replace` field

What it means

Each `sub_filters` entry requires a `replace` string substituted for the `search` regex matches. The entry's `replace` field was nil, so validation fails. This is the last mandatory sub_filter field checked; `with_params` is optional and defaults to an empty list when absent.

Source

Thrown at core/phishlet.go:454

	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")
			}
			if js.TriggerPaths == nil {
				return fmt.Errorf("js_inject: missing `trigger_paths` field")

View on GitHub (pinned to 4c0988a1d9)

Solutions

  1. Add `replace: '<replacement>'` (typically using the `{domain}` placeholder, e.g. `replace: 'accounts.{domain}'`) to the entry
  2. Confirm search and replace are consistent with each other (the replacement should re-substitute the phishing domain)
  3. Reload the phishlet; all remaining sub_filter checks should now pass

Example fix

// before
sub_filters:
  - triggers_on: 'auth.example.com'
    orig_sub: 'accounts'
    domain: 'example.com'
    mimes: ['text/html']
    search: 'accounts\\.example\\.com'
// 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

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

Type guard

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

Prevention

When it happens

Trigger: A `sub_filters:` entry omits `replace:`; core/phishlet.go's `if sf.Replace == nil` check fires after all other required fields validate.

Common situations: Truncating the entry after `search` while editing; assuming search-only filters are allowed; paste from docs cut off before the replace line.

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/d50e42fb36be73ee. Report an issue: GitHub.