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

Implemented writeYaml step #23

Merged
merged 8 commits into from
Aug 4, 2017

Conversation

witokondoria
Copy link
Member

@witokondoria witokondoria commented Jun 15, 2017

Delegates to snakeyaml the task of serializing any Object to a yaml file

pom.xml Outdated
@@ -28,7 +28,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.609.1</version>
<version>2.19</version>
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need this?
I don't see any reason for it. As I've said in previous PRs; When we bump anything here I want to see a more rigorous cleanup for example moving to more recent workflow.version and cleaning up all @Inject etc. And that should go in a separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

The real reason was to bump surefire plugin up to 2.19.1 so testing could be done with -Dtest=method1#method2. Will revert the change


@Override
public Step newInstance(Map<String, Object> arguments) throws Exception {
WriteYamlStep step = new WriteYamlStep();
Copy link
Member

Choose a reason for hiding this comment

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

I doubt this is needed for this step, skip it and just put file and data as required params in the @DataBoundConstructor.
The special handling here that you've seen in other steps are because they have an either or relation that can't be expressed in a better way.


@Override
protected Void run () throws Exception {
FilePath path = null;
Copy link
Member

Choose a reason for hiding this comment

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

Bad formating.

"def l = [] \n" +
"node('slaves') {\n" +
" writeYaml file: 'test', data: /['a': ]/ \n" +
" sh 'cat test' \n" +
Copy link
Member

Choose a reason for hiding this comment

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

There is a feature coming soon to pipeline which stores the parameters of steps, that might risk printing the step to the log, which means your assert might not assert on the thing it think it does.

It would be more precise if you archived the file and read it back in in the test to verify the contents. But it's just a nit.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will readYaml in every test

@witokondoria
Copy link
Member Author

Requested changes were adressed. @rsandell, could you review them?

Copy link
Member

@rsandell rsandell left a comment

Choose a reason for hiding this comment

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

Sorry for not responding earlier. But I have been waiting for today's security announcement.
Since this has the same security vulnerability as was disclosed in Script Security with groovy.json.JsonOutput.toJson(Object) as it allows arbitrary get methods to be invoked that a script would normally not be allowed to access.
One way we could solve this would be to only allow the step to take a Map. The other would be to somehow either look at the classloader to see if it is an object declared in the script or a library or to inspect the object to see if all get methods are whitelisted before proceeding. Or maybe the yaml library has a filtering option where we could filter only whitelisted getters somehow.

@witokondoria
Copy link
Member Author

Completely understandable, better block a bug than fix it at a later stage.
I believe its easier/leaner to limit the classes to dump (ala script-approval). Inspecting the object to check if every getter is whitelisted does not seem really clear.

Have added a recursive (:imp:) isValidObjectType method to check wether the object to dump is "safe". WDYT?

Copy link
Member

@rsandell rsandell left a comment

Choose a reason for hiding this comment

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

Besides my comments it looks good to me.
Just would like someone from the Jenkins cert team to take another look in case I've missed something.

Boolean ret = true;
Set entry = ((Map) obj).entrySet();
Iterator iterator = entry.iterator();
while (iterator.hasNext() && ret) {
Copy link
Member

Choose a reason for hiding this comment

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

This looks a bit convoluted, something like this would be more readable and simpler I think

for (Map.Entry entry : map.entrySet()) {
    if (!isValidObject(entry.getKey()) || !isValidObject(entry.getValue())) {
        return false;
    }
}

It also seems like you are overwriting the ret var every time in the loop, so effectively you are only checking the last entry in the map which my suggestion also solves.

Copy link
Member Author

@witokondoria witokondoria Jul 11, 2017

Choose a reason for hiding this comment

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

True that, Its result of a late commit. Will fix it.

} else if (obj instanceof Collection) {
Boolean ret = true;
Iterator iterator = ((Collection) obj).iterator();
while (iterator.hasNext() && ret) {
Copy link
Member

Choose a reason for hiding this comment

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

Same simplification and issue here as for the Map.
Suggestion:

for (Object o : ((Collection)obj)) {
    if (!isValidObject(o)) {
        return false;
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

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

ditto

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

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

The feature itself looks good, but I am very aware about the isValidObjectType implementation

pom.xml Outdated
<pluginRepositories>
<pluginRepository>
<id>repo.jenkins-ci.org</id>
<url>http://repo.jenkins-ci.org/public/</url>
Copy link
Member

Choose a reason for hiding this comment

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

https 🐋

* Writes a yaml file from the workspace.
*
* @author Javier DELGADO &lt;witokondoria@gmail.com&gt;.
*/
Copy link
Member

Choose a reason for hiding this comment

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

since TODO would be useful since we make attempts to visualize extension javadocs

Copy link
Member

Choose a reason for hiding this comment

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

not needed. it's not present in any other of the steps.


@DataBoundConstructor
public WriteYamlStep(@Nonnull String file, @Nonnull Object data) {
if (file == null) {
Copy link
Member

Choose a reason for hiding this comment

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

StringUtils.isBlank()

if (file == null) {
throw new IllegalArgumentException("file parameter must be provided to writeYaml");
}
this.file = file;
Copy link
Member

Choose a reason for hiding this comment

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

truncate spaces?

Copy link
Member

Choose a reason for hiding this comment

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

Does it also make sense to verify the patch and to ensure nobody passes an absolute one or a relative with traversal to upper levels?

Copy link
Member Author

Choose a reason for hiding this comment

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

want to trim the string? Forcing a path from within the current current workspace seems fine

Copy link
Member Author

Choose a reason for hiding this comment

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

From the writeFile Step, paths are not verified (so traversal is also possible).

Would you prefer a higer level implementation for this check?

*
* @param file file name
*/
public void setFile(String file) {
Copy link
Member

Choose a reason for hiding this comment

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

Do you need setters? If no, I would advice to either remove them and make the fields final OR to convert the class to @DataBoundSetters

(obj instanceof URL) || (obj instanceof Calendar) ||
(obj instanceof Date) || (obj instanceof UUID) ||
(obj == null)) {
return true;
Copy link
Member

Choose a reason for hiding this comment

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

It is strange, error-prone (E.g. GString, Long, etc.) and ineffective. Why would you need it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Its not a complete and final list, but a one less strict than plain Maps, with objects already whitelisted when toJson invocations:
Long is covered by Number
GStrings hasnt been even tested :(

Copy link
Member

Choose a reason for hiding this comment

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

My guess is that the GStrings will be evaluated/expanded in CPS/sandbox before entering the java context, but it needs to be tested non the less.

something like:

writeYaml data: "${currentBuild.rawBuild}", file: 'data'

might be enough

Copy link
Member Author

Choose a reason for hiding this comment

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

will add tests tomorrow!

Copy link
Member

@daniel-beck daniel-beck left a comment

Choose a reason for hiding this comment

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

Haven't been able to break this in some limited testing as indicated, but that's not exactly a strong endorsement :-/

file.title=File
file.description=File to save as YAML
text.title=Data
text.description=Object to serialize yo YAML
Copy link
Member

Choose a reason for hiding this comment

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

Yo dawg, I heard yo and yo dawg like yo yos.

Copy link
Member Author

@witokondoria witokondoria Jul 11, 2017

Choose a reason for hiding this comment

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

LOL, that made my afternoon

private boolean isValidObjectType(Object obj) {
if ((obj instanceof Boolean) || (obj instanceof Character) ||
(obj instanceof Number) || (obj instanceof String) ||
(obj instanceof URL) || (obj instanceof Calendar) ||
Copy link
Member

Choose a reason for hiding this comment

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

@@ -175,4 +175,11 @@
<url>https://github.com/jenkinsci/pipeline-utility-steps-plugin</url>
<tag>HEAD</tag>
</scm>

<pluginRepositories>
Copy link
Member

Choose a reason for hiding this comment

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

Time for a new parent pom before trying to fix this.

@witokondoria
Copy link
Member Author

Most of the changes were adressed (path check is unimplemented). Im willing to squash the commits in one, but have left all of them to easen reviews.

@rsandell rsandell merged commit 254439b into jenkinsci:master Aug 4, 2017
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.

4 participants