Skip to content

feat(java): read Jenkins plugin manifest licenses#10939

Merged
DmitriyLewen merged 5 commits into
aquasecurity:mainfrom
0xTaoZ:fix-jenkins-plugin-manifest-licenses
Jul 10, 2026
Merged

feat(java): read Jenkins plugin manifest licenses#10939
DmitriyLewen merged 5 commits into
aquasecurity:mainfrom
0xTaoZ:fix-jenkins-plugin-manifest-licenses

Conversation

@0xTaoZ

@0xTaoZ 0xTaoZ commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • read Jenkins plugin Plugin-License-Name manifest attributes as a JAR license fallback
  • support suffixed multi-license attributes such as Plugin-License-Name-2
  • preserve the existing precedence: embedded pom.xml licenses win before manifest licenses, and packed LICENSE files remain the final fallback

Closes #10936

Validation

  • go test ./pkg/dependency/parser/java/jar -run TestParseJenkinsPluginManifestLicenses -count=1
  • go test ./pkg/dependency/parser/java/jar -count=1
  • go test ./pkg/dependency/parser/java/... -count=1
  • git diff --check HEAD~1..HEAD

@CLAassistant

CLAassistant commented Jul 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@DmitriyLewen DmitriyLewen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hello
Thanks for your work!
I left a few comments.
Also, you need to update the docs and sign the CLA.

Comment thread pkg/dependency/parser/java/jar/parse.go Outdated
Comment on lines 150 to 151
// Classify and attach the LICENSE file now that the jar's own artifact is resolved
// (it may have been added above from MANIFEST.MF / SHA-1 / file name).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment isn't related to the new attachManifestLicenses().

Comment thread pkg/dependency/parser/java/jar/parse.go Outdated

// Classify and attach the LICENSE file now that the jar's own artifact is resolved
// (it may have been added above from MANIFEST.MF / SHA-1 / file name).
attachManifestLicenses(pkgs, fileProps.FilePath, m.licenses)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It makes sense to add a comment about the priority.
Classifying a license from a file is an expensive operation, so we first try to detect licenses from the manifest file.


// attachManifestLicenses attaches Jenkins plugin licenses declared in MANIFEST.MF
// to the jar's own package when a single unambiguous package belongs to this jar.
func attachManifestLicenses(pkgs []ftypes.Package, filePath string, licenses []string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A lot of duplicate code.
You can create a common function for attachFileLicenses() and attachManifestLicenses().
something like that:

 // ownJarPackage returns the single package that belongs to filePath and still
  // needs a license, or nil when there is none, the owner already has a license,
  // or the owner is ambiguous (multiple packages share the jar's file path, e.g.
  // an uber jar).
  func ownJarPackage(pkgs []ftypes.Package, filePath string) *ftypes.Package {
        var pkg *ftypes.Package
        for i := range pkgs {
                if pkgs[i].FilePath != filePath {
                        continue
                }
                if pkg != nil {
                        return nil // more than one package belongs to this jar
                }
                pkg = &pkgs[i]
        }
        if pkg == nil || len(pkg.Licenses) > 0 {
                return nil
        }
        return pkg
  }

Comment thread pkg/dependency/parser/java/jar/parse.go Outdated
m.bundleName = strings.TrimPrefix(line, "Bundle-Name:")
case strings.HasPrefix(line, "Bundle-SymbolicName:"):
m.bundleSymbolicName = strings.TrimPrefix(line, "Bundle-SymbolicName:")
default:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add a new case for Plugin-License-Name (don't use the default here).

Comment thread pkg/dependency/parser/java/jar/parse.go Outdated
Comment on lines +682 to +685
for _, r := range suffix {
if r < '0' || r > '9' {
return false
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think there's any chance that there won't be a number here. Let's simplify the check. And if users do report such a case, we'll add it in a separate PR.

Comment thread pkg/dependency/parser/java/jar/parse.go Outdated
Comment on lines +658 to +664
key, value, ok := strings.Cut(line, ":")
if !ok || !isPluginLicenseNameKey(key) {
continue
}
if name := strings.TrimSpace(value); name != "" {
m.licenses = append(m.licenses, name)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd create a separate function for getting the license, and inside it do the Plugin-License-Name- check, trim the Plugin-License-Name* prefix, and return the licenses. If no licenses are found, return nil.

e.g. m.licenses = parseManifestLicenses(line)

}
}

func newZipReader(t *testing.T, entries map[string]string) *bytes.Reader {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we need a separate test that builds a jar file. Let's add one test case to TestParse. And also create a new test function for parseManifestLicenses() (use the export_test.go file).

@0xTaoZ

0xTaoZ commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I’ll clean up the manifest parsing, reuse the package lookup logic, move the coverage into the existing parser tests, and update the docs.

@0xTaoZ

0xTaoZ commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Updated this now. I cleaned up the manifest license parsing, reused the jar package lookup, moved the parser coverage into the existing test table, added focused tests for parseManifestLicenses, and updated the Java license docs.\n\nLocal check: go test ./pkg/dependency/parser/java/... -count=1\n\nPlease take another look when you have time.

@0xTaoZ

0xTaoZ commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@DmitriyLewen thanks again for the clear review. I pushed the updates, and the CLA check is green now too. Happy to adjust anything else if needed. I’d be glad to keep contributing to Trivy, and would appreciate another look when you have time.

@DmitriyLewen DmitriyLewen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for your work!

I refactored a bit.
Can you recheck?

@0xTaoZ

0xTaoZ commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

I rechecked the refactor and it looks good to me. I also ran go test ./pkg/dependency/parser/java/... -count=1 locally.

@DmitriyLewen DmitriyLewen added this pull request to the merge queue Jul 10, 2026
Merged via the queue into aquasecurity:main with commit f065203 Jul 10, 2026
30 of 31 checks passed
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.

feat(java): detect JAR license from the Plugin-License-Name manifest attribute (Jenkins plugins)

3 participants