Skip to content

fix(runner/triggers): dedupe CronJob-run findings, skip agent-managed scan jobs - #533

Open
mayankpande88 wants to merge 1 commit into
mainfrom
fix/job-owner-suffix-normalization
Open

fix(runner/triggers): dedupe CronJob-run findings, skip agent-managed scan jobs#533
mayankpande88 wants to merge 1 commit into
mainfrom
fix/job-owner-suffix-normalization

Conversation

@mayankpande88

Copy link
Copy Markdown
Contributor

Summary

Every run of a CronJob — and of an orchestrator-scheduled scan Job (trivy/popeye/nova) — fired a brand-new Finding chain. The pod's one-level owner walk returned the generated Job name (mycron-29123456, trivy-image-scan-<hex>), so each run produced a distinct fingerprint that never deduped against the previous run's, and downstream each event minted its own triage signal class (one LLM classification call per scan-job run).

Two changes:

  1. CronJob-run owner normalizationcanonicalOwner strips the CronJob controller's scheduled-time suffix (fmt.Sprintf("%s-%d", cronJob.Name, unixMinutes) — a controller-defined format, same trust level as the existing ReplicaSet pod-template-hash strip). All runs of a CronJob now resolve to the same owner → same fingerprint → dedup and recurrence work. Other generated-name shapes (random hex tails etc.) are deliberately NOT guessed at — those are conventions, not contracts, and stripping them risks merging distinct hand-named Jobs.

  2. Agent-managed Jobs no longer produce FindingsBuildJob now propagates the app.kubernetes.io/managed-by=nudgebee-agent + nudgebee.com/orchestrator labels to the pod template (previously Job-object-only, invisible to pod-scoped matchers), and Engine.Match skips any object carrying the agent's managed-by value. An ImagePullBackOff on a trivy scan pod is the scan orchestrator's run accounting, not a customer-facing finding. Foreign managed-by values (Helm etc.) still match — pinned by test.

Type of change

  • Bug fix (non-breaking)

Chart version

  • No version bump needed (runner code only; image tag bump lands as a separate chore(chart) commit per repo convention)

Test plan

  • make validate (fmt + lint + test) passes in runner/
  • New tests: TestResolveOwner_JobStripsCronJobTimestamp, TestResolveOwner_JobKeepsNonCronName, TestStripJobGeneratedSuffix (incl. hand-named / short-numeric / hex-tail / empty-strip edge cases), TestEngine_SkipsAgentManagedObjects (pod + Job + foreign managed-by), pod-template label assertions in TestBuildJob_HygieneInvariants

… scan jobs

Every run of a CronJob (and of an orchestrator-scheduled scan Job) fired
a brand-new Finding chain: the pod's one-level owner walk returned the
generated Job name (mycron-29123456, trivy-image-scan-b0690686), so each
run produced a distinct fingerprint that never deduped, and downstream
each minted its own triage signal class.

- canonicalOwner now strips the CronJob controller's scheduled-time
  suffix (a controller-defined format, same trust level as the existing
  ReplicaSet pod-template-hash strip), so all runs of a CronJob resolve
  to the same owner. Other generated-name shapes are deliberately not
  guessed at.
- The agent's own scheduled Jobs (trivy/popeye/nova scans) no longer
  produce Findings at all: BuildJob now propagates the managed-by and
  orchestrator labels to the pod template, and Engine.Match skips any
  object labeled app.kubernetes.io/managed-by=nudgebee-agent. Their
  failures are the scan orchestrator's run accounting, not a
  customer-facing finding (e.g. ImagePullBackOff on a trivy scan pod).
@mayankpande88
mayankpande88 requested a review from a team as a code owner July 15, 2026 09:44

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request propagates agent management labels to scheduled scan pods and filters them out in the trigger engine to prevent internal scan failures from generating customer-facing findings. It also normalizes CronJob-created Job names by stripping the scheduled-time suffix to ensure consistent owner resolution and deduplication of findings across runs. Feedback is provided to optimize the CronJob suffix stripping function by replacing the regular expression with a manual string scan to avoid heap allocations on a hot path.

Comment on lines +105 to +115
var cronJobScheduleSuffix = regexp.MustCompile(`-\d{8,10}$`)

// stripJobGeneratedSuffix normalizes a CronJob-created Job name to the
// CronJob's own name, so every scheduled run resolves to the same owner.
// Names without the scheduled-time suffix pass through unchanged.
func stripJobGeneratedSuffix(name string) string {
if stripped := cronJobScheduleSuffix.ReplaceAllString(name, ""); stripped != name && stripped != "" {
return stripped
}
return name
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The stripJobGeneratedSuffix function is called on a hot path (for every kubewatch event matching a Job). Using a regular expression here introduces unnecessary overhead and allocations. We can optimize this by performing a simple string scan to check if the name ends with a hyphen followed by 8 to 10 digits. This approach is significantly faster and avoids any heap allocations.

// stripJobGeneratedSuffix normalizes a CronJob-created Job name to the
// CronJob's own name, so every scheduled run resolves to the same owner.
// Names without the scheduled-time suffix pass through unchanged.
func stripJobGeneratedSuffix(name string) string {
    idx := strings.LastIndexByte(name, '-')
    if idx <= 0 {
        return name
    }
    suffix := name[idx+1:]
    if len(suffix) < 8 || len(suffix) > 10 {
        return name
    }
    for i := 0; i < len(suffix); i++ {
        if suffix[i] < '0' || suffix[i] > '9' {
            return name
        }
    }
    return name[:idx]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant