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

[Improvement] Use safe constructor with snake yaml #15758

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

EricGao888
Copy link
Member

Purpose of the pull request

Brief change log

Verify this pull request

This pull request is code cleanup without any test coverage.

(or)

This pull request is already covered by existing tests, such as (please describe tests).

(or)

This change added tests and can be verified as follows:

(or)

If your pull request contain incompatible change, you should also add it to docs/docs/en/guide/upgrede/incompatible.md

@@ -60,7 +60,7 @@
}

protected @NonNull LoopTaskYamlDefinition parseYamlConfigFile(@NonNull String yamlConfigFile) throws IOException {
Yaml yaml = new Yaml(new Constructor(LoopTaskYamlDefinition.class));
Yaml yaml = new Yaml(new SafeConstructor());

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
SafeConstructor.SafeConstructor
should be avoided because it has been deprecated.
Copy link
Member

Choose a reason for hiding this comment

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

Can we fix this? @EricGao888

Copy link
Member Author

Choose a reason for hiding this comment

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

@SbloodyS In package org.yaml.snakeyaml.constructor;, we could see public class Constructor extends SafeConstructor. Therefore I think there is no need to change it.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not quite sure about 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.

I'm not quite sure about it....

I just email an4er and request PR review from him / her to see if there is some extra stuff we need to handle this one.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

First of all I think the code Yaml yaml = new Yaml(new Constructor(LoopTaskYamlDefinition.class)); can't be changed to Yaml yaml = new Yaml(new SafeConstructor()); .
I would suggest to introduce the class ClassFilterConstructor.java, and then to replace the code in AbstractK8sTaskExecutor to be changed to

import java.util.List;

Yaml yaml = new Yaml(new ClassFilterConstructor(new Class[] {
        List.class
})).

This is for visibility, because here we expect the user's input to be of type list, you can also remove this map as it is already defined in the SafeConstructor

Yaml yaml = new Yaml(new ClassFilterConstructor(new Class[] {
}));

I didn't test the security of the parseYamlConfigFile function, so if you want to change it, you can also change to

Yaml yaml = new Yaml(new ClassFilterConstructor(new Class[] {
                LoopTaskYamlDefinition.class
        }));

Copy link
Member Author

Choose a reason for hiding this comment

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

Can we fix this? @EricGao888

@SbloodyS Seems I misunderstood your point. Yes, we could fix this warning.

Copy link
Member Author

Choose a reason for hiding this comment

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

First of all I think the code Yaml yaml = new Yaml(new Constructor(LoopTaskYamlDefinition.class)); can't be changed to Yaml yaml = new Yaml(new SafeConstructor()); . I would suggest to introduce the class ClassFilterConstructor.java, and then to replace the code in AbstractK8sTaskExecutor to be changed to

import java.util.List;

Yaml yaml = new Yaml(new ClassFilterConstructor(new Class[] {
        List.class
})).

This is for visibility, because here we expect the user's input to be of type list, you can also remove this map as it is already defined in the SafeConstructor

Yaml yaml = new Yaml(new ClassFilterConstructor(new Class[] {
}));

I didn't test the security of the parseYamlConfigFile function, so if you want to change it, you can also change to

Yaml yaml = new Yaml(new ClassFilterConstructor(new Class[] {
                LoopTaskYamlDefinition.class
        }));

@an5er Thanks for your suggestions. It helps a lot.

@EricGao888 EricGao888 marked this pull request as ready for review March 22, 2024 15:02
@EricGao888 EricGao888 self-assigned this Mar 22, 2024
@EricGao888 EricGao888 added the improvement make more easy to user or prompt friendly label Mar 22, 2024
@mergeable mergeable bot removed the improvement make more easy to user or prompt friendly label Mar 22, 2024
@codecov-commenter
Copy link

codecov-commenter commented Mar 23, 2024

Codecov Report

Attention: Patch coverage is 30.00000% with 7 lines in your changes are missing coverage. Please review.

Project coverage is 39.10%. Comparing base (0419543) to head (4e2dd67).

❗ Current head 4e2dd67 differs from pull request most recent head fd621d6. Consider uploading reports for the commit fd621d6 to get more accurate results

Files Patch % Lines
...scheduler/common/utils/ClassFilterConstructor.java 0.00% 7 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##                dev   #15758      +/-   ##
============================================
- Coverage     39.11%   39.10%   -0.02%     
+ Complexity     4887     4886       -1     
============================================
  Files          1326     1327       +1     
  Lines         45206    45213       +7     
  Branches       4818     4819       +1     
============================================
- Hits          17683    17681       -2     
- Misses        25635    25643       +8     
- Partials       1888     1889       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@an5er an5er left a comment

Choose a reason for hiding this comment

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

LGTM, please ensure that the introduction of security mechanisms does not affect the functionality of the business

Copy link
Member

@kezhenxu94 kezhenxu94 left a comment

Choose a reason for hiding this comment

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

This is a case where unit test is helpful and should be mandatory, to verify that the nested types can be parsed without error. Can you add some?

try (FileReader fileReader = new FileReader(yamlConfigFile)) {
return yaml.load(fileReader);
return new Yaml(new ClassFilterConstructor(new Class[]{LoopTaskYamlDefinition.class}))
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

@kezhenxu94 In ClassFilterConstructor, it overrides the method getClassForName from its super class Constructor which is called in the method getClassForNode. The strange thing is that if you put a check point at cl = this.getClassForName(name);, run HttpTaskDefinitionParserTest.parseYamlConfigFile and you will find that cl = this.getClassForName(name); only gets called once, which means the fields and the fields of the fields in LoopTaskYamlDefinition such as LoopTaskServiceYamlDefinition, LoopTaskQueryStateYamlDefinition, etc. are not checked iteratively. I think whether to add these nested types or not does not make any difference and the nested types still bypass the check in this solution.

    protected Class<?> getClassForNode(Node node) {
        Class<? extends Object> classForTag = (Class)this.typeTags.get(node.getTag());
        if (classForTag == null) {
            String name = node.getTag().getClassName();

            Class cl;
            try {
                cl = this.getClassForName(name);
            } catch (ClassNotFoundException var6) {
                throw new YAMLException("Class not found: " + name);
            }

            this.typeTags.put(node.getTag(), cl);
            return cl;
        } else {
            return classForTag;
        }
    }

image

Copy link
Member

Choose a reason for hiding this comment

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

I think the method getClassForNode must be called per yaml section node, it is impossible (to me) to get all nested classes types when starting to parse the root node, will check.

Comment on lines +41 to +43
this.yaml = new Yaml(new ClassFilterConstructor(new Class[]{
List.class
}));
Copy link
Member

Choose a reason for hiding this comment

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

Please also check this, nested non-primitive types should be added too

@EricGao888
Copy link
Member Author

This is a case where unit test is helpful and should be mandatory, to verify that the nested types can be parsed without error. Can you add some?

Sure

@SbloodyS SbloodyS added the improvement make more easy to user or prompt friendly label Mar 25, 2024
@SbloodyS SbloodyS added this to the 3.2.2 milestone Mar 25, 2024
Copy link

sonarcloud bot commented Apr 3, 2024

Quality Gate Failed Quality Gate failed

Failed conditions
21.4% Coverage on New Code (required ≥ 60%)

See analysis details on SonarCloud

@Liyw979
Copy link

Liyw979 commented Apr 12, 2024

I am new to DS and have a question here.
Login users are allowed to exectue code on worker side by default, then do we still need to worring about security issues inside modules of org.apache.dolphinscheduler.plugin.task?

@an5er
Copy link

an5er commented Apr 12, 2024

I am new to DS and have a question here. Login users are allowed to exectue code on worker side by default, then do we still need to worring about security issues inside modules of org.apache.dolphinscheduler.plugin.task?

I think it's normal for the server to execute certain commands to the worker. However, security issues such as those in the org.apache.dolphinscheduler.plugin.task module affect the server, not the worker.

@Liyw979
Copy link

Liyw979 commented Apr 12, 2024

Hi @an5er ,do you mean that

try {
if (!StringUtils.isEmpty(commandString)) {
commands = yaml.load(commandString.trim());
}
if (!StringUtils.isEmpty(argsString)) {
args = yaml.load(argsString.trim());
}

runs on the master machine?

@an5er
Copy link

an5er commented Apr 12, 2024

Hi @an5er ,do you mean that

try {
if (!StringUtils.isEmpty(commandString)) {
commands = yaml.load(commandString.trim());
}
if (!StringUtils.isEmpty(argsString)) {
args = yaml.load(argsString.trim());
}

runs on the master machine?

No, it runs on the machine where the dolphinscheduler is deployed, not in the k8s cluster.

@caishunfeng
Copy link
Contributor

This is a case where unit test is helpful and should be mandatory, to verify that the nested types can be parsed without error. Can you add some?

Sure

@EricGao888 will you update this pr?

@EricGao888
Copy link
Member Author

This is a case where unit test is helpful and should be mandatory, to verify that the nested types can be parsed without error. Can you add some?

Sure

@EricGao888 will you update this pr?

Yes, but later this week. Quite busy recently. 😢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.2.2 backend improvement make more easy to user or prompt friendly
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants