Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replaceDescription is not robust #8307

Merged
merged 1 commit into from
Jul 28, 2023
Merged

replaceDescription is not robust #8307

merged 1 commit into from
Jul 28, 2023

Conversation

basil
Copy link
Member

@basil basil commented Jul 27, 2023

Problem

This is a fix for an unreleased regression. As of #8278, hudson.tasks.junit.JUnitResultArchiverTest#setDescription started failing in PCT with:

AssertionError: no form found
	at org.junit.Assert.fail(Assert.java:89)
	at hudson.tasks.junit.JUnitResultArchiverTest.findForm(JUnitResultArchiverTest.java:228)
	at hudson.tasks.junit.JUnitResultArchiverTest.testSetDescription(JUnitResultArchiverTest.java:216)
	at hudson.tasks.junit.JUnitResultArchiverTest.setDescription(JUnitResultArchiverTest.java:196)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.jvnet.hudson.test.JenkinsRule$1.evaluate(JenkinsRule.java:606)
	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:299)
	at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:293)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.lang.Thread.run(Thread.java:833)

Evaluation

After debugging this, I can see that #8278 exposed a pre-existing bug by making the browser's behavior more strict.

The original bug was actually introduced in #6511. Here we have a Jelly editable description in the JUnit plugin that sets a permission attribute (required), a description attribute (optional), but no submission URL (also optional). This seems like a completely valid thing to do, but the code in editable-description.js from #6511 only handles two cases:

  1. Description and submission URL are both set,
  2. Neither are set.

Here the description is set, but the submission URL is not set, and the code in editable-description.js from #6511 does not handle this third case. It invokes replaceDescription with a non-null description (the one from the JUnit plugin's getDescription() method) and a null submission URL. The replaceDescription function, similarly assuming that either both are set or neither are set, sees that the submission URL is not undefined (that's right, it's defined as null) and passes this into the parameters object.

So far the code is buggy, but this was never a problem in practice in the Prototype implementation of replaceDescription (because Prototype converted null values into the empty string) or before #8278, but it is a problem after #8278. If I had to guess why #8278 made any difference, it would be that adding the header possibly puts the browser in a more strict mode. In any case the root cause of this whole problem is not #8278 but rather that #6511 failed to handle this legitimate case.

Solution

To fix this I read the documentation noting that

All modern web browsers return null when the specified attribute does not exist on the specified element.

so I fixed the (sole) caller of replaceDescription to always call it with the result of Element#getAttribute (either a valid string or null) and then set the description and parameters separately in the form object in replaceDescription based on whether or not those passed-in values were null and non-empty. There is no need to check for undefined anymore since that is never returned by Element#getAttribute.

Testing done

hudson.tasks.junit.JUnitResultArchiverTest#setDescription failed after #8278 (not yet released) but without this PR and now passes with this PR.

Proposed changelog entries

N/A

Proposed upgrade guidelines

N/A

Submitter checklist

Desired reviewers

@mention

Before the changes are marked as ready-for-merge:

Maintainer checklist

@basil basil added the skip-changelog Should not be shown in the changelog label Jul 27, 2023
@basil basil requested a review from a team July 27, 2023 20:29
@basil basil self-assigned this Jul 27, 2023
@basil
Copy link
Member Author

basil commented Jul 27, 2023

This PR is now ready for merge. We will merge it after approximately 24 hours if there is no negative feedback. Please see the merge process documentation for more information about the merge process. Thanks!

@basil basil added the ready-for-merge The PR is ready to go, and it will be merged soon if there is no negative feedback label Jul 27, 2023
@basil basil merged commit 3172131 into jenkinsci:master Jul 28, 2023
basil added a commit to basil/jenkins that referenced this pull request Aug 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready-for-merge The PR is ready to go, and it will be merged soon if there is no negative feedback skip-changelog Should not be shown in the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants