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

New Injected Test: Require a Stapler verb annotation on web method looking methods #133

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

daniel-beck
Copy link
Member

@daniel-beck daniel-beck commented May 25, 2019

Related PR: jenkins-infra/jenkins.io#2291

This addition to InjectedTest will ensure all explicit or implied Stapler web methods declare an HTTP verb that they're to be used with. Similar to escape-by-default in Jelly, which is also only enforced by InjectedTests (and in a way the recent behavior reversal since 2.138.2).

@RequirePOST has been around forever, while @POST and the other "verb" annotations were available since Jenkins 1.651. I think this 3y 3m old release is a more than reasonable baseline (once there's a non-TypedFilterimplementation for Jenkins before 2.138.4).

The current releases of matrix-auth would behave as follows with this change:

[INFO] 
[INFO] Results:
[INFO] 
[ERROR] Failures: 
[ERROR]   PluginAutomaticTestBuilder$OtherTests.testStaplerDispatches:155 There should be no web methods that lack HTTP verb annotations like @RequirePOST, @GET, @POST, etc. -- see https://jenkins.io/redirect/developer/csrf-protection
Expected: is an empty collection
     but: <[hudson.security.GlobalMatrixAuthorizationStrategy$DescriptorImpl#doCheckName, org.jenkinsci.plugins.matrixauth.AuthorizationContainerDescriptor#doCheckName_, org.jenkinsci.plugins.matrixauth.AuthorizationMatrixNodeProperty$DescriptorImpl#doCheckName]>
[INFO] 
[ERROR] Tests run: 54, Failures: 1, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------

Unsure whether all the rules are 100% matching what Stapler does, but it should be close enough.

TODO:

  • Add implementation for core baselines before 2.138.4 and TypedFilter
  • It doesn't seem possible to annotate methods such that they take multiple verbs. Some web methods in core support that (showing a UI on GET, performing an action on POST), so there's no straightforward migration path there. Allow suppressing this check somehow in those cases.

@Wadeck
Copy link
Contributor

Wadeck commented May 27, 2019

there's no straightforward migration path there

We can split the method into two, annotate one with @GET and the second with @POST. One of the difference with @RequirePOST, the two previous ones do not fail the request if the method is incorrect.

Copy link
Contributor

@Wadeck Wadeck left a comment

Choose a reason for hiding this comment

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

The code is promising. That will cause lots of failures for most of the plugins, as the @GET is really rarely used. Do you plan to add a blog post explaining the reason of this hardening?

@daniel-beck
Copy link
Member Author

We can split the method into two

Well, yeah, but that's not exactly straightforward IMO, especially if faces with a few dozen of these in some plugins. The code needs to change nontrivially. We probably need @WebMethod too, or achieve different method signatures through creative argument declarations.

Do you plan to add a blog post explaining the reason of this hardening?

Yes, plus a better redirect target. It should take a while for this to get picked up by plugins anyway.


A potential negative side effect of this would be that we cannot change form validation request methods at will anymore, without breaking lots of plugins. Looking at you, jenkinsci/jenkins@09d6046.

@@ -65,6 +84,7 @@ public static TestSuite build(Map<String,?> params) throws Exception {
String packaging = StringUtils.defaultIfBlank((String)params.get("packaging"), "hpi");
if ("hpi".equals(packaging)) {
inJenkins.addTest(new OtherTests("testPluginActive", params));
inJenkins.addTest(new OtherTests("testStaplerDispatches", params));
Copy link
Member

Choose a reason for hiding this comment

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

I would rather other things did not go into InjectedTest (and that it would die). The reason being is that skipping part of the tests (rather than all of them) is pretty much impossible as it uses old junit Suites rather than newer test classes, and is hard to debug.
This would be much better going forward in a generate-tests mojo in maven-hpi-plugin which then also makes it much easier to debug in an IDE. but otherwise 👍

Copy link
Member

Choose a reason for hiding this comment

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

skipping part of the tests (rather than all of them) is pretty much impossible

Well, you should not be skipping any of them. :-)

I am not sure how creating JUnit 4-style tests would make it easier to ignore some test cases; target/generated-test-sources/whatever/InjectedTest.java would still not be editable.

Note that there is a reason all the tests go into one suite: so that only one Jenkins startup is needed, to minimize overhead.

At any rate, InjectedTest is a well-established part of Jenkins plugin development. Proposals to replace its structure should be kept separate. So long as this PR does not introduce a test which is going to fail widely and spuriously (which I have not yet reviewed), it should be fine IMO.

method.setAccessible(true);
return method;
} catch (ClassNotFoundException e) {
LOGGER.warning("This test requires Jenkins 2.154, Jenkins LTS 2.138.4, or newer to run, use e.g. -Djenkins.version=2.138.4");
Copy link
Member

Choose a reason for hiding this comment

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

Use of assume from JUnit might be appropriate here for better integration with error reporting.

@oleg-nenashev oleg-nenashev changed the title Require a Stapler verb annotation on web method looking methods New Injected Test: Require a Stapler verb annotation on web method looking methods Jun 4, 2019
@oleg-nenashev
Copy link
Member

Needs a merge conflict fix + clarity on TODO items in the PR description

@jglick jglick removed their request for review January 6, 2020 19:37
@daniel-beck
Copy link
Member Author

PR build fails because of #167 (comment)

@jglick
Copy link
Member

jglick commented Apr 6, 2020

This would be better put on hold until jenkinsci/jenkins#4623 is merged, or exclude doCheckXXX methods, or exclude them unless the Jenkins version indicates that jenkinsci/jenkins#4623 is included.

@daniel-beck
Copy link
Member Author

This would be better put on hold until jenkinsci/jenkins#4623 is merged

Unsure. Would be beneficial, but IMO overall doesn't make a big difference if the docs say:

  • Use @POST for any doCheck* and add checkMethod="post" or your plugin will break once Default to POST form validation jenkins#4623 is merged
  • Use @POST for any doCheck* and add checkMethod="post" or your plugin will break on Jenkins 2.2xy and newer

The advice needs to favor @POST (or @RequirePOST) for most endpoints anyway; only few, such as doWs, doArtifact, doIndex should be annotated @GET.

@jglick
Copy link
Member

jglick commented Apr 6, 2020

Agreed, it is fine so long as the docs guide users to use @POST rather than the HTTP method currently being sent.

@daniel-beck
Copy link
Member Author

I still need to figure some stuff out.

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

Successfully merging this pull request may close these issues.

6 participants