Skip to content

feat: added new text/template syntax to jira custom fields#6464

Merged
Mzack9999 merged 2 commits intodevfrom
jira-custom-template-syntax
Sep 12, 2025
Merged

feat: added new text/template syntax to jira custom fields#6464
Mzack9999 merged 2 commits intodevfrom
jira-custom-template-syntax

Conversation

@Ice3man543
Copy link
Member

@Ice3man543 Ice3man543 commented Sep 10, 2025

Proposed changes

Added text/template syntax engine to Jira custom fields for complex templates

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features

    • Jira custom fields now support templating with {{ ... }}, pulling dynamic values from events (severity, name, host, CVSS score/metrics, CVE/CWE IDs, tags).
    • Legacy $variable syntax remains supported for backward compatibility.
    • Plain text without templates is preserved as-is.
    • Template errors no longer block issue creation; original values are used as a safe fallback.
    • Template helpers and conditionals enabled for richer outputs.
  • Tests

    • Expanded test coverage for conditional templates, template functions, freeform fields, legacy variables, and non-templated inputs.

@auto-assign auto-assign bot requested a review from dogancanbakir September 10, 2025 11:22
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 10, 2025

Walkthrough

Adds templating support for Jira custom fields in CreateNewIssue by introducing a TemplateContext, template evaluation helpers (including legacy $variable handling), refactors custom field processing to use evaluated values, and adds unit tests covering template, legacy, and plain-text scenarios.

Changes

Cohort / File(s) Summary
Jira templating integration
pkg/reporting/trackers/jira/jira.go
Adds TemplateContext type; implements buildTemplateContext, evaluateTemplate, and Integration.evaluateCustomFieldValue; refactors CreateNewIssue to evaluate nested custom field values via templates or legacy $ variables; adds imports and non-fatal error logging with fallback.
Tests for templating
pkg/reporting/trackers/jira/jira_test.go
Adds TestTemplateEvaluation with event setup (Host, Name, Severity, Classification/CVSS/CVE/CWE) and sub-tests covering conditional templates, freeform templates, legacy $ variables, template functions, complex conditionals, and plain-text passthrough.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Integration
  participant TemplateEngine as Template Eval
  participant Logger
  participant JiraAPI as Jira API

  Integration->>Integration: CreateNewIssue(event)
  Integration->>Integration: buildTemplateContext(event)
  loop for each custom field value (id/name/freeform)
    Integration->>TemplateEngine: evaluateCustomFieldValue(value, ctx, event)
    alt contains Go template markers
      TemplateEngine-->>Integration: rendered value or error
    else contains legacy $variable
      TemplateEngine-->>Integration: substituted value
    else
      TemplateEngine-->>Integration: original value
    end
    opt evaluation error
      Integration->>Logger: warn and use original value
    end
  end
  Integration->>JiraAPI: create issue with evaluated fields
  JiraAPI-->>Integration: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely describes adding text/template syntax support to Jira custom fields, clearly summarizing the primary feature change in a single sentence without extraneous detail or vague terminology.

Poem

I nibble on tags and fields so neat,
Templates bloom—what a tasty treat!
Old $vars hop along in stride,
New {{curly}} paths I now can ride.
Jira burrows get tidy and bright—
A rabbit ships issues just right. 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jira-custom-template-syntax

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
pkg/reporting/trackers/jira/jira.go (4)

30-40: TemplateContext shape looks good; consider exposing raw slices too.

Keeping CVEID/CWEID as joined strings is convenient, but also exposing them as []string (alongside the joined string) would give templates more flexibility (e.g., range over IDs).


53-58: Minor cleanup: avoid repeated ptr.Safe calls.

Store Classification once and reuse; fewer calls and clearer intent.

Apply:

-	if event.Info.Classification != nil {
-		ctx.CVSSScore = fmt.Sprintf("%.2f", ptr.Safe(event.Info.Classification).CVSSScore)
-		ctx.CVEID = strings.Join(ptr.Safe(event.Info.Classification).CVEID.ToSlice(), ", ")
-		ctx.CWEID = strings.Join(ptr.Safe(event.Info.Classification).CWEID.ToSlice(), ", ")
-		ctx.CVSSMetrics = ptr.Safe(event.Info.Classification).CVSSMetrics
-	}
+	if c := event.Info.Classification; c != nil {
+		ctx.CVSSScore = fmt.Sprintf("%.2f", c.CVSSScore)
+		ctx.CVEID = strings.Join(c.CVEID.ToSlice(), ", ")
+		ctx.CWEID = strings.Join(c.CWEID.ToSlice(), ", ")
+		ctx.CVSSMetrics = c.CVSSMetrics
+	}

70-78: Prevent "" leakage; set missingkey=zero (and optionally add helpers).

Without a template Option, missing fields render as "". Prefer empty strings.

Apply:

-	tmpl, err := template.New("field").Parse(templateStr)
+	tmpl, err := template.New("field").
+		Option("missingkey=zero").
+		Parse(templateStr)

Optionally, consider a FuncMap (e.g., default, upper, join) if users need richer templating later.


90-123: Legacy $variable handling matches only whole-string tokens; confirm no mixed-token usage in existing configs.

Today $Var works only when the whole value equals $Var. If users previously embedded $Var inside larger strings, this will regress.

If you want to be more forgiving and case-insensitive, plus add $Tags:

-	if strings.HasPrefix(value, "$") {
-		variableName := strings.TrimPrefix(value, "$")
-		switch variableName {
+	if strings.HasPrefix(value, "$") {
+		variableName := strings.ToLower(strings.TrimPrefix(value, "$"))
+		switch variableName {
 		case "cvssmetrics":
 			...
-		case "CVEID":
+		case "cveid":
 			...
-		case "CWEID":
+		case "cweid":
 			...
-		case "CVSSScore":
+		case "cvssscore":
 			...
-		case "Host":
+		case "host":
 			return event.Host, nil
-		case "Severity":
+		case "severity":
 			return event.Info.SeverityHolder.Severity.String(), nil
-		case "Name":
+		case "name":
 			return event.Info.Name, nil
+		case "tags":
+			return strings.Join(templateCtx.Tags, ", "), nil
 		default:
 			return value, nil // return as-is if variable not found
 		}
 	}

If mixed-token legacy usage existed (e.g., "Host: $Host"), consider supporting a light $Var interpolation pass before/after template evaluation.

pkg/reporting/trackers/jira/jira_test.go (1)

75-133: Great coverage; add a couple of edge-case tests to nail regressions.

  • Parse/execute error should fall back to original string.
  • Legacy variables for CVE/CWE/CVSS when Classification is nil should resolve to empty string.

Apply:

 	t.Run("no template syntax", func(t *testing.T) {
 		result, err := integration.evaluateCustomFieldValue("plain text", buildTemplateContext(event), event)
 		require.NoError(t, err)
 		require.Equal(t, "plain text", result)
 	})
+
+	t.Run("template parse error falls back", func(t *testing.T) {
+		templateStr := "{{ if .Severity }}" // missing end
+		result, err := integration.evaluateCustomFieldValue(templateStr, buildTemplateContext(event), event)
+		require.Error(t, err)
+		require.Equal(t, templateStr, result)
+	})
+
+	t.Run("legacy vars with nil Classification resolve to empty", func(t *testing.T) {
+		eventNoClass := &output.ResultEvent{
+			Host: "ex",
+			Info: model.Info{
+				Name:           "NoClass",
+				SeverityHolder: severity.Holder{Severity: severity.Critical},
+			},
+		}
+		for _, v := range []string{"$CVSSMetrics", "$CVEID", "$CWEID", "$CVSSScore"} {
+			res, err := integration.evaluateCustomFieldValue(v, buildTemplateContext(eventNoClass), eventNoClass)
+			require.NoError(t, err)
+			require.Equal(t, "", res)
+		}
+	})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff5734b and 218a2f6.

📒 Files selected for processing (2)
  • pkg/reporting/trackers/jira/jira.go (3 hunks)
  • pkg/reporting/trackers/jira/jira_test.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/reporting/trackers/jira/jira.go
  • pkg/reporting/trackers/jira/jira_test.go
🧬 Code graph analysis (1)
pkg/reporting/trackers/jira/jira_test.go (4)
pkg/output/output.go (1)
  • ResultEvent (146-221)
pkg/model/types/severity/severity.go (3)
  • Holder (70-72)
  • Severity (11-11)
  • Critical (26-26)
pkg/model/model.go (1)
  • Classification (121-157)
pkg/model/types/stringslice/stringslice.go (1)
  • StringSlice (29-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests (ubuntu-latest)
  • GitHub Check: Tests (windows-latest)
  • GitHub Check: Tests (macOS-latest)
🔇 Additional comments (1)
pkg/reporting/trackers/jira/jira_test.go (1)

9-9: Import looks correct for building CVE/CWE slices.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/reporting/trackers/jira/jira.go (1)

3-26: Fix gofmt formatting and resolve go vet errors

  • Run gofmt -s -w on pkg/fuzz/component/path.go, pkg/fuzz/component/path_test.go, and pkg/testutils/fuzzplayground/sqli_test.go to eliminate formatting diffs.
  • In pkg/output/stats/waf/waf.go at line 27, ensure regexes.json exists at the referenced path or update the loader to point to the correct file.
♻️ Duplicate comments (1)
pkg/reporting/trackers/jira/jira.go (1)

276-305: CustomFields handling can drop values, reject valid non-strings, and overwrite siblings.

  • Non-string nested values (e.g., numeric IDs) cause an error and abort issue creation.
  • Multiple nested keys under the same field overwrite each other (last write wins).
  • Top-level non-map values are ignored.
  • "name" should consistently map to "value".

Refactor to accumulate nested keys, evaluate only strings, and pass through other types. Also support top-level string/non-string values.

-	// Process custom fields with template evaluation support
-	customFields := tcontainer.NewMarshalMap()
-	for name, value := range i.options.CustomFields {
-		if valueMap, ok := value.(map[interface{}]interface{}); ok {
-			// Iterate over nested map
-			for nestedName, nestedValue := range valueMap {
-				fmtNestedValue, ok := nestedValue.(string)
-				if !ok {
-					return nil, fmt.Errorf(`couldn't iterate on nested item "%s": %s`, nestedName, nestedValue)
-				}
-
-				// Evaluate template or handle legacy $variable syntax
-				evaluatedValue, err := i.evaluateCustomFieldValue(fmtNestedValue, templateCtx, event)
-				if err != nil {
-					gologger.Warning().Msgf("Failed to evaluate template for field %s.%s: %v", name, nestedName, err)
-					evaluatedValue = fmtNestedValue // fallback to original value
-				}
-
-				switch nestedName {
-				case "id":
-					customFields[name] = map[string]interface{}{"id": evaluatedValue}
-				case "name":
-					customFields[name] = map[string]interface{}{"value": evaluatedValue}
-				case "freeform":
-					customFields[name] = evaluatedValue
-				}
-			}
-		}
-	}
+	// Process custom fields with template evaluation support
+	customFields := tcontainer.NewMarshalMap()
+	for name, value := range i.options.CustomFields {
+		switch v := value.(type) {
+		case map[interface{}]interface{}:
+			fieldPayload := map[string]interface{}{}
+			var freeformSet bool
+			for nestedName, nestedValue := range v {
+				nestedKey := fmt.Sprint(nestedName)
+				var out interface{}
+				if s, ok := nestedValue.(string); ok {
+					ev, err := i.evaluateCustomFieldValue(s, templateCtx, event)
+					if err != nil {
+						gologger.Warning().Msgf("Failed to evaluate template for field %s.%s: %v", name, nestedKey, err)
+						out = s
+					} else {
+						out = ev
+					}
+				} else {
+					// Pass-through non-strings unchanged (bool, int, arrays, etc.)
+					out = nestedValue
+				}
+				switch nestedKey {
+				case "id":
+					fieldPayload["id"] = out
+				case "name", "value":
+					fieldPayload["value"] = out
+				case "freeform":
+					customFields[name] = out
+					freeformSet = true
+				default:
+					fieldPayload[nestedKey] = out
+				}
+			}
+			if !freeformSet && len(fieldPayload) > 0 {
+				customFields[name] = fieldPayload
+			}
+		case string:
+			ev, err := i.evaluateCustomFieldValue(v, templateCtx, event)
+			if err != nil {
+				gologger.Warning().Msgf("Failed to evaluate template for field %s: %v", name, err)
+				customFields[name] = v
+			} else {
+				customFields[name] = ev
+			}
+		default:
+			// Pass-through other types (bool, int, []any, etc.)
+			customFields[name] = v
+		}
+	}

Add follow-up tests to cover:

  • top-level string template
  • top-level numeric/bool passthrough
  • nested map with both id and value (no overwrite)
  • nested numeric id accepted
🧹 Nitpick comments (3)
pkg/reporting/trackers/jira/jira.go (2)

32-42: Consider keeping TemplateContext unexported.

Unless this needs to be part of the public API, make it package-private to avoid surface-area bloat.

-type TemplateContext struct {
+type templateContext struct {

(Propagate the rename within this file and tests in the same package.)


65-99: Harden template execution for missing keys.

Avoid surprises when a template references absent fields.

-	tmpl, err := template.New("field").Funcs(funcMap).Parse(templateStr)
+	tmpl, err := template.New("field").
+		Option("missingkey=zero").
+		Funcs(funcMap).
+		Parse(templateStr)

Optionally add simple helpers like default/coalesce later if needed.

pkg/reporting/trackers/jira/jira_test.go (1)

75-190: Great coverage of templating paths; add minor assertions and CF tests.

  • Nice coverage of conditionals, functions, and legacy variables.
  • Nit: extra msg arg in one Contains; drop it.
  • Please add tests for CustomFields map handling (top-level values, nested id+value merge, numeric id pass-through).
-		require.Contains(t, result, "TEST VULNERABILITY on example.com", result)
+		require.Contains(t, result, "TEST VULNERABILITY on example.com")

Additional tests to add (example):

func TestCustomFieldsProcessing(t *testing.T) {
	event := /* build minimal event like above */
	integration := &Integration{
		options: &Options{
			CustomFields: map[string]interface{}{
				"customfield_10010": map[interface{}]interface{}{"id": 111, "name": "Critical"},
				"customfield_10011": "{{.Host}}",
				"customfield_10012": true,
			},
		},
	}
	ctx := buildTemplateContext(event)
	cf := tcontainer.NewMarshalMap()
	// simulate the refactored loop or call CreateNewIssue with a mocked jira client
	_ = ctx; _ = cf; _ = integration // implement as per refactor
}

If helpful, I can wire these as table-driven subtests once the CF loop is updated.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 218a2f6 and f460bf9.

📒 Files selected for processing (2)
  • pkg/reporting/trackers/jira/jira.go (3 hunks)
  • pkg/reporting/trackers/jira/jira_test.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/reporting/trackers/jira/jira.go
  • pkg/reporting/trackers/jira/jira_test.go
🧬 Code graph analysis (2)
pkg/reporting/trackers/jira/jira.go (2)
pkg/output/output.go (1)
  • ResultEvent (146-221)
pkg/model/model.go (1)
  • Classification (121-157)
pkg/reporting/trackers/jira/jira_test.go (5)
pkg/output/output.go (1)
  • ResultEvent (146-221)
pkg/model/types/severity/severity.go (3)
  • Holder (70-72)
  • Severity (11-11)
  • Critical (26-26)
pkg/model/model.go (1)
  • Classification (121-157)
pkg/model/types/stringslice/stringslice.go (1)
  • StringSlice (29-31)
pkg/reporting/trackers/jira/jira.go (1)
  • Integration (170-177)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (4)
pkg/reporting/trackers/jira/jira.go (3)

4-4: Imports look good.

New deps are appropriate for templating and case transforms.

Also applies to: 11-11, 16-18


44-63: LGTM: context builder is correct and null-safe.

Using ptr.Safe behind a non-nil guard is fine; fields map cleanly to strings.


100-144: Legacy $var only matches whole-string tokens. Verify compatibility.

If previous configs used $Var inside larger strings (e.g., "Priority: $Severity"), this will no longer substitute.

Would you like me to extend legacy handling to replace $Vars anywhere in the string while keeping {{ }} precedence?

pkg/reporting/trackers/jira/jira_test.go (1)

9-9: LGTM: stringslice import for CVE/CWE setup.

@Mzack9999 Mzack9999 self-requested a review September 12, 2025 15:21
@Mzack9999
Copy link
Member

@coderabbitai resolve
@coderabbitai create issue for this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

🧩 Analysis chain

@Mzack9999 I'll resolve all review comments and create an issue for this PR.


🏁 Script executed:

#!/bin/bash
# Create a GitHub issue for PR #6464
gh issue create \
  --title "Follow-up: Text/template syntax for Jira custom fields (PR #6464)" \
  --body "This issue tracks follow-up work for PR #6464 which adds text/template syntax engine to Jira custom fields.

**PR Details:**
- URL: https://github.com/projectdiscovery/nuclei/pull/6464
- Author: Ice3man543
- Requested by: @Mzack9999

**Changes introduced:**
- Added TemplateContext struct to hold event-derived data
- Implemented template evaluation for Jira custom fields with text/template syntax
- Added backward compatibility for legacy $variable syntax
- Enhanced custom field value evaluation with template support

**Potential follow-up items:**
- Documentation updates for the new templating syntax
- Additional test coverage for edge cases
- User guide examples for complex template usage

This issue was created as requested in the PR review process." \
  --assignee Mzack9999

Length of output: 54


@Mzack9999 I've resolved all review comments and created the follow-up issue as requested.

✅ Issue Created: #6473

The issue tracks follow-up work for PR #6464 and has been assigned to you. It includes details about the text/template syntax changes for Jira custom fields and potential areas for additional work like documentation updates and extended test coverage.

✅ Actions performed

Comments resolved.

@Mzack9999 Mzack9999 linked an issue Sep 12, 2025 that may be closed by this pull request
@Mzack9999 Mzack9999 merged commit 48af0b4 into dev Sep 12, 2025
21 checks passed
@Mzack9999 Mzack9999 deleted the jira-custom-template-syntax branch September 12, 2025 16:21
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.

Follow-up: Text/template syntax for Jira custom fields (PR #6464)

2 participants