-
Notifications
You must be signed in to change notification settings - Fork 315
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
Filter environment variables when creating child processes #5984
Filter environment variables when creating child processes #5984
Conversation
Codecov ReportBase: 57.79% // Head: 57.83% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## main #5984 +/- ##
============================================
+ Coverage 57.79% 57.83% +0.04%
- Complexity 2013 2019 +6
============================================
Files 324 324
Lines 18761 18761
Branches 3876 3881 +5
============================================
+ Hits 10842 10850 +8
+ Misses 6862 6852 -10
- Partials 1057 1059 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
# A file with default configuration options used by ORT. These options can be overridden in custom configuration files. | ||
|
||
ort: | ||
variablesToPropagate: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phew. Looking at the length of this, I really wonder whether the "allow"-approach is the right one.
In either case we should probably consider to support wildcards or regexes, so one could allow / deny e.g. CARGO_*
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I was already sceptical when the decision was taken. It will be very difficult to come up with an exhaustive list. In addition, this list will have to be maintained when there are new versions of tools.
Using of wildcards will simplify the configuration, but again increase the risk that an unwanted variable is passed through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether we should come up with something completely different... a "crazy" approach could be to look at the entropy of the value of an environment variable to "guess" whether it's likely a password or so, and only then do not pass it on to the spawned process.
Or, instead of a global list, create tool-specific lists that are used in CommandLineTool
object
s. I.e. we should wrap each tool we call in an object
instead of implementing the interface in the main class itself (see MercurialCommand
for an example), and each of these objects gets its own (hard-coded) list of allowed environment variables. That should at least ease maintainability a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fear the entropy approach is too clever to work in practice.
If the allowed variables were configured at the command line tools, this could indeed make the maintenance a bit easier. But how could these configurations then be extended? For our use case, we will need to pass additional variables with credentials to package managers.
@@ -70,12 +79,21 @@ class ProcessCapture( | |||
* Filter out the variables that are not white-listed to be propagated from [environment]. | |||
*/ | |||
private fun filterEnvironmentVariables(environment: MutableMap<String, String>): MutableMap<String, String> { | |||
val keysToRemove = environment.keys.filterNot { it in variablesToPropagate } | |||
val keys = environment.keys.handleCase() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively to this, apply .toSortedMap(String.CASE_INSENSITIVE_ORDER)
to environment
on Windows before looking up the keys.
- NODE_PATH | ||
- NODE_EXTRA_CA_CERTS | ||
- PREFIX | ||
- TERM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also propagate PATH
and PWD
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PATH
really seems to be required. Lots of tests were failing because tools could not be found.
5487735
to
fbde14e
Compare
I have implemented an alternative approach as described in my last comment on #5973. This seems to be significantly easier by (hopefully) offering a comparable level of security. |
fbde14e
to
d95f17c
Compare
65e5231
to
8703f03
Compare
8703f03
to
da9c584
Compare
* A set with patterns for variable names that are denied by default. All variables containing one of these | ||
* strings (ignoring case) are not propagated to child processes. | ||
*/ | ||
val DEFAULT_DENY_PATTERNS = setOf( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about it, the term "pattern" in the variable name (and also in the docs above) probably is a bit too auspicious as these neither are glob nor regex patterns (I was looking for docs by which principle patterns are matched). So, as this simply is a case-insensitive sub-string search, maybe just use "term"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or "substrings", also see my comment about not using a regexes below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now using the term "substrings" (hopefully) everywhere.
) | ||
|
||
/** Stores the current deny patterns used by this filter. */ | ||
private var denyPatterns = DEFAULT_DENY_PATTERNS.toPatterns() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, this line confirms me that "patters" is the wrong word for the variable, because if they already were patters, there would be no point in converting them toPatterns()
.
BTW @oheger-bosch, did you forget to push? |
da9c584
to
6e712ec
Compare
I did not manage to finish this yesterday. Sorry for the confusion. Now all changes should be available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some (hopefully) final nits.
|
||
withEnvironment(env) { | ||
val proc = if (Os.isWindows) { | ||
ProcessCapture("cmd.exe", "/c", "echo %DB_USER%:%DB_PASSWORD%.%DB_CONN%") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering, why are you using .
as a separator before the DB_CONN
? For a more realistic example, shouldn't it be @
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but the "@" character could be problematic when it is interpreted by the shell. I did not want to mess around with escaping syntax, since for the test it is only relevant that some of the values are replaced while others are not.
6e712ec
to
78058c0
Compare
/** | ||
* Remove all keys from [environment] that do not pass this filter. | ||
*/ | ||
fun filter(environment: MutableMap<String, String>): MutableMap<String, String> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a function that modifies it parameters AND returns the modified version is a bit of codesmell.
I would rather have either an extension function MutableMap<String, String>.filter
or no return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of an extension function that only filters in-place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter is returned to have a single expression in ProcessCapture
where the filter is applied. I am not sure whether I understand the proposal for an extension function. How would the signature of such an extension function look like and how would it help in this context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written MutableMap<String, String>.filter()
and applied like that:
with(EnvironmentVariableFilter) {
environment().filter().putAll(environment)
}
but it is more verbose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or remove the return value, you don't use it in ProcessCapture
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, the extension function would suffer from the same codesmell problem, wouldn't it? If it returns a value (this
in this case), it is not obvious that filtering was done in-place.
I am fine with removing the return value. It is used in ProcessCapture
for the method chaining, but if this becomes more verbose, this should not be a big issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right: since environment()
is a MutableMap
and there is no 'cleaner' way of changing it, I am find to leave the code as it is.
78058c0
to
bf094ed
Compare
This object contains functionality to filter out specific variables from the environment when creating child processes. It is going to be used by ProcessCapture to prevent that sensitive information is accidentally passed to external programs called by ORT. Signed-off-by: Oliver Heger <oliver.heger@bosch.io>
These properties allow the configuration of the EnvironmentVariableFilter class introduced in the previous commit. Signed-off-by: Oliver Heger <oliver.heger@bosch.io>
Before spawning a new process, apply EnvironmentVariableFilter to the environment map to make sure that only uncritical variables are passed. Signed-off-by: Oliver Heger <oliver.heger@bosch.io>
After loading the configuration, pass the properties related to filtering environment variables to EnvironmentVariableFilter, so that they are taken into account when creating new child processes. Signed-off-by: Oliver Heger <oliver.heger@bosch.io>
bf094ed
to
23d5657
Compare
This PR addresses #5973. Refer to the single commits for further details.