tcell_agent 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +4 -0
  3. data/README.md +43 -0
  4. data/Rakefile +7 -0
  5. data/bin/tcell_agent +171 -0
  6. data/config/initializers/authlogic_auth.rb +51 -0
  7. data/config/initializers/devise_auth.rb +167 -0
  8. data/config/initializers/init.rb +8 -0
  9. data/lib/tcell_agent.rb +33 -0
  10. data/lib/tcell_agent/agent.rb +79 -0
  11. data/lib/tcell_agent/agent/event_processor.rb +133 -0
  12. data/lib/tcell_agent/agent/policy_manager.rb +138 -0
  13. data/lib/tcell_agent/agent/policy_types.rb +42 -0
  14. data/lib/tcell_agent/agent/static_agent.rb +22 -0
  15. data/lib/tcell_agent/api.rb +101 -0
  16. data/lib/tcell_agent/appsensor.rb +42 -0
  17. data/lib/tcell_agent/appsensor/cmdi.rb +32 -0
  18. data/lib/tcell_agent/appsensor/path_traversal.rb +33 -0
  19. data/lib/tcell_agent/appsensor/sqli.rb +55 -0
  20. data/lib/tcell_agent/appsensor/xss.rb +40 -0
  21. data/lib/tcell_agent/authlogic.rb +26 -0
  22. data/lib/tcell_agent/configuration.rb +148 -0
  23. data/lib/tcell_agent/dataloss.rb +0 -0
  24. data/lib/tcell_agent/devise.rb +83 -0
  25. data/lib/tcell_agent/instrumentation.rb +44 -0
  26. data/lib/tcell_agent/logger.rb +46 -0
  27. data/lib/tcell_agent/policies/add_script_tag_policy.rb +47 -0
  28. data/lib/tcell_agent/policies/appsensor_policy.rb +76 -0
  29. data/lib/tcell_agent/policies/clickjacking_policy.rb +113 -0
  30. data/lib/tcell_agent/policies/content_security_policy.rb +119 -0
  31. data/lib/tcell_agent/policies/dataloss_policy.rb +175 -0
  32. data/lib/tcell_agent/policies/honeytokens_policy.rb +67 -0
  33. data/lib/tcell_agent/policies/http_redirect_policy.rb +84 -0
  34. data/lib/tcell_agent/policies/http_tx_policy.rb +60 -0
  35. data/lib/tcell_agent/policies/login_fraud_policy.rb +42 -0
  36. data/lib/tcell_agent/policies/secure_headers_policy.rb +64 -0
  37. data/lib/tcell_agent/rails.rb +146 -0
  38. data/lib/tcell_agent/rails/devise.rb +0 -0
  39. data/lib/tcell_agent/rails/dlp.rb +204 -0
  40. data/lib/tcell_agent/rails/middleware/body_filter_middleware.rb +69 -0
  41. data/lib/tcell_agent/rails/middleware/context_middleware.rb +50 -0
  42. data/lib/tcell_agent/rails/middleware/global_middleware.rb +53 -0
  43. data/lib/tcell_agent/rails/middleware/headers_middleware.rb +176 -0
  44. data/lib/tcell_agent/rails/routes.rb +130 -0
  45. data/lib/tcell_agent/rails/settings_reporter.rb +40 -0
  46. data/lib/tcell_agent/sensor_events/app_config.rb +16 -0
  47. data/lib/tcell_agent/sensor_events/app_sensor.rb +240 -0
  48. data/lib/tcell_agent/sensor_events/dlp.rb +58 -0
  49. data/lib/tcell_agent/sensor_events/honeytokens.rb +16 -0
  50. data/lib/tcell_agent/sensor_events/login_fraud.rb +43 -0
  51. data/lib/tcell_agent/sensor_events/metrics.rb +24 -0
  52. data/lib/tcell_agent/sensor_events/sensor.rb +85 -0
  53. data/lib/tcell_agent/sensor_events/server_agent.rb +101 -0
  54. data/lib/tcell_agent/sensor_events/util/redirect_utils.rb +22 -0
  55. data/lib/tcell_agent/sensor_events/util/sanitizer_utilities.rb +153 -0
  56. data/lib/tcell_agent/sensor_events/util/utils.rb +21 -0
  57. data/lib/tcell_agent/sinatra.rb +41 -0
  58. data/lib/tcell_agent/start_background_thread.rb +63 -0
  59. data/lib/tcell_agent/userinfo.rb +8 -0
  60. data/lib/tcell_agent/utils/queue_with_timeout.rb +60 -0
  61. data/lib/tcell_agent/version.rb +5 -0
  62. data/spec/controllers/application_controller.rb +12 -0
  63. data/spec/lib/tcell_agent/api/api_spec.rb +36 -0
  64. data/spec/lib/tcell_agent/appsensor_spec.rb +66 -0
  65. data/spec/lib/tcell_agent/policies/add_script_tag_policy_spec.rb +37 -0
  66. data/spec/lib/tcell_agent/policies/appsensor_policy_spec.rb +40 -0
  67. data/spec/lib/tcell_agent/policies/clickjacking_policy_spec.rb +71 -0
  68. data/spec/lib/tcell_agent/policies/content_security_policy_spec.rb +71 -0
  69. data/spec/lib/tcell_agent/policies/dataloss_policy_spec.rb +88 -0
  70. data/spec/lib/tcell_agent/policies/honeytokens_policy_spec.rb +22 -0
  71. data/spec/lib/tcell_agent/policies/http_redirect_policy_spec.rb +62 -0
  72. data/spec/lib/tcell_agent/policies/http_tx_policy_spec.rb +22 -0
  73. data/spec/lib/tcell_agent/policies/login_policy_spec.rb +42 -0
  74. data/spec/lib/tcell_agent/policies/secure_headers_policy_spec.rb +67 -0
  75. data/spec/lib/tcell_agent/rails/middleware/global_middleware_spec.rb +187 -0
  76. data/spec/lib/tcell_agent/rails_spec.rb +57 -0
  77. data/spec/lib/tcell_agent/sensor_events/dlp_spec.rb +14 -0
  78. data/spec/lib/tcell_agent/sensor_events/util/redirect_utils_spec.rb +25 -0
  79. data/spec/lib/tcell_agent/sensor_events/util/sanitizer_utilities_spec.rb +57 -0
  80. data/spec/lib/tcell_agent_spec.rb +22 -0
  81. data/spec/resources/normal_config.json +13 -0
  82. data/spec/spec_helper.rb +4 -0
  83. data/tcell_agent.gemspec +29 -0
  84. metadata +249 -0
@@ -0,0 +1,21 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'logger'
4
+ require 'cgi'
5
+ require 'uri'
6
+ require 'openssl'
7
+
8
+ module TCellAgent
9
+ module SensorEvents
10
+ module Util
11
+ def self.jhash(str)
12
+ str.each_char.reduce(0) do |result, char|
13
+ [((result << 5) - result) + char.ord].pack('L').unpack('l').first
14
+ end
15
+ end
16
+ def self.calculateRouteId(method, path, params=nil)
17
+ jhash("#{method}|#{path}")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'sinatra/base'
4
+ require 'sinatra'
5
+ require 'tcell_agent/agent'
6
+ require 'tcell_agent/instrumentation'
7
+
8
+ module TCellAgent
9
+ class Sinatra::Response
10
+ include Sinatra
11
+ alias_method :original_finish, :finish
12
+ def finish
13
+ status, headers, response = original_finish
14
+
15
+ TCellAgent::Instrumentation.safe_block("Setting CSP Headers") {
16
+ content_security_policy = TCellAgent.policy(TCellAgent::PolicyTypes::CSP)
17
+ if content_security_policy
18
+ content_security_policy.each(
19
+ nil,
20
+ nil,
21
+ nil) do | header_pair |
22
+ headers[header_pair["name"]] = header_pair["value"]
23
+ end
24
+ end
25
+ }
26
+
27
+ TCellAgent::Instrumentation.safe_block("Setting Secure Headers") {
28
+ secure_headers_policy = TCellAgent.policy(TCellAgent::PolicyTypes::SecureHeaders)
29
+ if secure_headers_policy
30
+ secure_headers_policy.headers.each do | secure_header |
31
+ headers[secure_header.name] = secure_header.value
32
+ end
33
+ end
34
+ }
35
+
36
+ [status, headers, response]
37
+ end
38
+ end
39
+ end
40
+
41
+
@@ -0,0 +1,63 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require 'tcell_agent/logger'
4
+ require 'tcell_agent/agent'
5
+ require 'tcell_agent/configuration'
6
+
7
+ agent = ::TCellAgent::Agent.new(Process.pid)
8
+ TCellAgent.thread_agent = agent
9
+
10
+ # creates a fork and pipes a string from parent to child
11
+ if File.basename($0) != 'rake'
12
+ if (File.basename($0) == 'rails' &&
13
+ ((defined?(Thin) && (Rack::Handler.default == Rack::Handler::Thin)) ||
14
+ (defined?(WEBrick) && (Rack::Handler.default == Rack::Handler::WEBrick))))
15
+ TCellAgent.logger.debug("Initializing background thread: Thin")
16
+ begin
17
+ TCellAgent.thread_agent = agent
18
+ agent.start
19
+ rescue Exception => e
20
+ TCellAgent.logger.error("Could not start worker.", e.message)
21
+ end
22
+ elsif defined?(Puma) && defined?(Puma.cli_config) && Puma.cli_config.options[:workers] > 1
23
+ agent.start_event_processor(false)
24
+ # preload app
25
+ TCellAgent.logger.debug("Initializing background thread: Puma Preload")
26
+ puma_worker_start = Proc.new do
27
+ begin
28
+ agent = TCellAgent::Agent.new(Process.pid)
29
+ TCellAgent.thread_agent = agent
30
+ agent.start
31
+ rescue Exception => e
32
+ TCellAgent.logger.error("Could not start worker.", e.message)
33
+ end
34
+ end
35
+ Puma.cli_config.options[:before_worker_boot].push(puma_worker_start)
36
+ elsif defined?(Unicorn) && Unicorn::HttpServer::LISTENERS.length == 0
37
+ agent.start_event_processor(false)
38
+ TCellAgent.logger.debug("Initializing background thread: Unicorn Preload")
39
+ class Unicorn::HttpServer
40
+ alias old_init_worker_process init_worker_process
41
+ def init_worker_process(work)
42
+ begin
43
+ agent = TCellAgent::Agent.new(Process.pid)
44
+ TCellAgent.thread_agent = agent
45
+ agent.start
46
+ rescue Exception => e
47
+ TCellAgent.logger.error("Could not start worker.", e.message)
48
+ end
49
+ return old_init_worker_process(work)
50
+ end
51
+ end
52
+ else
53
+ TCellAgent.logger.debug("Initializing background thread (no-preload).")
54
+ begin
55
+ TCellAgent.thread_agent = agent
56
+ agent.start
57
+ rescue Exception => e
58
+ TCellAgent.logger.error("Could not start worker.", e.message)
59
+ end
60
+ end
61
+ end
62
+
63
+
@@ -0,0 +1,8 @@
1
+ module TCellAgent
2
+ class UserInformation
3
+ # Devise
4
+ def self.getUserFromRequest(request)
5
+ return nil
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ require "tcell_agent/logger"
4
+ require 'thread'
5
+ require 'logger'
6
+
7
+ module TCellAgent
8
+ class QueueWithTimeout
9
+ def initialize
10
+ @mutex = Mutex.new
11
+ @queue = []
12
+ @response_time_table = {}
13
+ @recieved = ConditionVariable.new
14
+ end
15
+
16
+ def <<(x)
17
+ @mutex.synchronize do
18
+ @queue << x
19
+ @recieved.signal
20
+ end
21
+ end
22
+ def add_response_time(route_id, response_time)
23
+ @mutex.synchronize do
24
+ if (route_id == nil || route_id == "")
25
+ route_id = "?"
26
+ end
27
+ @response_time_table[route_id] = @response_time_table.fetch(route_id,{})
28
+ @response_time_table[route_id]["c"] = @response_time_table[route_id].fetch("c",0) + 1
29
+ @response_time_table[route_id]["mx"] = [@response_time_table[route_id].fetch("mx",0), response_time].max
30
+ @response_time_table[route_id]["mn"] = [@response_time_table[route_id].fetch("mn",response_time), response_time].min
31
+ @response_time_table[route_id]["t"] = ((@response_time_table[route_id].fetch("t",0)*(@response_time_table[route_id]["c"]-1)) + response_time) / @response_time_table[route_id]["c"]
32
+ end
33
+ end
34
+ def length
35
+ return @queue.length
36
+ end
37
+ def get_response_time_table
38
+ return @response_time_table
39
+ end
40
+ def reset_response_time_table
41
+ @mutex.synchronize do
42
+ @response_time_table = {}
43
+ end
44
+ end
45
+ def pop(non_block = false)
46
+ pop_with_timeout(non_block ? 0 : nil)
47
+ end
48
+
49
+ def pop_with_timeout(timeout = nil)
50
+ @mutex.synchronize do
51
+ if @queue.empty?
52
+ @recieved.wait(@mutex, timeout) if timeout != 0
53
+ #if we're still empty after the timeout, raise exception
54
+ raise ThreadError, "queue empty" if @queue.empty?
55
+ end
56
+ @queue.shift
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # See the file "LICENSE" for the full license governing this code.
2
+
3
+ module TCellAgent
4
+ VERSION = "0.2.2"
5
+ end
@@ -0,0 +1,12 @@
1
+ describe 'GET /' do
2
+ context 'Get homepage' do
3
+ it 'Adds CSP Headers' do
4
+
5
+ user = create(:user)
6
+
7
+ post :create, session: { email: user.email, password: 'invalid' }
8
+
9
+ expect(response).to render_template(:new)
10
+ expect(flash[:notice]).to match(/^Email and password do not match/)
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'addressable/template'
3
+
4
+
5
+ module TCellAgent
6
+ class TCellApi
7
+ describe "successful POST on /user/create" do
8
+ it "should redirect to dashboard" do
9
+ tapi = TCellApi.new
10
+ TCellAgent.configuration.app_id = "test-appid"
11
+ TCellAgent.configuration.api_key = "test-apikey"
12
+
13
+ def checkreq(req)
14
+ return '{"result":{"csp-headers":{"app_id":"testapp-Becwu","policy_id":' \
15
+ '"acf60560-4e76-11e5-874c-7d71d425b275","headers":[{"name":"Content-Security-Policy-Report-Only",' \
16
+ '"value":"font-src \'none\'; script-src \'self\'; reflected-xss block; ' \
17
+ 'style-src \'self\'; connect-src' \
18
+ ' \'none\'" ,"report-uri":"http://localhost:3000/csp/cab5e750e66d614bd46fd07a7078db1e74b4f427b2a135b2c96eca684a642707"}]}}}'
19
+ end
20
+ uri_template =
21
+ Addressable::Template.new "https://api.tcell.io/api/v1/app/{app}/update"
22
+ stub_request(:any, uri_template).
23
+ to_return(lambda { |request| {
24
+ :body => checkreq(request), :status => 200,
25
+ :headers => { 'Content-Tyoe' => 'application/json' }
26
+ } })
27
+
28
+ # to_return(:body => resbody,
29
+ result = tapi.pollAPI
30
+ TCellAgent.configuration.app_id = nil
31
+ TCellAgent.configuration.api_key = nil
32
+ expect(result["csp-headers"]["app_id"]).to eq("testapp-Becwu")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ describe AppSensor do
5
+
6
+ context "Safe String" do
7
+ it "Tests Generally Safe Strings" do
8
+ expect(AppSensor.generallySafe("abc def")).to eq(true)
9
+ expect(AppSensor.generallySafe("abc d\">ef")).to eq(false)
10
+ expect(AppSensor.generallySafe("abc \0>ef")).to eq(false)
11
+ end
12
+ end
13
+ context "XSS Detection Test" do
14
+ it "Tests Standard XSS Probes" do
15
+ expect(AppSensor.isXss("abc def")).to eq(false)
16
+ expect(AppSensor.isXss("O'Reilly")).to eq(false)
17
+
18
+ expect(AppSensor.isXss("abc\"><script>")).to eq(true)
19
+ expect(AppSensor.isXss("<script>")).to eq(true)
20
+ expect(AppSensor.isXss("O'><img src='x' onerror='alert(1)'>Reilly")).to eq(true)
21
+ expect(AppSensor.isXss("O'><img/src='x' onerror='alert(1)'>Reilly")).to eq(true)
22
+ expect(AppSensor.isXss("\"><script>alert(/XSSPOSED/);</script>")).to eq(true)
23
+ end
24
+ end
25
+ context "SQLi Detection Test" do
26
+ it "Tests Standard XSS Probes" do
27
+ expect(AppSensor.isSqli("abc def")).to eq(false)
28
+ expect(AppSensor.isSqli("555--")).to eq(false)
29
+ expect(AppSensor.isSqli("555--666")).to eq(false)
30
+ expect(AppSensor.isSqli("I should order by friday")).to eq(false)
31
+ expect(AppSensor.isSqli('b<img src="a"><script>alert(1)</script>')).to eq(false)
32
+
33
+ expect(AppSensor.isSqli("a\" OR \"5\"= \"5")).to eq(true)
34
+ expect(AppSensor.isSqli("a' OR '5'= '5")).to eq(true)
35
+ expect(AppSensor.isSqli("a';--")).to eq(true)
36
+ expect(AppSensor.isSqli("a'--")).to eq(true)
37
+ #expect(AppSensor.isSqli("4 OR 3=3--")).to eq(true)
38
+ expect(AppSensor.isSqli("10 OrDeR By 10--")).to eq(true)
39
+ end
40
+ end
41
+ context "Cmd Injection Test" do
42
+ it "Tests for null character" do
43
+ expect(AppSensor.isCmdi("bob.txt echo 'hi'")).to eq(false)
44
+ expect(AppSensor.isCmdi("bob.txt; echo 'hi'")).to eq(true)
45
+ end
46
+ end
47
+ context "Path Traversal Test" do
48
+ it "Tests for path traversal" do
49
+ expect(AppSensor.isPathTraversal("bob.txt echo 'hi'")).to eq(false)
50
+ expect(AppSensor.isPathTraversal("3/.5")).to eq(false)
51
+ expect(AppSensor.isPathTraversal("../../../test.config")).to eq(true)
52
+ expect(AppSensor.isPathTraversal("/etc/passwd")).to eq(true)
53
+ end
54
+ end
55
+ context "String Characters Test" do
56
+ it "Tests for null character" do
57
+ expect(AppSensor.containsReturnChars("abc def")).to eq(false)
58
+ expect(AppSensor.containsReturnChars("abc\x0adef")).to eq(true)
59
+ expect(AppSensor.containsReturnChars("abc\x0a\x0ddef")).to eq(true)
60
+ expect(AppSensor.containsReturnChars("abc\x0ddef")).to eq(true)
61
+ expect(AppSensor.containsReturnChars("abc\x00 def")).to eq(false)
62
+ end
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ module Policies
5
+ describe AddScriptTagPolicy do
6
+ policy_json_empty = {
7
+ "policy_id"=>"01a1",
8
+ "data"=>{
9
+ }
10
+ }
11
+
12
+ policy_json_one = {
13
+ "policy_id"=>"01a1",
14
+ "data"=>{
15
+ "js_agent_api_key"=>"000-000-1"
16
+ }
17
+ }
18
+
19
+ empty_policy = AddScriptTagPolicy.fromJson(policy_json_empty)
20
+ context "test empty agent" do
21
+ it "enabled is false" do
22
+ expect(empty_policy.policy_id).to eq("01a1")
23
+ expect(empty_policy.enabled).to eq(false)
24
+ end
25
+ end
26
+
27
+ from_json = AddScriptTagPolicy.fromJson(policy_json_one)
28
+ context "tests xss is true and enabled true" do
29
+ it "returns true" do
30
+ expect(from_json.policy_id).to eq("01a1")
31
+ expect(from_json.enabled).to eq(true)
32
+ expect(from_json.js_agent_api_key).to eq("000-000-1")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ module Policies
5
+ describe AppSensorPolicy do
6
+ policy_json_empty = {
7
+ "policy_id"=>"01a1",
8
+ "data"=>{
9
+ "options"=>{}
10
+ }
11
+ }
12
+
13
+ policy_json_one = {
14
+ "policy_id"=>"01a1",
15
+ "data"=>{
16
+ "options"=>{
17
+ "xss"=>true
18
+ }
19
+ }
20
+ }
21
+
22
+ empty_policy = AppSensorPolicy.fromJson(policy_json_empty)
23
+ context "test empty agent" do
24
+ it "enabled is false" do
25
+ expect(empty_policy.policy_id).to eq("01a1")
26
+ expect(empty_policy.enabled).to eq(false)
27
+ end
28
+ end
29
+
30
+ from_json = AppSensorPolicy.fromJson(policy_json_one)
31
+ context "tests xss is true and enabled true" do
32
+ it "returns true" do
33
+ expect(from_json.policy_id).to eq("01a1")
34
+ expect(from_json.enabled).to eq(true)
35
+ expect(from_json.option_enabled?("xss")).to eq(true)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ module TCellAgent
4
+ module Policies
5
+ describe ClickjackingPolicy do
6
+ content_security_policy_json = {
7
+ "policy_id"=>"00a1",
8
+ "headers"=>[
9
+ {"name"=>"csp", "value"=>"csp header value"}
10
+ ]
11
+ }
12
+ csp_from_json = ClickjackingPolicy.fromJson(content_security_policy_json)
13
+ context "initialized with 3 items" do
14
+ it "returns true" do
15
+ expect(csp_from_json.policy_id).to eq("00a1")
16
+ expect(csp_from_json.headers[0].type).to eq("csp")
17
+ expect(csp_from_json.headers[0].value).to eq("csp header value")
18
+ end
19
+ end
20
+ context "headers match up appropriately" do
21
+ it "returns content-security-policy headers" do
22
+ expect(ClickjackingPolicy.cspHeadersForType("csp")).to match_array(["Content-Security-Policy"])#,"X-Content-Security-Policy","X-WebKit-CSP"])
23
+ end
24
+ end
25
+ end
26
+ describe ContentSecurityPolicy do
27
+ content_security_policy_json = {
28
+ "policy_id"=>"01a1",
29
+ "headers"=>[
30
+ {"name"=>"csp-header-is-bad", "value"=>"csp header value"}
31
+ ]
32
+ }
33
+ csp_policy = ClickjackingPolicy.fromJson(content_security_policy_json)
34
+ context "csp header example, invalid header" do
35
+ it "returns false" do
36
+ expect(csp_policy.headers.length).to eq(0)
37
+ end
38
+ end
39
+ end
40
+ describe ClickjackingPolicy do
41
+ content_security_policy_json = {
42
+ "policy_id"=>"01a1",
43
+ "headers"=>[
44
+ {"name"=>"csp", "value"=>"value123\\nabc"}
45
+ ]
46
+ }
47
+ csp_policy = ClickjackingPolicy.fromJson(content_security_policy_json)
48
+ context "secure header, value is bad" do
49
+ it "returns false" do
50
+ expect(csp_policy.headers.length).to eq(0)
51
+ end
52
+ end
53
+ end
54
+ describe ClickjackingPolicy do
55
+ content_security_policy_json = {
56
+ "policy_id"=>"01a1",
57
+ "headers"=>[
58
+ {"name"=>"csp", "value"=>"value normal", "report-uri"=>"https://example.com/abcdde"}
59
+ ]
60
+ }
61
+ csp_policy = ClickjackingPolicy.fromJson(content_security_policy_json)
62
+ context "secure header, report-uri seperate" do
63
+ it "returns false" do
64
+ expect(csp_policy.headers.length).to eq(1)
65
+ expect(csp_policy.headers[0].value).to eq("value normal; report-uri https://example.com/abcdde")
66
+ expect(csp_policy.headers[0].value("1","2","3")).to eq("value normal; report-uri https://example.com/abcdde?tid=1&sid=2&uid=3")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end