New Audit unsound-contains: Checks for problematic contains() usage - #577
Conversation
|
Hi @Holzhaus, thanks for opening this. Two comments:
|
| However, this condition will not only evaluate to `true` if either | ||
| `refs/heads/main` or `refs/heads/develop` is passed, but also for substrings of | ||
| those values. For example, if someone pushes to a branch named `main`, then | ||
| `github.ref` would contain the string `refs/heads/mai` and the job would also | ||
| executed. |
There was a problem hiding this comment.
I'm having trouble following this example; is it supposed to be this instead?
| However, this condition will not only evaluate to `true` if either | |
| `refs/heads/main` or `refs/heads/develop` is passed, but also for substrings of | |
| those values. For example, if someone pushes to a branch named `main`, then | |
| `github.ref` would contain the string `refs/heads/mai` and the job would also | |
| executed. | |
| However, this condition will not only evaluate to `true` if either | |
| `refs/heads/main` or `refs/heads/develop` is passed, but also for substrings of | |
| those values. For example, if someone pushes to a branch named `mai`, then | |
| `github.ref` would contain the string `refs/heads/mai` and the job would also | |
| executed. |
(NB: This points to a threat model question: GitHub mostly treats branches on the same repo as having equivalent access, so flagging this kind of thing by default might be really noisy for ordinary users. It might be good to instead give these a "pedantic" or "auditor" persona, since we can't uniformly assume that they're always problematic.)
There was a problem hiding this comment.
Yes, sorry for the confusion, that was a typo. I actually found another typo, i'll push an update.
This points to a threat model question: GitHub mostly treats branches on the same repo as having equivalent access, so flagging this kind of thing by default might be really noisy for ordinary users.
This is true for many open-source projects. In professional settings, I've mostly seen repos where all developers work on the same repo (rathen than forks) and push to feature branches. Often, the main branch is guarded by branch protection rules (e.g., requires a PR, Approvals, Code Owner Reviews). So this is an actual security issue for such cases.
It might be good to instead give these a "pedantic" or "auditor" persona, since we can't uniformly assume that they're always problematic.
I initially made a list of particularily problematic contexts (e.g., github.ref) and only flagged those. But on second thought, I removed this because I couldn't come up with a single legitimate use case where you want to check if a context var is contained in a string rather than a list of strings. And in the latter case you should be using the fromJSON approach.
There was a problem hiding this comment.
I removed this because I couldn't come up with a single legitimate use case where you want to check if a context var is contained in a string rather than a list of strings. And in the latter case you should be using the
fromJSONapproach.
Yeah, good point. Thanks for explaining!
Sure.
Yes, I have actually seen this in the real world, but it's not an open-source project so I cannot disclose more details.
I'm not sure if this will be actually that noisy. I only matches I'll check if there is some convenient way to use the GitHub API for downloading a bunch of random workflow files and see if this audit triggers any false positives. |
I've found some publicly visible real world examples on GitHub. For obvious reasons I don't want to post them there, but I'll provide them via email to you. I tried to find some code snippets that would be detected by the audit and that are okay to post here because they are not exploitable:
|
c1a1285 to
c780beb
Compare
|
Thanks @Holzhaus, this is shaping up really well! I did an initial review pass on it and left some thoughts. (Apologies for the delay; I'll have more time for reviews in the coming days.) |
…ns` docs Co-authored-by: William Woodruff <william@yossarian.net>
|
Thanks @Holzhaus, this is looking really good to me. Two small nitpicks and then I'll do a final pass review. (Edit: also, the lint is failing!) |
Must be a version mismatch, |
Could you try rebasing or fast-forwarding this branch onto the latest |
|
@Holzhaus let me know if you're planning on resuming this -- if not, I'll probably revive it on a new PR and credit you. Thanks! |
|
Yes, I will have a look later today. Sorry, totally forgot about it. |
| expr: &'a Expr, | ||
| ) -> Box<dyn Iterator<Item = (&'a str, &'a Context<'a>)> + 'a> { | ||
| match expr { | ||
| Expr::Call { func, args: exprs } if func.0.eq_ignore_ascii_case("contains") => { |
There was a problem hiding this comment.
Nitpick: this should work now, since expr::Function has PartialEq<&str> with the right case folding:
| Expr::Call { func, args: exprs } if func.0.eq_ignore_ascii_case("contains") => { | |
| Expr::Call { func, args: exprs } if func == "contains" => { |
|
|
||
| | Type | Examples | Introduced in | Works offline | Enabled by default | | ||
| |----------|------------------------------------------------|---------------|---------------|--------------------| | ||
| | Workflow | [bypassable-contains-conditions.yml] | v1.6.0 | ✅ | ✅ | |
There was a problem hiding this comment.
| | Workflow | [bypassable-contains-conditions.yml] | v1.6.0 | ✅ | ✅ | | |
| | Workflow | [bypassable-contains-conditions.yml] | v1.7.0 | ✅ | ✅ | |
There was a problem hiding this comment.
NB: This also needs a new Configurable column like the current docs, see e.g.:
|
Thanks @Holzhaus, this looks really great to me! I left two small nitpicks, but otherwise I think this is good to go. One thought: how do you feel about giving this a shorter name, something like |
bypassable-contains-conditions: Checks for problematic contains() usageunsound-contains: Checks for problematic contains() usage
|
Hi @woodruffw, thanks. I integrated your suggestions and renamed the audit from |
|
Thanks @Holzhaus! I'll do a final pass tonight and hopefully merge then. I appreciate the time and effort you've put into this and the other PRs you've sent! |
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Signed-off-by: William Woodruff <william@yossarian.net>
Handles case insensitivity for us. Signed-off-by: William Woodruff <william@yossarian.net>
|
Merged, thanks again! |
See https://github.com/woodruffw/zizmor/pull/577/files?short_path=927c421#diff-927c421dd1ad1dea67595c286c6dd91d883ca0cd0ddb34413383e035ca2d2a55 for details.