Skip to content

Commit

Permalink
Add integration test for ActiveRecord SQL Injection detection
Browse files Browse the repository at this point in the history
  • Loading branch information
y9v committed Jan 27, 2025
1 parent f666f2c commit 371eab8
Showing 1 changed file with 106 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# frozen_string_literal: true

require 'datadog/tracing/contrib/support/spec_helper'
require 'datadog/appsec/spec_helper'
require 'rack/test'

require 'sqlite3'
require 'active_record'
require 'datadog/tracing'
require 'datadog/appsec'

RSpec.describe 'ActiveRecord SQL Injection' do
include Rack::Test::Methods

before do
stub_const('User', Class.new(ActiveRecord::Base)).tap do |klass|
klass.establish_connection({ adapter: 'sqlite3', database: ':memory:' })

klass.connection.create_table 'users', force: :cascade do |t|
t.string :name, null: false
end

# prevent internal sql requests from showing up
klass.count
end

Datadog.configure do |c|
c.tracing.enabled = true
c.tracing.instrument :rack
c.tracing.instrument :http

c.appsec.enabled = true
c.appsec.instrument :rack
c.appsec.instrument :active_record

c.appsec.ruleset = {
rules: [
{
id: 'rasp-003-001',
name: 'SQL Injection',
tags: {
type: 'sql_injection',
category: 'exploit',
module: 'rasp'
},
conditions: [
{
operator: 'sqli_detector',
parameters: {
resource: [{ address: 'server.db.statement' }],
params: [{ address: 'server.request.query' }],
db_type: [{ address: 'server.db.system' }]
}
}
],
on_match: ['block']
}
]
}

c.remote.enabled = false
end

allow_any_instance_of(Datadog::Tracing::Transport::HTTP::Client).to receive(:send_request)
end

after do
Datadog.configuration.reset!
Datadog.registry[:rack].reset_configuration!
end

let(:app) do
stack = Rack::Builder.new do
use Datadog::Tracing::Contrib::Rack::TraceMiddleware
use Datadog::AppSec::Contrib::Rack::RequestMiddleware

map '/rasp' do
run(
lambda do |env|
request = Rack::Request.new(env)
users = User.find_by_sql(
"SELECT * FROM users WHERE name = '#{request.params['name']}'"
)

[200, { 'Content-Type' => 'application/json' }, [users.to_json]]
end
)
end
end

stack.to_app
end

let(:http_service_entry_span) do
Datadog::Tracing::Transport::TraceFormatter.format!(trace)
spans.find { |s| s.name == 'rack.request' }
end

context 'when RASP check triggered for database query' do
before do
get('/rasp', { 'name' => "Bob'; OR 1=1" }, { 'REMOTE_ADDR' => '127.0.0.1' })
end

it { expect(last_response).to be_forbidden }
end
end

0 comments on commit 371eab8

Please sign in to comment.