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

browser monitoring injection logic hardening #2465

Merged
merged 3 commits into from Feb 29, 2024
Merged

Conversation

fallwith
Copy link
Contributor

These changes introduce hardening to the logic that determines whether or not to inject the browser agent script tag.

In particular, the following 2 issues have been addressed:

  1. If either #traced_call or #should_instrument? encounter any exceptions, these exceptions should be handled and logged without impacting the observed web application.
  2. Allow a response headers hash to use symbols for values.

Closes #2462

These changes introduce hardening to the logic that determines whether
or not to inject the browser agent script tag.

In particular, the following 2 issues have been addressed:

1. If either `#traced_call` or `#should_instrument?` encounter any
   exceptions, these exceptions should be handled and logged without
   impacting the observed web application.
2. Allow a response headers hash to use symbols for values.

Closes #2462
@fallwith
Copy link
Contributor Author

NOTE to @kaylareopelle and @hannahramadan: I was worried about introducing performance overhead while supporting hash values as symbols. After some benchmarking, the solution I came up with is to use #match? which is defined on both Symbol and String. The switch to using #match? should actually introduce a small performance boost to all existing users, while also adding support for hash-values-as-symbols users.

Here is the benchmarking code in question:

require 'benchmark'

SYMBOLS = { :'an/example' => :'a/value',
            :'another-example' => :'another-value',
            :content_type => :'text/html',
            :traceparent => :'00-da8bc8cc6d062849b0efcf3c169afb5a-7d3efb1b173fecfa-01',
            :tracestate => :'33@nr=0-0-33-2827902-7d3efb1b173fecfa-e8b91a159289ff74-1-1.234567-1518469636035' }

STRINGS = SYMBOLS.each_with_object({}) { |(k,v), h| h[k.to_s] = v.to_s }
TEXT_HTML = 'text/html'
TEXT_HTML_PATTERN = %r{text/html}.freeze
TIMES = 1_000_000

puts 'Finding a speedy way to test symbol text inclusion...'

Benchmark.bm do |x|
  x.report 'baseline' do
    TIMES.times do
      # use STRINGS twice, as SYMBOLS will error out
      STRINGS.each { |k, v| STRINGS[k].include?(TEXT_HTML) }
      STRINGS.each { |k, v| STRINGS[k].include?(TEXT_HTML) }
    end
  end

  x.report '#to_s' do
    TIMES.times do
      SYMBOLS.each { |k, v| SYMBOLS[k].to_s.include?(TEXT_HTML) }
      STRINGS.each { |k, v| STRINGS[k].to_s.include?(TEXT_HTML) }
    end
  end

  x.report '#match? with pattern' do
    TIMES.times do
      SYMBOLS.each { |k, v| SYMBOLS[k].match?(TEXT_HTML_PATTERN) }
      STRINGS.each { |k, v| STRINGS[k].match?(TEXT_HTML_PATTERN) }
    end
  end

  x.report '#match? with string' do
    TIMES.times do
      SYMBOLS.each { |k, v| SYMBOLS[k].match?(TEXT_HTML) }
      STRINGS.each { |k, v| STRINGS[k].match?(TEXT_HTML) }
    end
  end
end

Copy link
Contributor

@kaylareopelle kaylareopelle left a comment

Choose a reason for hiding this comment

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

I really like these updates, @fallwith. Especially with the performance boost!

I think we might want a changelog entry for this. What do you think?

@fallwith
Copy link
Contributor Author

I really like these updates, @fallwith. Especially with the performance boost!

I think we might want a changelog entry for this. What do you think?

Sounds good. Let me figure out what's going on with these unit tests that aren't happy with the new rescue blocks and then I'll circle back with an entry that matches whatever the code that passes the tests ends up looking like.

- we want to rescue all exceptions spawning from checks to determine
  whether the browser agent should be inserted, but we can't rescue
  `#traced_call` without breaking expected workflows
- add a unit test for a hash with a symbol as the content type value
- added a README.md entry
@fallwith
Copy link
Contributor Author

I really like these updates, @fallwith. Especially with the performance boost!
I think we might want a changelog entry for this. What do you think?

Sounds good. Let me figure out what's going on with these unit tests that aren't happy with the new rescue blocks and then I'll circle back with an entry that matches whatever the code that passes the tests ends up looking like.

Ah ok. I was rescuing too far up the chain. We want legitimate issues to be surfaced and not rescued. With the latest commit we're still rescuing all headers parsing and other determinations of whether the browser agent should be inserted. The tests should pass now and a changelog entry has been added.

use `#send` in the test
Copy link

SimpleCov Report

Coverage Threshold
Line 93.73% 93%
Branch 71.85% 71%

ATTACHMENT = 'attachment'.freeze
TEXT_HTML = 'text/html'.freeze
ATTACHMENT = /attachment/.freeze
TEXT_HTML = %r{text/html}.freeze
Copy link
Contributor

Choose a reason for hiding this comment

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

TIL about %r{}! Nice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's most useful for cases like this where there's a literal forward slash character involved in the pattern.

@@ -65,6 +65,10 @@ def should_instrument?(env, status, headers)
html?(headers) &&
!attachment?(headers) &&
!streaming?(env, headers)
rescue StandardError => e
Copy link
Contributor

Choose a reason for hiding this comment

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

Could do rescue => e here but as I'm also cool with it as-is!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha! You and I were talking about this one recently and I was trying to figure out where my knowledge of a related best practice was coming from.

It turns out that RuboCop prefers it this way, with a named class. See Style/RescueStandardError. For some reason this project has (temporarily?) disabled that cop, but I still heed its advice and would prefer to stick with a named class.

Copy link
Contributor

@hannahramadan hannahramadan left a comment

Choose a reason for hiding this comment

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

ser bra ut!

@fallwith fallwith merged commit ad0dfa4 into dev Feb 29, 2024
28 checks passed
@fallwith fallwith deleted the nettleserovervåking branch February 29, 2024 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

browser_monitoring throws exception on non string HTTP header Content-Type
3 participants