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

Ruby: Taint does not flow into do-end blocks #3880

Open
1 of 3 tasks
minusworld opened this issue Sep 15, 2021 · 6 comments
Open
1 of 3 tasks

Ruby: Taint does not flow into do-end blocks #3880

minusworld opened this issue Sep 15, 2021 · 6 comments
Labels
feature:taint lang:ruby priority:medium user:internal requested only by someone within Semgrep Inc.

Comments

@minusworld
Copy link
Member

Describe the bug
This taint rule does not appear to flow into the do-end block. I want it to match the # ruleid inside the block.
The block looks like this:

  # ruleid: avoid-tainted-http-request
  Net::HTTP.start(uri.host, uri.port) do |http|
    # ruleid: avoid-tainted-http-request
    req = Net::HTTP::Get.new uri
    resp = http.request request
  end

To Reproduce
https://semgrep.dev/s/GwQe

What is the priority of the bug to you?

  • P0: blocking your adoption of Semgrep or workflow
  • P1: important to fix or quite annoying
  • P2: regular bug that should get fixed
minusworld pushed a commit to semgrep/semgrep-rules that referenced this issue Sep 15, 2021
minusworld pushed a commit to semgrep/semgrep-rules that referenced this issue Sep 15, 2021
* Check for presence of default routes

* Updated avoid-default-routes with message and classification

* Added spaces

* Implement for avoid-tainted-file-access

* Fix annotations for avoid-tainted-file-access

* Add avoid link-to

* avoid-redirect

* Update message for avoid-redirect

* avoid-render-dynamic-path

* avoid-session-manipulation

* Rewrite pattern to be more clear in avoid-tainted-file-access

* Fix avoid-redirect test

* Switch avoid-tainted-file-access to use taint mode

* Add a layer of separation test

* Update avoid-session-manipulation

* Update avoid-link-to

* Updated avoid-render-dynamic-path

* Modified avoid-render-dynamic-path test

* Updated avoid-redirect

* Update message for link-to

* Added attempt at message to avoid-session-manipulation

* Split out FTP rules from tainted-file-access

* Move ftp rule

* Move session-manipulation rule

* Split out HTTP rules from avoid-tainted-file-access

* Move avoid-taitned-http-request

* Split out shell calls

* Move remaining tainted* rules out from xss

* Update CWE for file access

* Fix tests

* Add new test case to test reason for FN

* Make avoid-default-routes respond to tests

* Fix classifications and messages

* Update avoid-link-to.yaml

* Update avoid-session-manipulation.yaml

* Use Ttodo comment until semgrep/semgrep#3880 is resolved

Co-authored-by: grayson <grayson@returntocorp.com>
@emjin emjin added feature:taint priority:medium user:internal requested only by someone within Semgrep Inc. labels Sep 15, 2021
@emjin
Copy link
Contributor

emjin commented Sep 15, 2021

cc @IagoAbal @mmcqd

@IagoAbal
Copy link
Collaborator

Do you need to match both the block and the Get inside the block with the exact same rule? Most likely this is the result of taint-mode removing those kind of "duplicate matches", see PR #3781. If taint finds two matches, and one is strictly contained in the other, it will only report the larger one.

@IagoAbal
Copy link
Collaborator

IagoAbal commented Sep 16, 2021

I think this is mainly an issue of the Ruby front-end that encodes:

Net::HTTP.start(uri.host, uri.port) do |http|
    # ruleid: avoid-tainted-http-request
    req = Net::HTTP::Get.new uri
    resp = http.request request
end

in Generic as

  Net::HTTP.start(uri.host, uri.port, |http|
    # ruleid: avoid-tainted-http-request
    req = Net::HTTP::Get.new uri
    resp = http.request request)

which I don't find very accurate. And that encoding causes that matching the call to Net::HTTP.start also matches the entire do-end block.

ievans pushed a commit to semgrep/semgrep-rules that referenced this issue Sep 17, 2021
* Check for presence of default routes

* Updated avoid-default-routes with message and classification

* Added spaces

* Implement for avoid-tainted-file-access

* Fix annotations for avoid-tainted-file-access

* Add avoid link-to

* avoid-redirect

* Update message for avoid-redirect

* avoid-render-dynamic-path

* avoid-session-manipulation

* Rewrite pattern to be more clear in avoid-tainted-file-access

* Fix avoid-redirect test

* Switch avoid-tainted-file-access to use taint mode

* Add a layer of separation test

* Update avoid-session-manipulation

* Update avoid-link-to

* Updated avoid-render-dynamic-path

* Modified avoid-render-dynamic-path test

* Updated avoid-redirect

* Update message for link-to

* Added attempt at message to avoid-session-manipulation

* Split out FTP rules from tainted-file-access

* Move ftp rule

* Move session-manipulation rule

* Split out HTTP rules from avoid-tainted-file-access

* Move avoid-taitned-http-request

* Split out shell calls

* Move remaining tainted* rules out from xss

* Update CWE for file access

* Fix tests

* Add new test case to test reason for FN

* Make avoid-default-routes respond to tests

* Fix classifications and messages

* Update avoid-link-to.yaml

* Update avoid-session-manipulation.yaml

* Use Ttodo comment until semgrep/semgrep#3880 is resolved

Co-authored-by: grayson <grayson@returntocorp.com>
@r2c-demo
Copy link
Collaborator

@IagoAbal
Copy link
Collaborator

IagoAbal commented Nov 2, 2021

And that encoding causes that matching the call to Net::HTTP.start also matches the entire do-end block.

Well, I didn't know about Ruby blocks and I thought they had different semantics.. sorry if confused things. So it seems that the do-end block is a lambda that is passed as an argument to the method that precedes it?

Anyways, perhaps we could instead encode:

Net::HTTP.start(uri.host, uri.port) do |http|
    # ruleid: avoid-tainted-http-request
    req = Net::HTTP::Get.new uri
    resp = http.request request
end

this other way:

 Net::HTTP.start(uri.host, uri.port)({|http|
    req = Net::HTTP::Get.new uri
    resp = http.request request})

So that Net::HTTP.start(...) will not match the entire do-end block as it does now (see https://semgrep.dev/s/lZOo), which may not be entirely intuitive. Then you should get the behavior that you expected.

IagoAbal added a commit that referenced this issue Nov 3, 2021
@IagoAbal
Copy link
Collaborator

IagoAbal commented Nov 3, 2021

This also requires taint analysis to handle nested functions.

IagoAbal added a commit that referenced this issue Nov 16, 2021
Passing the body of the block as yet another argument had some
undesirable (?) side effects. For example, given `f(x) { |n| puts n }`,
pattern `f(...)` matched the entire block rather than just `f(x)`, and
`f($X)` did not match anything!

Helps #3880

test plan:
make test # tests included
IagoAbal added a commit that referenced this issue Nov 17, 2021
Passing the body of the block as yet another argument had some
undesirable (?) side effects. For example, given `f(x) { |n| puts n }`,
pattern `f(...)` matched the entire block rather than just `f(x)`, and
`f($X)` did not match anything!

Helps #3880

test plan:
make test # tests included
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature:taint lang:ruby priority:medium user:internal requested only by someone within Semgrep Inc.
Development

No branches or pull requests

5 participants
@minusworld @IagoAbal @emjin @r2c-demo and others