kgretzky/evilginx2 · error
sub_filters: missing `triggers_on` field
Error message
sub_filters: missing `triggers_on` field
What it means
Thrown when iterating `sub_filters` entries and an entry lacks the field that should map to `triggers_on`. Note the message is misleading: the check is sf.Hostname == nil, i.e. the missing YAML key is actually `triggers_on`'s counterpart `hostname`? In upstream Evilginx3 the sub_filter fields are `triggers_on`, `orig_sub`, `domain`, `search`, `replace` mapped to struct fields where Hostname corresponds to `triggers_on` - so a missing `triggers_on:` key produces this error. sub_filters let a phishlet rewrite response bodies (e.g. strip logout links).
Source
Thrown at core/phishlet.go:439
}
if !session_set {
p.proxyHosts[0].handle_session = true
}
landing_set := false
for _, ph := range p.proxyHosts {
if ph.is_landing {
landing_set = true
break
}
}
if !landing_set {
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{}View on GitHub (pinned to 4c0988a1d9)
Solutions
- Add `triggers_on:` with the target hostname to each sub_filters entry
- Format triggers_on as the hostname string (e.g. accounts.example.com) that the filter applies to
- Keep orig_sub, domain, search, and replace present as well since each is checked next
Example fix
// before
sub_filters:
- orig_sub: accounts
domain: example.com
search: 'href="logout"'
replace: '#'
// after
sub_filters:
- triggers_on: 'accounts.example.com'
orig_sub: accounts
domain: example.com
search: 'href="logout"'
replace: '#'
Defensive patterns
Strategy: validation
Validate before calling
type subFilter struct {
Hostname *string `yaml:"triggers_on"`
Sub *string `yaml:"orig_sub"`
Domain *string `yaml:"domain"`
Mimes []string `yaml:"mimes"`
}
var fp struct { SubFilters *[]subFilter `yaml:"sub_filters"` }
yaml.Unmarshal(data, &fp)
for i, sf := range *fp.SubFilters {
if sf.Hostname == nil {
return fmt.Errorf("sub_filters[%d]: missing triggers_on", i)
}
} Type guard
func hasTriggersOn(sf SubFilter) bool {
return sf.Hostname != nil
} Try / catch
err := cfg.AddPhishlet("local", name)
if err != nil {
if strings.Contains(err.Error(), "sub_filters: missing `triggers_on`") {
log.Fatalf("phishlet %s: each sub_filters entry must start with triggers_on hostname", name)
}
return err
} Prevention
- Start every sub_filters entry with triggers_on followed by orig_sub, domain, search, replace
- Copy the six-field order from official phishlets
- Quote hostname values to avoid YAML parsing surprises
- Lint sub_filters indentation so all fields are siblings
When it happens
Trigger: A `sub_filters:` entry in the phishlet YAML omits `triggers_on: <hostname>` (or misspells/mis-indents it) so sf.Hostname stays nil during validation.
Common situations: Adding a sub_filter with only search/replace; typo like `trigger_on`; indentation nesting triggers_on under another key.
Related errors
- sub_filters: missing `orig_sub` field
- sub_filters: missing `domain` field
- sub_filters: missing `mimes` field
- sub_filters: missing `search` field
- sub_filters: missing `replace` field
AI-assisted analysis of kgretzky/evilginx2@4c0988a1d9 (2026-09-05).
Data as JSON: /api/errors/68fc256b98a55874.
Report an issue: GitHub.