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

[APPSEC-9341] Allow blocking response template configuration via ENV variables #2975

Merged
merged 2 commits into from
Aug 28, 2023

Conversation

lloeki
Copy link
Member

@lloeki lloeki commented Jul 17, 2023

What does this PR do?

Allow blocking response configuration:
- change status code
- change response body
- specify redirect target (location header)

Motivation

Enable users to customize attack response.

Add support for configuring the block response template over configuration and ENV variables.

The customer can tweak the response body of the blocking page or default to the one store on the library.

On the first implementation that Loic created, we used the class instance variable @cache to store the result from reading the custom templates. I moved it into the configuration since it already provides a cache by default.

Additional Notes

- Initial implementation operates via static configuration.
- Then remote configuration support can be added by altering configuration fetching in AppSec::Response
- There will be a bit of refactoring when #2970 is merged

How to test the change?

specs

@github-actions github-actions bot added the appsec Application Security monitoring product label Jul 17, 2023
Copy link
Member Author

@lloeki lloeki left a comment

Choose a reason for hiding this comment

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

Left some comments for reviewers to peruse

lib/datadog/appsec/configuration/settings.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/configuration/settings.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/configuration/settings.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/configuration/settings.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/configuration/settings.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/response.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/response.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/response.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/response.rb Outdated Show resolved Hide resolved
lib/datadog/appsec/response.rb Outdated Show resolved Hide resolved
@GustavoCaso
Copy link
Member

GustavoCaso commented Jul 24, 2023

@lloeki here is an alternative suggestion for configuration of this feature.

settings :block do
  # HTTP status code to block with
  option :status do |o|
    o.type :int
    o.default 403
  end

  # only applies to redirect status codes
  option :location do |o|
    o.type :string
    o.setter { |v| URI(v) unless v.nil? }
  end

  # only applies to non-redirect status codes with bodies
  settings :templates do
    option :html do |o|
      o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML'
      o.type :string
      o.setter do |value|
        unless File.exist?(value)
          raise(ArgumentError, "appsec.templates.html: file not found: #{value}")
        end
      end
    end

    option :json do |o|
      o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_JSON'
      o.type :string
      o.setter do |value|
        unless File.exist?(value)
          raise(ArgumentError, "appsec.templates.json: file not found: #{value}")
        end
      end
    end

    option :html do |o|
      o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_TEXT'
      o.type :string
      o.setter do |value|
        unless File.exist?(value)
          raise(ArgumentError, "appsec.templates.text: file not found: #{value}")
        end
      end
    end
  end
end

It separates each type into its own option. It allows us to control the setting with a single ENV variable. Also, it removes the need to have to maintain a hash for all possible templates.

The code for the moment in which we need to retrieve either the default template or the configured one the code could look like this:

def content(content_type)
  content_format = content_type.split('/')[1]).to_sym

  raise ArgumentError, "unexpected type: #{content_type.inspect}" unless valid_format?(content_format)

  using_default = Datadog.configuration.appsec.block.templates.using_default?(content_format)

  if using_default
    Datadog::AppSec::Assets.blocked(format: content_format)
  else
    path = Datadog.configuration.appsec.block.templates.send(content_format).to_s

    cache[path] ||= (File.open(path, 'rb', &:read) || '')
  end
end

This new code uses using_default? to check if the template for that content type has been configured. We can do this because we have split each template on its separate configuration option.

What do you think 😄 ?

@GustavoCaso
Copy link
Member

Actually, @lloeki looking at the documentation on the DD site https://docs.datadoghq.com/security/application_security/threats/library_configuration/#configure-a-custom-blocking-page-or-payload

The documented way to configure the blocking response is appsec.http.blocked.template.html, and appsec.http.blocked.template.json. So it looks like we might have to provide those entry points for consistency

@GustavoCaso GustavoCaso force-pushed the configurable-block-response branch 2 times, most recently from 848c156 to 400e62f Compare August 25, 2023 09:45
@GustavoCaso GustavoCaso changed the title Allow blocking response configuration Allow blocking response template configuration via ENV variables Aug 25, 2023
@GustavoCaso GustavoCaso changed the title Allow blocking response template configuration via ENV variables [APPSEC-9341] Allow blocking response template configuration via ENV variables Aug 25, 2023
@GustavoCaso GustavoCaso marked this pull request as ready for review August 25, 2023 11:33
@GustavoCaso GustavoCaso requested a review from a team August 25, 2023 11:33
settings :templates do
option :html do |o|
o.env 'DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML'
o.type :string, nilable: true
Copy link
Member

Choose a reason for hiding this comment

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

We need to provide nilable because we use using_default? when checking whether we need to use the default value.

https://github.com/DataDog/dd-trace-rb/pull/2975/files#diff-83b38af05def4a7362ff8abda84f27d84964c9fce7781eb7c4a27ecbfc235c66R77

Since using_default? computes the option, we needed to provide something that would make it work. Another solution would be to give an empty string as default.

@GustavoCaso GustavoCaso merged commit 5722775 into master Aug 28, 2023
162 checks passed
@GustavoCaso GustavoCaso deleted the configurable-block-response branch August 28, 2023 09:32
@github-actions github-actions bot added this to the 1.15.0 milestone Aug 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
appsec Application Security monitoring product
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants