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

Align env var with RFC #1938

Merged
merged 2 commits into from
Mar 23, 2022
Merged

Align env var with RFC #1938

merged 2 commits into from
Mar 23, 2022

Conversation

lloeki
Copy link
Contributor

@lloeki lloeki commented Mar 14, 2022

Current RFC-defined env vars are:

  • DD_APPSEC_ENABLED
  • DD_APPSEC_RULES # This one is currently not compliant, which this PR addresses
  • DD_APPSEC_WAF_TIMEOUT

@ivoanjo
Copy link
Member

ivoanjo commented Mar 14, 2022

This change looks reasonable:

  1. Are there any tests for it? In particular, the appsec system tests seem like they were passing both before and after this change, do they cover it at all?
  2. Could you link the RFC somewhere? I'm guessing this may be a google doc so as long as the permissions are properly set there shouldn't be an issue in keeping it here so that future colleagues can reference it :)

@lloeki
Copy link
Contributor Author

lloeki commented Mar 16, 2022

Are there any tests for it? In particular, the appsec system tests seem like they were passing both before and after this change, do they cover it at all?

When updating the rulesets there is no relevant spec to run (although admittedly there should be specs WRT asset loading and ruleset configuration picking up the correct file, but that's not related to ruleset update). Ruleset validity, evaluation, and behaviour conformance is indeed covered by system tests.

Duh, I confused that with another PR (#1937). There should be specs indeed on that one, hence why I left it as a draft and planned to add some.

@lloeki lloeki added this to the 1.0.0.beta2 milestone Mar 18, 2022
@lloeki lloeki marked this pull request as ready for review March 21, 2022 15:25
@lloeki lloeki requested a review from a team March 21, 2022 15:25
@lloeki lloeki force-pushed the appsec-change-rules-env-var branch from 9bc2ba6 to 24c9547 Compare March 21, 2022 15:25
@lloeki lloeki force-pushed the appsec-change-rules-env-var branch from 24c9547 to c1be54b Compare March 21, 2022 16:03
Copy link
Member

@ivoanjo ivoanjo left a comment

Choose a reason for hiding this comment

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

I had a lot of tiny suggestions and was prepared to press the approve button but then found that in the appsec/configuration/settings_spec.rb we have quite a lot of duplication when compared to extensions_spec.rb so...
I'm curious to know more about that one.

I don't think it's a blocker, so if you tell me "I really want it like this", I can live with it, but, yeah, it gave me bit of pause.

P.s.: You may want to rebase on top of current master, I fixed the CI issues with Ruby 2.3.

end
end

describe Datadog::AppSec::Configuration::Settings do
Copy link
Member

Choose a reason for hiding this comment

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

Minor: This is repeated from the top-level describe :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

duh, I claim brain bug on my side!


let(:dsl) { Datadog::AppSec::Configuration::DSL.new }

after { settings.send(:reset!) }
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't seem to do anything? The settings on the class seem all to be instance-level data, and RSpec creates a new instance for each test case so this should not make a difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch

Comment on lines +35 to +38
describe '#enabled' do
subject(:enabled) { settings.enabled }
it { is_expected.to eq(true) }
end
Copy link
Member

Choose a reason for hiding this comment

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

This confused me a lot!

The default on Datadog::AppSec::Configuration::Settings class is to have this as false so it took me a while to realize that in datadog/appsec/spec_helper.rb there was a allow(ENV).to receive(:[]).with('DD_APPSEC_ENABLED').and_return('true').

This doesn't seem to match the rest of the tests -- they all check the defaults in DEFAULTS and then separately test the env-updated behaviors -- so should this one be updated to also test that the default is false?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed this feels weird to have it set in spec_helper. I actually did not think much of the value being true instead of false...

require 'datadog/appsec/spec_helper'

RSpec.describe Datadog::AppSec::Configuration::Settings do
shared_context 'registry with integration' do
Copy link
Member

Choose a reason for hiding this comment

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

Minor: Since this "registry" is actually not shared (it only gets included at the top-level, once), we could just just remove the shared_context and make these lets be part of the top-level describe and have the same effect ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I kept it because I feel it should be reusable by other specs, but it probably doesn't belong in this file anymore.

Comment on lines +50 to +53
describe '#ruleset=' do
subject(:ruleset_) { settings.merge(dsl.tap { |c| c.ruleset = :risky }) }
it { expect { ruleset_ }.to change { settings.ruleset }.from(:recommended).to(:risky) }
end
Copy link
Member

Choose a reason for hiding this comment

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

The behavior of this method seems somewhat... weird. From the code, it seems like you can set it to :recommended, :risky, '/or/a/string/with/a/path/to/a/json/file'.

Am I getting this correct? Would it be worth documenting this as a comment somewhere? For instance, I'm unclear about the edge cases -- can you do DD_APPSEC_RULES=risky as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, this setting is settable to symbols to use one of the vendored presets, but the env var does not allow selection of these rulesets yet. This (and some other differences in env var vs dsl) will be addressed in a separate PR but is out of the scope of this one.

Comment on lines +55 to +63
describe '#waf_timeout' do
subject(:waf_timeout) { settings.waf_timeout }
it { is_expected.to eq(5000) }
end

describe '#waf_timeout=' do
subject(:waf_timeout_) { settings.merge(dsl.tap { |c| c.waf_timeout = 3 }) }
it { expect { waf_timeout_ }.to change { settings.waf_timeout }.from(5000).to(3) }
end
Copy link
Member

Choose a reason for hiding this comment

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

Minor: Have you considered documenting in the method name that this value is in microseconds e.g.waf_timeout_microseconds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This setter should accept durations with units (as strings like '1s'), like its sibling env var does. The env var is defined across libs as being DD_APPSEC_WAF_TIMEOUT and I did not want to introduce inconsistency. That said, it should be documented in the getter/setter docstring.

Comment on lines +75 to +83
describe '#trace_rate_limit' do
subject(:trace_rate_limit) { settings.trace_rate_limit }
it { is_expected.to eq(100) }
end

describe '#trace_rate_limit=' do
subject(:trace_rate_limit_) { settings.merge(dsl.tap { |c| c.trace_rate_limit = 2 }) }
it { expect { trace_rate_limit_ }.to change { settings.trace_rate_limit }.from(100).to(2) }
end
Copy link
Member

Choose a reason for hiding this comment

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

Minor: A bit like waf_timeout above, would it be worth stating in the method name that this is a trace_rate_limit_per_second or something?

Copy link
Contributor Author

@lloeki lloeki Mar 22, 2022

Choose a reason for hiding this comment

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

It's actually trace_rate_limit_per_second_per_concurrency_unit (or _per_thread, or _per_fiber since it lives in a fiber local). Again I'd rather have proper docstrings explaining the unit+behaviour of the feature (even though this one is not considered public API)

Comment on lines +112 to +114
before do
allow(ENV).to receive(:[]).with('DD_APPSEC_ENABLED').and_return('1')
end
Copy link
Member

Choose a reason for hiding this comment

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

Minor: Usually to modify env vars in the dd-trace-rb specs we end up using ClimateControl, e.g.

around { |example| ClimateControl.modify('DD_PROFILING_NO_EXTENSION' => nil) { example.run } }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I did not know that and found references to a similar approach in the specs, so did not look further.

TBH I'm not too sure of the added value of ClimateControl (is that mainly about this synchronize? Are tests ever run multithreaded?) and kind of find it less readable as well as being unable to assert whether ENV has received :[] for a given key.

Copy link
Member

Choose a reason for hiding this comment

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

One thing I think is an advantage is when debugging tests. When I was looking at the DD_APPSEC_ENABLED tests I was getting pretty confused where the env var was coming from because I was printing ENV and did not see it there, but ENV['DD_APPSEC_ENABLED'] returned a value.

But... I don't feel too strongly, just thought I'd mention it :)

Comment on lines +121 to +124
describe '#enabled=' do
subject(:enabled_) { settings.merge(dsl.tap { |c| c.enabled = false }) }
it { expect { enabled_ }.to change { settings.enabled }.from(true).to(false) }
end
Copy link
Member

Choose a reason for hiding this comment

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

Minor: This seems somewhat redundant. Since none of the setters depend on the env var, I don't think it's worth repeating them again, I suggest simplifying this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The tested behaviour here is that the env var should:

a) have less priority than the DSL setters (final value)
b) still be picked up before the DSL kicks in (change, notably from a non-default to a non-default)

This was actually useful to test because I triggered a small bug with it (that affected only specs, not real life apps) when this example was in spec/datadog/appsec/extensions_spec.rb.

Comment on lines 60 to 65

describe '#enabled=' do
subject(:enabled_) { settings.enabled = false }
it do
expect { enabled_ }.to change { settings.enabled }.from(true).to(false)
end
it { expect { enabled_ }.to change { settings.enabled }.from(true).to(false) }
end

Copy link
Member

Choose a reason for hiding this comment

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

⬆️ I can't comment on the exact lines above, but this whole spec seems weird.

This is not a spec for Datadog::Core::Configuration::Settings as the describe claims, this is a spec for Settings.new.appsec.

And... it quite repeats the new spec/datadog/appsec/configuration/settings_spec.rb. My suggestion would be to avoid doing so -- by having this spec check that Settings.new.appsec correctly delegates to a Datadog::AppSec::Configuration::Settings but not actually testing it through here.

That, or perhaps removing the Datadog::AppSec::Configuration::Settings and testing it completely through here. Having a spec that is almost copy-pasted from the other seems weird...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this whole spec seems weird

Yes it is. @marcotc added the extension spec to ensure that propagation of these values happens correctly but the subject being Settings.new.appsec defeats the purpose. I chose to implement a spec for Datadog::AppSec::Configuration::Settings because it felt more logical/unit to have these examples on the instance itself instead of going through and depending on a bigger chunk of code.

Interestingly enough the bug I mentioned in another comment is that reset! doesn't quite work because there's some memoization happening somewhere in or around that bigger chunk of code that escapes reset attempts and adding more test cases makes them fail randomly depending on ordering for reasons that only happen in specs.

My suggestion would be to avoid doing so -- by having this spec check that Settings.new.appsec correctly delegates to a Datadog::AppSec::Configuration::Settings but not actually testing it through here.

Agreed.

Copy link
Member

@ivoanjo ivoanjo left a comment

Choose a reason for hiding this comment

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

As discussed during the 2022-03-22 team meeting, there's still quite a bit of iteration that it would be nice to see on the tests, but the production code change we're quite happy with, so I'm approving this PR, any extra test changes will be done on a separate PR, to unblock release of beta2.

@lloeki lloeki merged commit ba4b04a into master Mar 23, 2022
@lloeki lloeki deleted the appsec-change-rules-env-var branch March 23, 2022 10:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants