{"record":{"id":"4f006ea6e4426dd5","repo":"kgretzky/evilginx2","slug":"phishlet-s-can-t-be-deleted-you-can-only-dele","errorCode":null,"errorMessage":"phishlet '%s' can't be deleted - you can only delete child phishlets.","messagePattern":"phishlet '(.+?)' can't be deleted - you can only delete child phishlets\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/config.go","lineNumber":592,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tsub_pl.ParentName = parent_site\n\n\tc.phishletNames = append(c.phishletNames, site)\n\tc.phishlets[site] = sub_pl\n\tc.VerifyPhishlets()\n\n\treturn nil\n}\n\nfunc (c *Config) DeleteSubPhishlet(site string) error {\n\tpl, err := c.GetPhishlet(site)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif pl.ParentName == \"\" {\n\t\treturn fmt.Errorf(\"phishlet '%s' can't be deleted - you can only delete child phishlets.\", site)\n\t}\n\n\tc.phishletNames = removeString(site, c.phishletNames)\n\tdelete(c.phishlets, site)\n\tdelete(c.phishletConfig, site)\n\tc.SavePhishlets()\n\treturn nil\n}\n\nfunc (c *Config) LoadSubPhishlets() {\n\tvar subphishlets []*SubPhishlet\n\tc.cfg.UnmarshalKey(CFG_SUBPHISHLETS, &subphishlets)\n\tfor _, spl := range subphishlets {\n\t\terr := c.AddSubPhishlet(spl.Name, spl.ParentName, spl.Params)\n\t\tif err != nil {\n\t\t\tlog.Error(\"phishlets: %s\", err)\n\t\t}\n\t}","sourceCodeStart":574,"sourceCodeEnd":610,"githubUrl":"https://github.com/kgretzky/evilginx2/blob/4c0988a1d9db4d172a185e979a38bfd0efdb5830/core/config.go#L574-L610","documentation":"DeleteSubPhishlet only allows deletion of child (inherited) phishlets. If the phishlet identified by `site` has no ParentName (i.e. it is a top-level phishlet), the config refuses to delete it and returns this error, protecting base phishlets from accidental removal.","triggerScenarios":"Calling cfg.DeleteSubPhishlet(site) with the name of a standalone/base phishlet (one created directly, not via CreateSubPhishlet), so pl.ParentName == \"\".","commonSituations":"Developers managing evilginx-style phishlet trees iterate over all phishlet names and call DeleteSubPhishlet on each, hitting the base phishlets; or they pass a typo'd name that resolves to a base phishlet instead of the intended child.","solutions":["Only call DeleteSubPhishlet for phishlets whose ParentName is non-empty; delete base phishlets through a different path if the API exposes one","Check c.phishlets[site].ParentName before calling, and skip base phishlets","Verify the site name refers to the child phishlet, not its parent"],"exampleFix":"// before\ncfg.DeleteSubPhishlet(\"linkedin\")\n// after\npl, _ := cfg.GetPhishlet(\"linkedin\")\nif pl.ParentName != \"\" {\n    cfg.DeleteSubPhishlet(\"linkedin\")\n}","handlingStrategy":"validation","validationCode":"func canDeleteSub(cfg *core.Config, site string) bool {\n    pl, err := cfg.GetPhishlet(site)\n    return err == nil && pl != nil && pl.ParentName != \"\"\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.DeleteSubPhishlet(site); err != nil {\n    log.Printf(\"skip %s: %v\", site, err)\n}","preventionTips":["Check ParentName before deleting","Enumerate only child phishlets when bulk-deleting","Confirm the exact child phishlet name"],"tags":["config","phishlet","go"],"backgroundTag":"phishlet-delete-not-child","analyzedSha":"4c0988a1d9db4d172a185e979a38bfd0efdb5830","analyzedAt":"2026-09-05T19:23:07.238Z","contentChangedAt":"2026-09-05T19:23:07.238Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}