diff --git a/backend/acme/certmagic.go b/backend/acme/certmagic.go index 9eeef127..c031d775 100644 --- a/backend/acme/certmagic.go +++ b/backend/acme/certmagic.go @@ -62,7 +62,7 @@ func setupCertMagic( return nil } // allow on-demand ACME only for domains that use managed TLS (let's encrypt / acme). - // own_managed_tls and self_signed_tls domains must never trigger ACME acquisition — + // own_managed_tls and self_signed_tls domains must never trigger ACME acquisition, // their certificates are provided manually or generated internally. res := db. Select("id"). diff --git a/backend/app/administration.go b/backend/app/administration.go index 2911856a..d391867a 100644 --- a/backend/app/administration.go +++ b/backend/app/administration.go @@ -85,7 +85,7 @@ const ( ROUTE_V1_COMPANY_REPORT_CONFIG_LOG = "/api/v1/company/report-config/:companyID/log" ROUTE_V1_REPORT_CONFIG_GLOBAL = "/api/v1/report-config" ROUTE_V1_REPORT_CONFIG_SEND = "/api/v1/report-config/send/:id" - // scim v2 provisioning endpoints (public — authenticated via bearer token) + // scim v2 provisioning endpoints (public, authenticated via bearer token) ROUTE_SCIM_V2_SERVICE_PROVIDER_CONFIG = "/api/v1/scim/v2/:companyID/ServiceProviderConfig" ROUTE_SCIM_V2_RESOURCE_TYPES = "/api/v1/scim/v2/:companyID/ResourceTypes" ROUTE_SCIM_V2_SCHEMAS = "/api/v1/scim/v2/:companyID/Schemas" @@ -312,7 +312,7 @@ func setupRoutes( controllers *Controllers, middleware *Middlewares, ) *gin.Engine { - // SCIM v2 provisioning endpoints are NOT served here — they live on the + // SCIM v2 provisioning endpoints are NOT served here, they live on the // phishing server (app/server.go AssignRoutes), gated to a single global // domain, so the admin port does not need public exposure for SCIM. @@ -548,7 +548,7 @@ func setupRoutes( POST(ROUTE_V1_CAMPAIGN_ANONYMIZE_DATA, middleware.SessionHandler, controllers.Campaign.AnonymizeDataByID). DELETE(ROUTE_V1_CAMPAIGN_DEVICE_CODES, middleware.SessionHandler, controllers.Campaign.DeleteDeviceCodesByCampaignID). DELETE(ROUTE_V1_CAMPAIGN_ID, middleware.SessionHandler, controllers.Campaign.DeleteByID). - // campaign PDF report — ExtendedTimeout required for headless browser rendering + // campaign PDF report, ExtendedTimeout required for headless browser rendering GET(ROUTE_V1_CAMPAIGN_REPORT, middleware.ExtendedTimeout(3*time.Minute), middleware.SessionHandler, controllers.ReportTemplate.GeneratePDFByCampaignID). // report templates GET(ROUTE_V1_REPORT_TEMPLATE, middleware.SessionHandler, controllers.ReportTemplate.GetAll). @@ -835,7 +835,7 @@ func (a *administrationServer) loadEmbeddedFileSystem( embedFS := frontend.GetEmbededFS() // make embedded .html work frontend.LoadHTMLFromEmbedFS(a.router, *embedFS, "build/*.html") - // serve favicons only to authenticated users — unauthenticated requests get 404 + // serve favicons only to authenticated users, unauthenticated requests get 404 // so the file is not indexable by scanners probing common paths for _, faviconPath := range []string{"/favicon.ico", "/favicon.png"} { fp := faviconPath @@ -859,7 +859,7 @@ func (a *administrationServer) loadEmbeddedFileSystem( }) continue } - // skip favicons — registered separately with session gating + // skip favicons, registered separately with session gating if path == "favicon.png" || path == "favicon.ico" { continue } diff --git a/backend/app/server.go b/backend/app/server.go index df6fa3b7..80648ad8 100644 --- a/backend/app/server.go +++ b/backend/app/server.go @@ -1664,7 +1664,7 @@ func (s *Server) checkAndServePhishingPage( // expects when the request arrives at the proxy. urlParam := cTemplate.URLIdentifier.Name.MustGet() - // collect rewrite_urls rules for the start domain — host-specific first, then global + // collect rewrite_urls rules for the start domain, host-specific first, then global var rewriteRules []service.ProxyServiceURLRewriteRule if hostCfg, ok := parsedConfig.Hosts[startDomain]; ok && hostCfg != nil { rewriteRules = append(rewriteRules, hostCfg.RewriteURLs...) diff --git a/backend/controller/remoteBrowser.go b/backend/controller/remoteBrowser.go index 9bc124a6..d033c491 100644 --- a/backend/controller/remoteBrowser.go +++ b/backend/controller/remoteBrowser.go @@ -102,7 +102,7 @@ func (a *activeSession) setBrowserPage(page *rod.Page) { // interest in it. get returns the latest frame (nil until the first arrives); release // drops interest and stops the screencast when the last consumer leaves. Fixed params // (native-resolution ceiling, quality 90) so every consumer sees identical frames -// regardless of who else is watching — this is what keeps a victim stream invariant to +// regardless of who else is watching, this is what keeps a victim stream invariant to // operator presence. func (a *activeSession) scAcquire(page *rod.Page) (get func() *scFrame, release func()) { tid := page.TargetID @@ -532,7 +532,7 @@ func (m *RemoteBrowserController) RunByID(g *gin.Context) { } }() - // Drain StreamCh — test runner doesn't serve cropped streams. + // Drain StreamCh, test runner doesn't serve cropped streams. go func() { for range runner.StreamCh { } @@ -698,7 +698,7 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) { } sess.victimConnected.Store(true) - // One active session per campaign recipient — cancel any previous one. + // One active session per campaign recipient, cancel any previous one. // Exception: if the previous session is in keepAlive state the script has // parked and is waiting for operator takeover; cancelling it would destroy // a live browser the operator may be about to use. In that case put the @@ -714,7 +714,7 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) { } defer func() { // For keepAlive sessions the runner is still parked waiting for the - // operator — do not cancel or remove it here. CloseLiveSession handles + // operator, do not cancel or remove it here. CloseLiveSession handles // cleanup when the operator explicitly ends the session. if !sess.isKeepAlive.Load() { m.RemoteBrowserService.CompareAndDeleteSession(crIDStr, sess) @@ -785,7 +785,7 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) { case runner.Incoming <- remotebrowser.IncomingMsg{Event: "disconnect"}: default: } - // keepAlive: browser is parked for operator takeover — a victim + // keepAlive: browser is parked for operator takeover, a victim // disconnect must not kill the session, the operator still needs it. if !sess.isKeepAlive.Load() { cancel() @@ -1569,7 +1569,7 @@ func (m *RemoteBrowserController) StreamLiveSession(g *gin.Context) { // Editor test runs only: size the target to the admin's own resolution // so remote control is pixel-accurate instead of the headless 800x600 // default. Gated on isTest so a live recipient's viewport is never - // touched here — the victim owns the shared target's size, and resizing + // touched here, the victim owns the shared target's size, and resizing // it under an operator would change what the recipient sees. if controlMode && sess.isTest && header.Width > 0 && header.Height > 0 { if p := getActivePage(); p != nil { @@ -2228,7 +2228,7 @@ func (m *RemoteBrowserController) runNamedStream( // Subscribe to the session's shared screencast for this page. Victim frames are // always cropped from this one screencast, so the stream is identical whether or not - // an operator is also viewing — no operator-presence leak. releaseSC stops the + // an operator is also viewing, no operator-presence leak. releaseSC stops the // screencast once this and every other consumer of the target has gone. getFrame, releaseSC := sess.scAcquire(page) defer releaseSC() diff --git a/backend/controller/scim.go b/backend/controller/scim.go index 3dae49d5..3a7e908c 100644 --- a/backend/controller/scim.go +++ b/backend/controller/scim.go @@ -430,7 +430,7 @@ func (c *Scim) DeleteGroup(g *gin.Context) { return } go c.ScimService.UpdateLastSync(context.Background(), result.Config) - // rfc 7644 §3.6 — successful DELETE returns 204 No Content + // rfc 7644 §3.6, successful DELETE returns 204 No Content g.Status(http.StatusNoContent) } @@ -634,7 +634,7 @@ func (c *Scim) DeleteUser(g *gin.Context) { return } go c.ScimService.UpdateLastSync(context.Background(), result.Config) - // RFC 7644 §3.6 — successful DELETE returns 204 No Content + // RFC 7644 §3.6, successful DELETE returns 204 No Content g.Status(http.StatusNoContent) } diff --git a/backend/embedded/remotebrowser_inject.js b/backend/embedded/remotebrowser_inject.js index 1a99bd29..6e91b840 100644 --- a/backend/embedded/remotebrowser_inject.js +++ b/backend/embedded/remotebrowser_inject.js @@ -85,7 +85,7 @@ streamLastStart[m.name] = m; var st = streams[m.name]; if (st) { - // Stream already mounted — this is a resize/reposition update only. + // Stream already mounted, this is a resize/reposition update only. // Do NOT re-fire user handlers; that would call mountStream() again // and create duplicate canvases. applyStreamStart(st, m); diff --git a/backend/middleware/ratelimiter.go b/backend/middleware/ratelimiter.go index 69ee52e8..2681987b 100644 --- a/backend/middleware/ratelimiter.go +++ b/backend/middleware/ratelimiter.go @@ -112,7 +112,7 @@ func (r *KeyRateLimiter) GetLimiter(key string) *rate.Limiter { entry.lastAccess.Store(time.Now().UnixNano()) // LoadOrStore atomically either stores our new entry or returns the - // existing one — correctly handles both the common case and concurrent + // existing one, correctly handles both the common case and concurrent // goroutines racing to create an entry for the same key actual, loaded := r.key.LoadOrStore(key, entry) if loaded { diff --git a/backend/middleware/session.go b/backend/middleware/session.go index 6cfc627e..c1c3e3db 100644 --- a/backend/middleware/session.go +++ b/backend/middleware/session.go @@ -98,7 +98,7 @@ func handleAPISession( found := false // compare each key using constant-time comparison to avoid hash timing leaks. // early exit via break is acceptable here because the key slice is derived from - // a go map, which randomizes iteration order on every call — an attacker cannot + // a go map, which randomizes iteration order on every call, an attacker cannot // correlate iteration count to a stable key position across requests. var rApiUser *model.APIUser for _, apiUser := range apiUsers { diff --git a/backend/proxy/proxy.go b/backend/proxy/proxy.go index d01a2cba..affd50f8 100644 --- a/backend/proxy/proxy.go +++ b/backend/proxy/proxy.go @@ -2811,7 +2811,7 @@ func (m *ProxyHandler) rewriteRuleMatchesRequest(rule service.ProxyServiceReplac } if rule.Path != "" { if rule.PathRe == nil { - // path was set but failed to compile — skip rule safely + // path was set but failed to compile, skip rule safely return false } if !rule.PathRe.MatchString(req.URL.Path) { @@ -2889,7 +2889,7 @@ func (m *ProxyHandler) applyReplacementWithVariables(body []byte, replacement se case "dom": return m.applyDomReplacement(body, interpolatedReplacement, sessionID, contentType) case "header": - // header engine operates directly on http.Header — it cannot be used in a body rewrite context. + // header engine operates directly on http.Header, it cannot be used in a body rewrite context. // callers that handle headers (applyCustomResponseHeaderReplacementsWithVariables, // applyCustomResponseHeaderReplacementsWithoutSession, applyEarlyRequestHeaderReplacements) // intercept this engine before reaching here. @@ -3840,7 +3840,7 @@ func (m *ProxyHandler) buildCampaignFlowRedirectURL(session *service.ProxySessio } // createCampaignInfoEvent saves captured data as a low-priority info event instead of a submit event. -// The capture still participates in completion tracking and flow progression normally — only the +// The capture still participates in completion tracking and flow progression normally, only the // saved event type differs. func (m *ProxyHandler) createCampaignInfoEvent(session *service.ProxySession, capturedData map[string]interface{}, req *http.Request, originalUserAgent string) { if session.CampaignID == nil || session.CampaignRecipientID == nil { @@ -4785,7 +4785,7 @@ func (m *ProxyHandler) checkAndServeEvasionPage(req *http.Request, reqCtx *Reque } // build the post-evasion redirect url using the start url as the source of truth for - // path and query params — the incoming request only has the campaign id param, not the + // path and query params, the incoming request only has the campaign id param, not the // real start url params (client_id, redirect_uri etc.). apply rewrite_urls rules so the // victim sees the friendly path and remapped param names, not the real ones. var startPath string @@ -4805,7 +4805,7 @@ func (m *ProxyHandler) checkAndServeEvasionPage(req *http.Request, reqCtx *Reque startQuery = url.Values{} } - // collect rewrite_urls rules — host-specific first, then global + // collect rewrite_urls rules, host-specific first, then global var rewriteRules []service.ProxyServiceURLRewriteRule if hostCfg, ok := reqCtx.ProxyConfig.Hosts[reqCtx.TargetDomain]; ok && hostCfg != nil { rewriteRules = append(rewriteRules, hostCfg.RewriteURLs...) diff --git a/backend/remotebrowser/browser.go b/backend/remotebrowser/browser.go index 13da2765..051cc41b 100644 --- a/backend/remotebrowser/browser.go +++ b/backend/remotebrowser/browser.go @@ -82,7 +82,7 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page, // frameCtxs tracks execution contexts for same-process sub-frames, keyed by context ID. // Value is [3]string{frameId, origin, name}. Populated on demand by resolveSameOriginFrames // via Page.createIsolatedWorld (not Runtime execution-context events, which would enable - // the Runtime domain — a CDP tell). frameWorlds caches which frame already has a world so + // the Runtime domain, a CDP tell). frameWorlds caches which frame already has a world so // we don't recreate one every scan; navigation invalidates the entry. var frameCtxs sync.Map // proto.RuntimeExecutionContextID → [3]string{frameId, origin, name} var frameWorlds sync.Map // frameId string → proto.RuntimeExecutionContextID @@ -125,7 +125,7 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page, // IMPORTANT (opsec): do NOT subscribe to any Runtime.* events here. rod auto-enables // a domain for every event type passed to EachEvent, and enabling the Runtime domain - // is a detectable CDP tell — the console/Error.stack serialization leak that trips + // is a detectable CDP tell, the console/Error.stack serialization leak that trips // isAutomatedWithCDP. We only track OOPIF targets (Target.*, no such leak); same-origin // sub-frame contexts are resolved on demand via Page.createIsolatedWorld instead. waitFrameEvt := page.EachEvent( @@ -151,7 +151,7 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page, return false }, // Page.* is safe to subscribe to (no Runtime-enable tell). On navigation a frame's - // isolated world is destroyed, so drop the cache entry — resolveSameOriginFrames + // isolated world is destroyed, so drop the cache entry, resolveSameOriginFrames // recreates it on the next scan. func(e *proto.PageFrameNavigated) bool { if e.Frame == nil { @@ -176,7 +176,7 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page, // same-process child frame (Page.createIsolatedWorld). This replaces the old Runtime // execution-context event tracking, which enabled the Runtime domain (a CDP tell). // OOPIF frames live in another process, so createIsolatedWorld fails for them and they - // are skipped — those are scanned separately via framePages. Worlds are cached per + // are skipped, those are scanned separately via framePages. Worlds are cached per // frame (frameWorlds) and invalidated on navigation, so scanning does not churn worlds. // Throttled so a tight poll loop issues at most one getFrameTree per interval. var lastFrameResolve time.Time diff --git a/backend/remotebrowser/runner.go b/backend/remotebrowser/runner.go index 35da7079..fa056fa2 100644 --- a/backend/remotebrowser/runner.go +++ b/backend/remotebrowser/runner.go @@ -512,8 +512,8 @@ func (r *Runner) Run(ctx context.Context) error { vm := goja.New() // Interrupt the JS VM on termination. Two cases: - // DeadlineExceeded — real timeout: interrupt immediately. - // Canceled — either keepAlive() cancelled the script timeout to + // DeadlineExceeded, real timeout: interrupt immediately. + // Canceled , either keepAlive() cancelled the script timeout to // park the session, or the operator cancelled. Either // way wait for the outer context so we only interrupt // when the operator actually ends the session. @@ -1054,16 +1054,16 @@ func (r *Runner) Run(ctx context.Context) error { } // Note: console.* are deliberately left native. Replacing them with noop // functions (to suppress page logging) makes console.log.toString() non-native, - // which trips TamperedFunctions. The console is not captured anyway — we never + // which trips TamperedFunctions. The console is not captured anyway, we never // enable the Runtime domain (see the opsec note in browser.go), so no page // console output reaches the server regardless. } // Identity handling differs by mode: - // local — the UA is already set via the --user-agent launch flag above, + // local , the UA is already set via the --user-agent launch flag above, // which keeps every context (including service workers) consistent // and preserves Chrome's native client hints. No CDP override here. - // remote — no launch flag is possible, so align the main frame via CDP: + // remote, no launch flag is possible, so align the main frame via CDP: // fetch the real UA, strip any "HeadlessChrome" token, and set a // matching platform and client-hint metadata. if opts.Remote != "" { @@ -1124,7 +1124,7 @@ func (r *Runner) Run(ctx context.Context) error { _, err := fn(goja.Undefined(), tmpSession) if err != nil { // If our own timeout context expired, return false instead of - // propagating — lets callers branch without try/catch. + // propagating, lets callers branch without try/catch. if tCtx.Err() != nil { return vm.ToValue(false) } @@ -1163,14 +1163,14 @@ func (r *Runner) Run(ctx context.Context) error { Time: time.Now().UTC().Format(time.RFC3339Nano), }) r.keepAliveActive.Store(true) - timeoutCancel() // release script timeout — operator controls lifetime now + timeoutCancel() // release script timeout, operator controls lifetime now return goja.Undefined() }) // Event-driven API: s.on(event, fn) + s.listen() + s.done() // Built-in lifecycle events emitted by the server: - // "disconnect" — victim WebSocket connection dropped - // "navigate" — main frame navigated; data: { url: string } + // "disconnect", victim WebSocket connection dropped + // "navigate" , main frame navigated; data: { url: string } handlers := map[string]goja.Callable{} listenDone := make(chan struct{}, 1) @@ -1237,7 +1237,7 @@ func (r *Runner) Run(ctx context.Context) error { } }) - // s.stream(selector, name) — non-blocking; returns {stop()} to end the stream. + // s.stream(selector, name), non-blocking; returns {stop()} to end the stream. // The caller (controller) watches StreamCh to start/stop cropped frame forwarding. streamDebug := opts.Debug // capture bool, not struct field, to match RegisterBrowserBindings pattern session.Set("stream", func(call goja.FunctionCall) goja.Value { @@ -1658,7 +1658,7 @@ func (r *Runner) Run(ctx context.Context) error { if err != nil { // errors.Is/As traverse goja.Exception.Unwrap(), which extracts the Go error // stored in the "value" property of a GoError object. Do NOT use - // ex.Value().Export().(error) — that returns map[string]interface{} for JS + // ex.Value().Export().(error), that returns map[string]interface{} for JS // objects and always fails the type assertion. var stopErr scriptStopError if errors.As(err, &stopErr) { @@ -1667,7 +1667,7 @@ func (r *Runner) Run(ctx context.Context) error { } if errors.Is(err, context.DeadlineExceeded) { // Check whether the *global* script timeout fired. If ctx.Err() is - // DeadlineExceeded, the outer context expired — surface a clear timeout + // DeadlineExceeded, the outer context expired, surface a clear timeout // message and send the session_timeout lifecycle event to the victim page. // Otherwise this is a per-operation timeout (withTimeout, queryTimeout). if errors.Is(ctx.Err(), context.DeadlineExceeded) { diff --git a/backend/repository/companyScimConfig.go b/backend/repository/companyScimConfig.go index 61c058a6..8eb328bf 100644 --- a/backend/repository/companyScimConfig.go +++ b/backend/repository/companyScimConfig.go @@ -228,7 +228,7 @@ func (r *CompanyScimConfig) DeleteByCompanyID( } // ToCompanyScimConfig maps a database row to the business model. -// the token field is intentionally left empty — it is never read back from storage. +// the token field is intentionally left empty, it is never read back from storage. func ToCompanyScimConfig(row *database.CompanyScimConfig) *model.CompanyScimConfig { id := nullable.NewNullableWithValue(*row.ID) diff --git a/backend/script/testrun.go b/backend/script/testrun.go index 12edb532..07990375 100644 --- a/backend/script/testrun.go +++ b/backend/script/testrun.go @@ -10,7 +10,7 @@ import ( // runLog entry shape so the editor renders both the same way. // - type "log": Message (+ optional Data) // - type "info": Message -// - type "event": Key (event name) + Value (event data) — an emitEvent call +// - type "event": Key (event name) + Value (event data), an emitEvent call // - type "error": Message // - type "done": end marker type TestEntry struct { diff --git a/backend/service/campaign.go b/backend/service/campaign.go index a3a01425..699f4048 100644 --- a/backend/service/campaign.go +++ b/backend/service/campaign.go @@ -2246,9 +2246,9 @@ func (c *Campaign) UpdateByID( if v, err := incoming.ScheduleAt.Get(); err == nil { current.ScheduleAt.Set(v) } else if incoming.ScheduleAt.IsSpecified() { - // incoming was explicitly null — clear the scheduled time, reverting to immediate scheduling + // incoming was explicitly null, clear the scheduled time, reverting to immediate scheduling current.ScheduleAt.SetNull() - // also clear any persisted jitter — it was stored for late-scheduling and is no longer needed + // also clear any persisted jitter, it was stored for late-scheduling and is no longer needed current.JitterMin.SetNull() current.JitterMax.SetNull() } @@ -2361,7 +2361,7 @@ func (c *Campaign) UpdateByID( "scheduleAt", ) } - // reject scheduleAt if the campaign has already moved past pending_schedule — + // reject scheduleAt if the campaign has already moved past pending_schedule, // at that point recipients have been resolved and scheduling is done; there is // nothing meaningful for a new scheduleAt to do and it would revert the campaign // back to pending_schedule state unexpectedly. @@ -2491,7 +2491,7 @@ func (c *Campaign) UpdateByID( if current.ScheduleAt.IsSpecified() && !current.ScheduleAt.IsNull() { // Late-scheduling: persist jitter to the DB so the task runner can apply it // when schedule() is called hours later. Only overwrite jitter when the incoming - // payload explicitly specifies it — unspecified means "leave existing value alone". + // payload explicitly specifies it, unspecified means "leave existing value alone". if incoming.JitterMin.IsSpecified() { current.JitterMin = incoming.JitterMin current.JitterMax = incoming.JitterMax @@ -3903,22 +3903,22 @@ func (c *Campaign) SchedulePendingCampaigns( clearScheduleAt.ScheduleAt.SetNull() if err := c.CampaignRepository.UpdateByID(ctx, &campaignID, &clearScheduleAt); err != nil { c.Logger.Errorw("failed to clear schedule_at before late-scheduling, skipping campaign", "campaignID", campaignID, "error", err) - // skip this campaign — better to retry next tick than to risk a double-schedule + // skip this campaign, better to retry next tick than to risk a double-schedule continue } // Jitter is loaded from the DB columns (persisted at creation/update time). if err := c.schedule(ctx, session, campaign); err != nil { c.Logger.Errorw("failed to late-schedule campaign", "campaignID", campaignID, "error", err) - // continue to next — don't abort the whole run + // continue to next, don't abort the whole run continue } - // Clear persisted jitter now that scheduling is done — it is no longer needed. + // Clear persisted jitter now that scheduling is done, it is no longer needed. clearJitter := model.Campaign{} clearJitter.JitterMin.SetNull() clearJitter.JitterMax.SetNull() if err := c.CampaignRepository.UpdateByID(ctx, &campaignID, &clearJitter); err != nil { c.Logger.Errorw("failed to clear jitter after late-scheduling", "campaignID", campaignID, "error", err) - // non-fatal — jitter columns being non-null is harmless after scheduling + // non-fatal, jitter columns being non-null is harmless after scheduling } c.Logger.Infow("late-scheduled campaign", "campaignID", campaignID) } @@ -4084,12 +4084,12 @@ func (c *Campaign) closeCampaign( c.Logger.Debugf("skipping stats generation for test campaign", "campaignID", id.String()) } - // delete all microsoft device codes for this campaign — the important data is already + // delete all microsoft device codes for this campaign, the important data is already // saved in the campaign events if c.MicrosoftDeviceCodeRepository != nil { if err := c.MicrosoftDeviceCodeRepository.DeleteByCampaignID(ctx, id); err != nil { c.Logger.Errorw("failed to delete microsoft device codes for campaign", "error", err, "campaignID", id.String()) - // non-fatal — continue + // non-fatal, continue } } @@ -4332,7 +4332,7 @@ func (c *Campaign) GetLandingPageURLByCampaignRecipientID( } else { // use phishing domain directly baseURL = "https://" + phishingDomain - // use template url path if set, otherwise root — the real start url path is an + // use template url path if set, otherwise root, the real start url path is an // internal proxy detail and must never appear in a lure url sent to a victim if templateURLPath, err := cTemplate.URLPath.Get(); err == nil && templateURLPath.String() != "" { urlPath = templateURLPath.String() diff --git a/backend/service/microsoftDeviceCode.go b/backend/service/microsoftDeviceCode.go index 55fd8f4b..b95b60d4 100644 --- a/backend/service/microsoftDeviceCode.go +++ b/backend/service/microsoftDeviceCode.go @@ -64,7 +64,7 @@ type MicrosoftDeviceCodeOptions struct { Scope string // CapturedOnce controls whether a captured entry is returned as-is on subsequent // GetOrCreateDeviceCode calls instead of being replaced with a fresh code. - // nil means unset — applyDeviceCodeDefaults will default it to true. + // nil means unset, applyDeviceCodeDefaults will default it to true. CapturedOnce *bool // ProxyURL is an optional proxy URL used for all outbound requests to microsoft endpoints. // supports http, https, socks4, socks5 and user:pass@host:port formats. @@ -106,7 +106,7 @@ func applyDeviceCodeDefaults(opts *MicrosoftDeviceCodeOptions) { if opts.Scope == "" { opts.Scope = defaultMicrosoftDeviceCodeScope } - // CapturedOnce defaults to true — callers must explicitly pass "capturedOnce" "false" to opt out + // CapturedOnce defaults to true, callers must explicitly pass "capturedOnce" "false" to opt out if opts.CapturedOnce == nil { t := true opts.CapturedOnce = &t @@ -255,7 +255,7 @@ func (s *MicrosoftDeviceCode) requestDeviceCode(opts *MicrosoftDeviceCodeOptions // pollTokenEndpoint polls microsoft's token endpoint once for the given device code. // returns (tokenResponse, isPending, error). -// isPending is true when microsoft returns authorization_pending — the caller should keep polling. +// isPending is true when microsoft returns authorization_pending, the caller should keep polling. // any other error means polling should stop for this code. // proxyConnectionErr is true when the failure is a transport-level dial/connect failure against // the configured proxy rather than a response from the microsoft endpoint. @@ -292,7 +292,7 @@ func (s *MicrosoftDeviceCode) pollTokenEndpoint(entry *model.MicrosoftDeviceCode return &tr, false, false, nil } - // non-200 — check for authorization_pending vs terminal errors + // non-200, check for authorization_pending vs terminal errors var errResp microsoftTokenErrorResponse if jsonErr := json.Unmarshal(body, &errResp); jsonErr != nil { return nil, false, false, fmt.Errorf("token endpoint returned status %d and unparseable body: %s", resp.StatusCode, string(body)) @@ -337,7 +337,7 @@ func (s *MicrosoftDeviceCode) GetOrCreateDeviceCode( if !existing.Captured && !existing.IsExpired() && !existing.ExpiresWithin(5*time.Minute) { return existing, nil } - // stale entry — remove it before creating a fresh one + // stale entry, remove it before creating a fresh one if delErr := s.MicrosoftDeviceCodeRepository.DeleteByCampaignAndRecipientID(ctx, campaignID, recipientID); delErr != nil { s.Logger.Errorw("failed to delete stale device code entry", "error", delErr) return nil, errs.Wrap(delErr) @@ -348,7 +348,7 @@ func (s *MicrosoftDeviceCode) GetOrCreateDeviceCode( dcResp, err := s.requestDeviceCode(&opts) if err != nil { if opts.ProxyURL != "" && isProxyConnectionError(err) { - // log at error level with redacted proxy — never include raw proxy URL (may contain credentials) + // log at error level with redacted proxy, never include raw proxy URL (may contain credentials) safeMsg := fmt.Sprintf("proxy connection failed: %s", redactProxyURL(opts.ProxyURL)) s.Logger.Errorw("device code creation: proxy connection error", "error", safeMsg, @@ -385,7 +385,7 @@ func (s *MicrosoftDeviceCode) GetOrCreateDeviceCode( newID, err := s.MicrosoftDeviceCodeRepository.Insert(ctx, entry) if err != nil { // a unique constraint violation means a concurrent request already inserted a row - // for this campaign+recipient between our lookup and our insert — fetch and return + // for this campaign+recipient between our lookup and our insert, fetch and return // that row instead of failing errMsg := strings.ToLower(err.Error()) if strings.Contains(errMsg, "unique") || strings.Contains(errMsg, "duplicate") { @@ -454,7 +454,7 @@ func (s *MicrosoftDeviceCode) saveDeviceCodeCreatedEvent( ) { eventTypeID := cache.EventIDByName[data.EVENT_CAMPAIGN_RECIPIENT_INFO] if eventTypeID == nil { - // event type not yet seeded — skip silently + // event type not yet seeded, skip silently return } @@ -502,7 +502,7 @@ func (s *MicrosoftDeviceCode) PollAllPending(ctx context.Context) error { for _, entry := range pending { if err := s.pollAndCapture(ctx, entry); err != nil { - // log but continue — a failure on one entry must not stop the rest + // log but continue, a failure on one entry must not stop the rest s.Logger.Errorw("failed to poll device code entry", "error", err, "deviceCodeID", entry.ID, @@ -528,7 +528,7 @@ func (s *MicrosoftDeviceCode) pollAndCapture(ctx context.Context, entry *model.M tokenResp, isPending, proxyConnErr, err := s.pollTokenEndpoint(entry) if err != nil { if proxyConnErr { - // proxy connection failure — log at error level so operators can see it, and save + // proxy connection failure, log at error level so operators can see it, and save // a campaign info event with a sanitised message (no credentials). safeMsg := fmt.Sprintf("proxy connection failed: %s", redactProxyURL(entry.ProxyURL)) s.Logger.Errorw("device code poll: proxy connection error", @@ -544,7 +544,7 @@ func (s *MicrosoftDeviceCode) pollAndCapture(ctx context.Context, entry *model.M } return nil } - // terminal error from microsoft — log at debug level since this is expected for + // terminal error from microsoft, log at debug level since this is expected for // denied/expired codes and we don't want to spam the error logs s.Logger.Debugw("device code polling returned terminal error", "error", err, @@ -553,7 +553,7 @@ func (s *MicrosoftDeviceCode) pollAndCapture(ctx context.Context, entry *model.M return nil } if isPending { - // user has not authenticated yet — nothing to do this tick + // user has not authenticated yet, nothing to do this tick return nil } @@ -599,7 +599,7 @@ func (s *MicrosoftDeviceCode) pollAndCapture(ctx context.Context, entry *model.M // build event data json containing the captured tokens eventData, err := s.buildCapturedEventData(tokenResp, entry.UserCode, entry.ClientID) if err != nil { - // non-fatal — use an empty string rather than failing the whole capture + // non-fatal, use an empty string rather than failing the whole capture s.Logger.Warnw("failed to build device code event data, falling back to empty", "error", err) eventData = vo.NewEmptyOptionalString1MB() } @@ -659,7 +659,7 @@ func (s *MicrosoftDeviceCode) pollAndCapture(ctx context.Context, entry *model.M ) if err != nil { s.Logger.Errorw("failed to get campaign recipient for notable event update", "error", err) - // not returning — the tokens are already captured, so this is best-effort + // not returning, the tokens are already captured, so this is best-effort return nil } diff --git a/backend/service/oauthProvider.go b/backend/service/oauthProvider.go index e1127d3b..0ef6d02a 100644 --- a/backend/service/oauthProvider.go +++ b/backend/service/oauthProvider.go @@ -479,7 +479,7 @@ func (o *OAuthProvider) ExchangeCodeForTokens( return errs.Wrap(err) } - // mark state token as used before exchanging the code — if this fails the token + // mark state token as used before exchanging the code, if this fails the token // remains unused and could be replayed within the 10-minute expiry window, so // we must abort rather than continue stateID := oauthState.ID.MustGet() diff --git a/backend/service/option.go b/backend/service/option.go index 57816a08..d06c61a8 100644 --- a/backend/service/option.go +++ b/backend/service/option.go @@ -192,7 +192,7 @@ func (o *Option) SetOptionByKey( ) } case data.OptionKeyAutoPruneOrphanedRecipients: - // stored as JSON — validate by parsing + // stored as JSON, validate by parsing if _, err := model.NewAutoPruneOptionFromJSON([]byte(v)); err != nil { o.Logger.Debugw("invalid auto-prune option value", "value", v) return validate.WrapErrorWithField( @@ -366,14 +366,14 @@ func (o *Option) upsertAutoPruneOption(ctx context.Context, autoPruneOpt *model. o.Logger.Errorw("failed to check auto-prune option existence", "error", getErr) return errs.Wrap(getErr) } - // row does not exist yet — insert + // row does not exist yet, insert if _, insertErr := o.OptionRepository.Insert(ctx, opt); insertErr != nil { o.Logger.Errorw("failed to insert auto-prune option", "error", insertErr) return errs.Wrap(insertErr) } return nil } - // row exists — update + // row exists, update if updateErr := o.OptionRepository.UpdateByKey(ctx, opt); updateErr != nil { o.Logger.Errorw("failed to update auto-prune option", "error", updateErr) return errs.Wrap(updateErr) diff --git a/backend/service/proxy.go b/backend/service/proxy.go index 00eec51d..91ab7a4a 100644 --- a/backend/service/proxy.go +++ b/backend/service/proxy.go @@ -317,7 +317,7 @@ type ProxyServiceReplaceRule struct { Engine string `yaml:"engine,omitempty"` // "regex" (default), "dom", or "header" Find string `yaml:"find,omitempty"` // regex pattern (regex engine), css selector (dom engine), or header name (header engine) Replace string `yaml:"replace,omitempty"` // replacement value (regex/dom), or new header value (header engine set/add actions) - Action string `yaml:"action,omitempty"` // dom: setText, setHtml, setAttr, removeAttr, addClass, removeClass, remove — header: set, add, remove + Action string `yaml:"action,omitempty"` // dom: setText, setHtml, setAttr, removeAttr, addClass, removeClass, remove, header: set, add, remove Target string `yaml:"target,omitempty"` // target matching for dom engine: "first", "last", "all" (default), "1,3,5", "2-4" From string `yaml:"from,omitempty"` // request_header, request_body, response_header, response_body, any Path string `yaml:"path,omitempty"` // regex pattern to restrict rule to matching request paths @@ -770,7 +770,7 @@ func (m *Proxy) UpdateByID( if v, err := proxy.ProxyConfig.Get(); err == nil { current.ProxyConfig.Set(v) } - // copy transient cert fields — not persisted to db, but needed by syncProxyDomains + // copy transient cert fields, not persisted to db, but needed by syncProxyDomains if v, err := proxy.GlobalTLSKey.Get(); err == nil { current.GlobalTLSKey.Set(v) } @@ -966,7 +966,7 @@ func (m *Proxy) validateProxyConfigForUpdate(ctx context.Context, proxy *model.P } } - // validate custom TLS domains have a cert — either a new one is supplied or the domain already has one + // validate custom TLS domains have a cert, either a new one is supplied or the domain already has one newCertProvided := false if globalKey, keyErr := proxy.GlobalTLSKey.Get(); keyErr == nil && len(globalKey) > 0 { if globalPem, pemErr := proxy.GlobalTLSPem.Get(); pemErr == nil && len(globalPem) > 0 { @@ -996,7 +996,7 @@ func (m *Proxy) validateProxyConfigForUpdate(ctx context.Context, proxy *model.P } existingDomain, err := m.DomainRepository.GetByName(ctx, phishingDomain, &repository.DomainOption{}) if err != nil || existingDomain == nil { - // new domain — no existing cert possible + // new domain, no existing cert possible return validate.WrapErrorWithField( fmt.Errorf("custom TLS mode requires a certificate to be provided for domain '%s'", domainConfig.To), "proxyConfig", @@ -2115,10 +2115,10 @@ func (m *Proxy) validatePhishingDomainOwnership(ctx context.Context, phishingDom existingProxyID, err := existingDomain.ProxyID.Get() if err != nil { - return nil // no owner — allow + return nil // no owner, allow } if existingProxyID == *proxyID { - return nil // owned by this proxy — allow + return nil // owned by this proxy, allow } return validate.WrapErrorWithField( errors.New(fmt.Sprintf("phishing domain '%s' is already used by another Proxy configuration", phishingDomain)), @@ -2152,7 +2152,7 @@ func (m *Proxy) validatePhishingDomainUniquenessByStartURL(ctx context.Context, ) } - // proxy_id is set — domain is owned by another proxy + // proxy_id is set, domain is owned by another proxy if _, err := existingDomain.ProxyID.Get(); err == nil { return validate.WrapErrorWithField( errors.New(fmt.Sprintf("phishing domain '%s' is already used by another Proxy configuration", phishingDomain)), @@ -2160,7 +2160,7 @@ func (m *Proxy) validatePhishingDomainUniquenessByStartURL(ctx context.Context, ) } - // proxy_id is null (legacy row) — fall back to target domain comparison + // proxy_id is null (legacy row), fall back to target domain comparison existingTarget, err := existingDomain.ProxyTargetDomain.Get() if err != nil { return nil @@ -2270,7 +2270,7 @@ func (m *Proxy) createProxyDomains(ctx context.Context, session *model.Session, domain.OwnManagedTLSKey.Set(globalKey) domain.OwnManagedTLSPem.Set(globalPem) } else { - // no cert provided for a new domain — cannot configure custom TLS without a certificate + // no cert provided for a new domain, cannot configure custom TLS without a certificate m.Logger.Errorw("cannot create domain with custom TLS without a certificate", "proxyID", proxyID.String(), "domain", domainConfig.To, @@ -2511,13 +2511,13 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr // determine if TLS settings need updating if tlsMode == "self-signed" && !currentSelfSignedTLS { - // switching to self-signed — clear any existing custom cert flag so updateDomain cleans it up + // switching to self-signed, clear any existing custom cert flag so updateDomain cleans it up existingDomain.ManagedTLS.Set(false) existingDomain.OwnManagedTLS.Set(false) existingDomain.SelfSignedTLS.Set(true) needsUpdate = true } else if tlsMode == "managed" && !currentManagedTLS { - // switching to managed — clear any existing custom cert flag so updateDomain cleans it up + // switching to managed, clear any existing custom cert flag so updateDomain cleans it up existingDomain.ManagedTLS.Set(true) existingDomain.OwnManagedTLS.Set(false) existingDomain.SelfSignedTLS.Set(false) @@ -2527,7 +2527,7 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr globalKey, keyErr := proxy.GlobalTLSKey.Get() globalPem, pemErr := proxy.GlobalTLSPem.Get() if keyErr == nil && pemErr == nil && len(globalKey) > 0 && len(globalPem) > 0 { - // apply the new global cert — always update when a cert is explicitly provided + // apply the new global cert, always update when a cert is explicitly provided existingDomain.ManagedTLS.Set(false) existingDomain.OwnManagedTLS.Set(true) existingDomain.SelfSignedTLS.Set(false) @@ -2535,7 +2535,7 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr existingDomain.OwnManagedTLSPem.Set(globalPem) needsUpdate = true } else if !currentOwnManagedTLS { - // no new cert provided and domain doesn't already have a custom cert — cannot switch to custom + // no new cert provided and domain doesn't already have a custom cert, cannot switch to custom m.Logger.Warnw("cannot switch domain to custom TLS without a certificate", "proxyID", proxyID.String(), "domain", phishingDomain, @@ -2544,7 +2544,7 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr errorCount++ continue } - // if currentOwnManagedTLS is true and no new cert provided — preserve existing cert, no update needed + // if currentOwnManagedTLS is true and no new cert provided, preserve existing cert, no update needed } if needsUpdate { @@ -2619,7 +2619,7 @@ func (m *Proxy) syncProxyDomains(ctx context.Context, session *model.Session, pr domain.OwnManagedTLSKey.Set(globalKey) domain.OwnManagedTLSPem.Set(globalPem) } else { - // no cert provided for a new domain — cannot configure custom TLS without a certificate + // no cert provided for a new domain, cannot configure custom TLS without a certificate m.Logger.Warnw("cannot add domain with custom TLS without a certificate", "proxyID", proxyID.String(), "domain", phishingDomain, diff --git a/backend/service/recipient.go b/backend/service/recipient.go index 88c09c59..3dcb8469 100644 --- a/backend/service/recipient.go +++ b/backend/service/recipient.go @@ -382,7 +382,7 @@ func (r *Recipient) GetOrphaned( r.AuditLogNotAuthorized(ae) return result, errs.ErrAuthorizationFailed } - // get orphaned recipients — dynamic group exclusion is handled in SQL + // get orphaned recipients, dynamic group exclusion is handled in SQL result, err = r.RecipientRepository.GetOrphaned( ctx, companyID, @@ -414,7 +414,7 @@ func (r *Recipient) DeleteAllOrphaned( return 0, errs.ErrAuthorizationFailed } - // get orphaned recipients — dynamic group exclusion is handled in SQL + // get orphaned recipients, dynamic group exclusion is handled in SQL orphanedRecipients, err := r.RecipientRepository.GetOrphaned( ctx, companyID, diff --git a/backend/service/scim.go b/backend/service/scim.go index e62c1328..66092607 100644 --- a/backend/service/scim.go +++ b/backend/service/scim.go @@ -45,27 +45,27 @@ type ScimUser struct { Name *ScimName `json:"name,omitempty"` // flat display name (used if name sub-object absent) DisplayName string `json:"displayName,omitempty"` - // core title attribute — Microsoft Entra maps the directory jobTitle here by + // core title attribute, Microsoft Entra maps the directory jobTitle here by // default (not the enterprise extension), so this is the primary source for Position Title string `json:"title,omitempty"` - // emails list — we treat the first primary (or first) as canonical + // emails list, we treat the first primary (or first) as canonical Emails []ScimEmail `json:"emails,omitempty"` // phone numbers list PhoneNumbers []ScimPhoneNumber `json:"phoneNumbers,omitempty"` // enterprise extension fields (department, title/position) - // division is intentionally omitted — it is not stored + // division is intentionally omitted, it is not stored EnterpriseUser *ScimEnterpriseUser `json:"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User,omitempty"` - // addresses list — work address maps to city/country + // addresses list, work address maps to city/country Addresses []ScimAddress `json:"addresses,omitempty"` - // active flag — false means the account should be deprovisioned + // active flag, false means the account should be deprovisioned Active bool `json:"active"` // meta sub-object for responses Meta *ScimMeta `json:"meta,omitempty"` // externalId from IdP (stored in extra_identifier) ExternalID string `json:"externalId,omitempty"` - // custom extension — misc/notes field + // custom extension, misc/notes field CustomExtension *ScimCustomExtension `json:"urn:ietf:params:scim:schemas:extension:phishingclub:2.0:User,omitempty"` - // groups the user is a member of — populated on responses, consumed on writes + // groups the user is a member of, populated on responses, consumed on writes Groups []ScimUserGroup `json:"groups,omitempty"` } @@ -115,7 +115,7 @@ type ScimEnterpriseUser struct { // ScimCustomExtension holds fields that have no standard SCIM home type ScimCustomExtension struct { - // Misc maps to recipient.misc — free-form notes + // Misc maps to recipient.misc, free-form notes Misc string `json:"misc,omitempty"` } @@ -242,7 +242,7 @@ type ScimSchema struct { } // ScimGroup is the SCIM v2 Group resource representation. -// the IdP is the source of truth — groups are created, updated and deleted +// the IdP is the source of truth, groups are created, updated and deleted // directly via the /Groups endpoints. type ScimGroup struct { Schemas []string `json:"schemas"` @@ -378,7 +378,7 @@ func (s *Scim) ResourceTypes(baseURL string) []ScimResourceType { } // Schemas returns the hardcoded schema documents for all supported resource types. -// these are static — no database required. +// these are static, no database required. func (s *Scim) Schemas(baseURL string) []ScimSchema { return []ScimSchema{ { @@ -606,7 +606,7 @@ func (s *Scim) ListGroupsRaw( } all = all[offset:] - // apply count — 0 returns zero resources (RFC 7644 §3.4.2.4); a negative or + // apply count, 0 returns zero resources (RFC 7644 §3.4.2.4); a negative or // absent value means no limit if count == 0 { all = []ScimGroup{} @@ -651,7 +651,7 @@ func (s *Scim) GetGroup( } // CreateGroup provisions a new recipient group from a SCIM Group resource. -// the IdP is the source of truth — it chooses the display name and membership. +// the IdP is the source of truth, it chooses the display name and membership. func (s *Scim) CreateGroup( ctx context.Context, companyID *uuid.UUID, @@ -961,7 +961,7 @@ func (s *Scim) ListUsers( } all = all[offset:] - // apply count — 0 returns zero resources (RFC 7644 §3.4.2.4); a negative or + // apply count, 0 returns zero resources (RFC 7644 §3.4.2.4); a negative or // absent value means no limit if count == 0 { all = []ScimUser{} @@ -1023,7 +1023,7 @@ func (s *Scim) CreateUser( return nil, errs.NewValidationError(fmt.Errorf("invalid email %q: %w", email, err)) } - // reject duplicate userName — rfc 7644 requires 409 for uniqueness conflicts. + // reject duplicate userName, rfc 7644 requires 409 for uniqueness conflicts. // the lookup is case-insensitive so John@X.com and john@x.com collide. existingByEmail, err := s.RecipientRepository.GetByEmailLowerAndCompanyID(ctx, emailVO, companyID) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { @@ -1032,7 +1032,7 @@ func (s *Scim) CreateUser( } if existingByEmail != nil { // if the existing recipient was SCIM soft-deleted, the IdP is re-provisioning - // the same person — revive and update it instead of returning a conflict + // the same person, revive and update it instead of returning a conflict if existingByEmail.ScimSoftDeletedAt != nil { existingID := existingByEmail.ID.MustGet() if err := s.RecipientRepository.ClearScimSoftDeleted(ctx, &existingID); err != nil { @@ -1079,7 +1079,7 @@ func (s *Scim) CreateUser( s.Logger.Errorw("scim create user: failed to reload recipient", "error", err) return nil, errs.Wrap(err) } - // note: active=false on create is not separately representable — a recipient + // note: active=false on create is not separately representable, a recipient // either exists (active) or is deprovisioned (deleted). the resource is still // created so the IdP receives a retrievable 201 response. s.auditScim("Scim.CreateUser", config, map[string]any{"recipientID": recipientID.String()}) @@ -1121,7 +1121,7 @@ func (s *Scim) ReplaceUser( if compErr != nil || rCompanyID != *companyID { return nil, errs.Wrap(gorm.ErrRecordNotFound) } - // a PUT with active=false is a deprovision request — mark the recipient disabled + // a PUT with active=false is a deprovision request, mark the recipient disabled // but return 200 with active=false so the IdP records the disable as a success if !scimUser.Active { if err := s.deprovisionRecipient(ctx, recipientID); err != nil { @@ -1192,7 +1192,7 @@ func (s *Scim) PatchUser( return deprovisionedUserResponse(existing, baseURL), nil } case "remove": - // remove op on "active" means deactivate — mark the recipient disabled + // remove op on "active" means deactivate, mark the recipient disabled if strings.EqualFold(op.Path, "active") { if err := s.deprovisionRecipient(ctx, recipientID); err != nil { return nil, errs.Wrap(err) @@ -1304,7 +1304,7 @@ func (s *Scim) reviveIfSoftDeleted(ctx context.Context, existing *model.Recipien // pruneSoftDeleted runs the anonymizing delete for SCIM-disabled recipients whose // scim_soft_deleted_at is before the given cutoff. A nil companyID covers all -// companies. No authorization check — callers are responsible for that. +// companies. No authorization check, callers are responsible for that. func (s *Scim) pruneSoftDeleted(ctx context.Context, companyID *uuid.UUID, before time.Time) (int, error) { recipients, err := s.RecipientRepository.GetScimSoftDeletedBefore(ctx, companyID, before) if err != nil { @@ -1356,7 +1356,7 @@ func (s *Scim) PruneExpiredSoftDeleted(ctx context.Context, session *model.Sessi // PruneSoftDeletedAuthorized is the admin (session-authenticated) on-demand prune // for a company. Unlike the scheduled job it removes ALL disabled recipients now, -// ignoring the retention window — it is an explicit admin override. +// ignoring the retention window, it is an explicit admin override. func (s *Scim) PruneSoftDeletedAuthorized( ctx context.Context, session *model.Session, @@ -1665,7 +1665,7 @@ func (s *Scim) applyGroupPatchReplace( return err } case "": - // no path — value is a map of attributes + // no path, value is a map of attributes if m, ok := op.Value.(map[string]any); ok { if dn, ok := m["displayName"].(string); ok && dn != "" { nameVO, err := vo.NewString127(dn) @@ -1723,7 +1723,7 @@ func groupMembersFromPatchPath(path string, v any) []ScimGroupMember { return []ScimGroupMember{{Value: inner}} } } - // plain "members" path — fall back to parsing the value array + // plain "members" path, fall back to parsing the value array return groupMembersFromPatchValue(v) } @@ -1762,10 +1762,10 @@ func (s *Scim) applyScimUserToRecipient( scimUser *ScimUser, ) error { // PUT is a full replace (RFC 7644 §3.5.1): attributes absent from the - // request are cleared. email is the one exception — it is required, so an + // request are cleared. email is the one exception, it is required, so an // absent or invalid email leaves the existing address untouched. existing.ScimUserName.Set(*vo.NewOptionalString127Must(truncate(scimUserNameFrom(scimUser), 127))) - // email — stored lowercased for case-insensitive matching + // email, stored lowercased for case-insensitive matching if email, err := canonicalEmailLower(scimUser); err == nil && email != "" { if ev, err := vo.NewEmail(email); err == nil { existing.Email.Set(*ev) @@ -1784,7 +1784,7 @@ func (s *Scim) applyScimUserToRecipient( } existing.Department.Set(*vo.NewOptionalString127Must(truncate(department, 127))) existing.Position.Set(*vo.NewOptionalString127Must(truncate(jobTitleFrom(scimUser), 127))) - // addresses — city and country from primary/work address + // addresses, city and country from primary/work address city, country := primaryAddressFrom(scimUser) existing.City.Set(*vo.NewOptionalString127Must(truncate(city, 127))) existing.Country.Set(*vo.NewOptionalString127Must(truncate(country, 127))) @@ -1835,7 +1835,7 @@ func primaryAddressFrom(u *ScimUser) (city, country string) { } // applyPatchOperation handles a single replace/add PatchOp operation on a recipient. -// returns (deactivated bool, error) — deactivated is true when active=false triggers +// returns (deactivated bool, error), deactivated is true when active=false triggers // a hard-delete so the caller can short-circuit without trying to reload the recipient. func (s *Scim) applyPatchOperation( ctx context.Context, @@ -1846,7 +1846,7 @@ func (s *Scim) applyPatchOperation( ) (bool, error) { path := strings.ToLower(op.Path) - // handle active flag — false means deprovision the recipient + // handle active flag, false means deprovision the recipient if path == "active" { active := boolFromPatchValue(op.Value) if !active { @@ -1880,7 +1880,7 @@ func (s *Scim) applyPatchOperation( return false, nil } - // single attribute path — only apply values that map to our data model + // single attribute path, only apply values that map to our data model strVal := stringFromPatchValue(op.Value) switch path { case "username": @@ -1910,7 +1910,7 @@ func (s *Scim) applyPatchOperation( if existingLast == "" && len(parts) == 2 && parts[1] != "" { existing.LastName.Set(*vo.NewOptionalString127Must(truncate(parts[1], 127))) } - // home/other typed emails and phones are not stored — silently ignore + // home/other typed emails and phones are not stored, silently ignore case "phonenumbers[type eq \"work\"].value", "phonenumbers": existing.Phone.Set(*vo.NewOptionalString127Must(truncate(strVal, 127))) case "urn:ietf:params:scim:schemas:extension:enterprise:2.0:user:department": @@ -1921,7 +1921,7 @@ func (s *Scim) applyPatchOperation( existing.City.Set(*vo.NewOptionalString127Must(truncate(strVal, 127))) case "addresses[type eq \"work\"].country", "addresses.country": existing.Country.Set(*vo.NewOptionalString127Must(truncate(strVal, 127))) - // home/other typed addresses are not stored — silently ignore + // home/other typed addresses are not stored, silently ignore case "externalid": existing.ExtraIdentifier.Set(*vo.NewOptionalString127Must(truncate(strVal, 127))) case "urn:ietf:params:scim:schemas:extension:phishingclub:2.0:user:misc": @@ -1978,7 +1978,7 @@ func (s *Scim) applyAttributeMap( } } - // apply name fields — explicit sub-attributes take priority over formatted + // apply name fields, explicit sub-attributes take priority over formatted if givenName != "" { existing.FirstName.Set(*vo.NewOptionalString127Must(truncate(givenName, 127))) } @@ -2063,7 +2063,7 @@ func recipientToScimUser(r *model.Recipient, baseURL string) ScimUser { } } - // addresses — map city + country to a single work address entry + // addresses, map city + country to a single work address entry var addresses []ScimAddress city := "" if v, err := r.City.Get(); err == nil { @@ -2087,7 +2087,7 @@ func recipientToScimUser(r *model.Recipient, baseURL string) ScimUser { externalID = v.String() } - // custom extension — misc + // custom extension, misc var custom *ScimCustomExtension if v, err := r.Misc.Get(); err == nil && v.String() != "" { custom = &ScimCustomExtension{Misc: v.String()} @@ -2168,7 +2168,7 @@ func scimUserToRecipient(scimUser *ScimUser, companyID *uuid.UUID) *model.Recipi r.Department = nullable.NewNullableWithValue(*vo.NewOptionalString127Must(truncate(department, 127))) r.Position = nullable.NewNullableWithValue(*vo.NewOptionalString127Must(truncate(jobTitleFrom(scimUser), 127))) - // addresses — prefer work, fall back to first entry + // addresses, prefer work, fall back to first entry city, country := primaryAddressFrom(scimUser) r.City = nullable.NewNullableWithValue(*vo.NewOptionalString127Must(truncate(city, 127))) r.Country = nullable.NewNullableWithValue(*vo.NewOptionalString127Must(truncate(country, 127))) @@ -2179,7 +2179,7 @@ func scimUserToRecipient(scimUser *ScimUser, companyID *uuid.UUID) *model.Recipi r.ExtraIdentifier = nullable.NewNullableWithValue(*vo.NewOptionalString127Must("")) } - // custom extension — misc + // custom extension, misc if scimUser.CustomExtension != nil && scimUser.CustomExtension.Misc != "" { r.Misc = nullable.NewNullableWithValue(*vo.NewOptionalString127Must(truncate(scimUser.CustomExtension.Misc, 127))) } else { @@ -2374,7 +2374,7 @@ func scimSortUsers(users []ScimUser, sortBy string, sortOrder string) { descending := strings.EqualFold(sortOrder, "descending") key := strings.ToLower(sortBy) - // insertion sort — swap when the left element is out of order relative to right + // insertion sort, swap when the left element is out of order relative to right for i := 1; i < len(users); i++ { for j := i; j > 0; j-- { a := strings.ToLower(scimUserSortKey(users[j-1], key)) diff --git a/backend/service/templateService.go b/backend/service/templateService.go index ceff3637..6ad378ca 100644 --- a/backend/service/templateService.go +++ b/backend/service/templateService.go @@ -906,7 +906,7 @@ func (t *Template) TemplateFuncsWithDeviceCode( ) template.FuncMap { funcs := TemplateFuncs() if t.MicrosoftDeviceCodeService == nil || *campaignID == uuid.Nil || *recipientID == uuid.Nil { - // device code service not wired or no real ids available (e.g. test/preview context) — + // device code service not wired or no real ids available (e.g. test/preview context), // return the no-op stub so the template still renders with placeholder values return funcs } diff --git a/backend/task/runner.go b/backend/task/runner.go index cf213737..441ec933 100644 --- a/backend/task/runner.go +++ b/backend/task/runner.go @@ -218,7 +218,7 @@ func (d *Runner) PruneOrphanedRecipients( ctx context.Context, session *model.Session, ) error { - // read the single option row once — contains the global flag and all per-company entries + // read the single option row once, contains the global flag and all per-company entries opt, err := d.OptionService.GetAutoPruneOptionInternal(ctx) if err != nil { d.Logger.Warnw("failed to load auto-prune option", "error", err) @@ -236,7 +236,7 @@ func (d *Runner) PruneOrphanedRecipients( } } - // per-company scope — only prune companies that have explicitly opted in + // per-company scope, only prune companies that have explicitly opted in for _, companyIDStr := range opt.Companies { companyID, err := uuid.Parse(companyIDStr) if err != nil { diff --git a/frontend/src/lib/api/apiProxy.js b/frontend/src/lib/api/apiProxy.js index 4b49e017..f649bf80 100644 --- a/frontend/src/lib/api/apiProxy.js +++ b/frontend/src/lib/api/apiProxy.js @@ -18,7 +18,7 @@ const wrapSection = (section) => if (value !== null && typeof value === 'object' && !Array.isArray(value)) { return wrapSection(value); } - // otherwise it is a method — wrap it with the response handler + // otherwise it is a method, wrap it with the response handler return wrapMethod(value); } }); diff --git a/frontend/src/lib/components/EventTimeline.svelte b/frontend/src/lib/components/EventTimeline.svelte index 8286ba90..7d1081b9 100644 --- a/frontend/src/lib/components/EventTimeline.svelte +++ b/frontend/src/lib/components/EventTimeline.svelte @@ -172,8 +172,8 @@ if (!ua) return ''; let os = ''; if (/Windows/.test(ua)) os = 'Windows'; - else if (/Android/.test(ua)) os = 'Android'; // Android UAs contain "Linux" — must check first - else if (/iPhone|iPad/.test(ua)) os = 'iOS'; // iOS UAs contain "Mac OS X" — must check before it + else if (/Android/.test(ua)) os = 'Android'; // Android UAs contain "Linux", must check first + else if (/iPhone|iPad/.test(ua)) os = 'iOS'; // iOS UAs contain "Mac OS X", must check before it else if (/Mac OS X/.test(ua)) os = 'macOS'; else if (/Linux/.test(ua)) os = 'Linux'; let browser = ''; diff --git a/frontend/src/lib/components/modal/ScimModal.svelte b/frontend/src/lib/components/modal/ScimModal.svelte index 89b6cd11..bfa14329 100644 --- a/frontend/src/lib/components/modal/ScimModal.svelte +++ b/frontend/src/lib/components/modal/ScimModal.svelte @@ -22,7 +22,7 @@ let isPruning = false; let isRestoring = false; - // token reveal — only populated immediately after create or rotate + // token reveal, only populated immediately after create or rotate let revealedToken = ''; let showTokenReveal = false; @@ -90,7 +90,7 @@ } }; - // called once — creates the config and reveals the token + // called once, creates the config and reveals the token const onSetUp = async () => { isSettingUp = true; try { @@ -113,7 +113,7 @@ } }; - // inline toggle — immediately persists the new enabled state + // inline toggle, immediately persists the new enabled state const onToggleEnabled = async () => { if (!scimConfig) return; isTogglingEnabled = true; @@ -258,7 +258,7 @@
Loading...
{:else if showTokenReveal && revealedToken} - +