tcell_agent 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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