tainted_love 0.1.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +2 -0
  4. data/bin/setup +3 -3
  5. data/bin/test +6 -2
  6. data/dev.yml +1 -1
  7. data/lib/tainted_love.rb +2 -2
  8. data/lib/tainted_love/replacer/base.rb +5 -1
  9. data/lib/tainted_love/replacer/replace_action_controller.rb +0 -4
  10. data/lib/tainted_love/replacer/replace_active_record.rb +21 -1
  11. data/lib/tainted_love/replacer/replace_graphql.rb +27 -0
  12. data/lib/tainted_love/replacer/replace_kernel.rb +1 -1
  13. data/lib/tainted_love/replacer/replace_object.rb +8 -2
  14. data/lib/tainted_love/replacer/replace_rack_builder.rb +51 -0
  15. data/lib/tainted_love/replacer/replace_rack_file.rb +25 -0
  16. data/lib/tainted_love/replacer/replace_rack_query_parser.rb +50 -0
  17. data/lib/tainted_love/replacer/replace_rails_user_input.rb +12 -27
  18. data/lib/tainted_love/replacer/replace_string.rb +69 -0
  19. data/lib/tainted_love/replacer/replace_tag_builder.rb +16 -0
  20. data/lib/tainted_love/reporter/base.rb +4 -1
  21. data/lib/tainted_love/reporter/stdout_reporter.rb +1 -0
  22. data/lib/tainted_love/utils.rb +4 -19
  23. data/lib/tainted_love/utils/proxy.rb +95 -0
  24. data/lib/tainted_love/validator/action_dispatch_diagnostics.rb +20 -0
  25. data/lib/tainted_love/validator/active_record_find.rb +15 -0
  26. data/lib/tainted_love/validator/erb_eval.rb +1 -3
  27. data/lib/tainted_love/validator/haml_eval.rb +25 -0
  28. data/lib/tainted_love/validator/i18n_load.rb +17 -0
  29. data/lib/tainted_love/validator/ignore.rb +21 -0
  30. data/lib/tainted_love/version.rb +1 -1
  31. data/service.yml +6 -0
  32. data/{example → tests/rails}/.gitignore +0 -0
  33. data/{example → tests/rails}/.ruby-version +0 -0
  34. data/{example → tests/rails}/Gemfile +5 -4
  35. data/{example → tests/rails}/Gemfile.lock +29 -32
  36. data/{example → tests/rails}/README.md +0 -0
  37. data/{example → tests/rails}/Rakefile +0 -0
  38. data/{example → tests/rails}/app/assets/config/manifest.js +0 -0
  39. data/{example → tests/rails}/app/assets/images/.keep +0 -0
  40. data/{example → tests/rails}/app/assets/javascripts/application.js +0 -0
  41. data/{example → tests/rails}/app/assets/javascripts/cable.js +0 -0
  42. data/{example → tests/rails}/app/assets/javascripts/channels/.keep +0 -0
  43. data/{example → tests/rails}/app/assets/javascripts/products.coffee +0 -0
  44. data/{example → tests/rails}/app/assets/stylesheets/application.css +0 -0
  45. data/{example → tests/rails}/app/assets/stylesheets/products.scss +0 -0
  46. data/{example → tests/rails}/app/assets/stylesheets/scaffolds.scss +0 -0
  47. data/{example → tests/rails}/app/channels/application_cable/channel.rb +0 -0
  48. data/{example → tests/rails}/app/channels/application_cable/connection.rb +0 -0
  49. data/{example → tests/rails}/app/controllers/application_controller.rb +0 -0
  50. data/{example → tests/rails}/app/controllers/concerns/.keep +0 -0
  51. data/tests/rails/app/controllers/graphql_controller.rb +43 -0
  52. data/{example → tests/rails}/app/controllers/products_controller.rb +0 -0
  53. data/tests/rails/app/controllers/test_cases_controller.rb +43 -0
  54. data/tests/rails/app/graphql/example_schema.rb +4 -0
  55. data/{example/app/models/concerns → tests/rails/app/graphql/mutations}/.keep +0 -0
  56. data/{example/lib/assets → tests/rails/app/graphql/types}/.keep +0 -0
  57. data/tests/rails/app/graphql/types/base_enum.rb +4 -0
  58. data/tests/rails/app/graphql/types/base_input_object.rb +4 -0
  59. data/tests/rails/app/graphql/types/base_interface.rb +5 -0
  60. data/tests/rails/app/graphql/types/base_object.rb +4 -0
  61. data/tests/rails/app/graphql/types/base_scalar.rb +4 -0
  62. data/tests/rails/app/graphql/types/base_union.rb +4 -0
  63. data/tests/rails/app/graphql/types/mutation_type.rb +10 -0
  64. data/tests/rails/app/graphql/types/product_type.rb +10 -0
  65. data/tests/rails/app/graphql/types/query_type.rb +46 -0
  66. data/tests/rails/app/graphql/types/taint_test_case_input.rb +8 -0
  67. data/{example → tests/rails}/app/helpers/application_helper.rb +0 -0
  68. data/{example → tests/rails}/app/helpers/products_helper.rb +0 -0
  69. data/{example → tests/rails}/app/helpers/test_cases_helper.rb +0 -0
  70. data/{example → tests/rails}/app/jobs/application_job.rb +0 -0
  71. data/{example → tests/rails}/app/mailers/application_mailer.rb +0 -0
  72. data/{example → tests/rails}/app/models/application_record.rb +0 -0
  73. data/{example/lib/tasks → tests/rails/app/models/concerns}/.keep +0 -0
  74. data/{example → tests/rails}/app/models/product.rb +0 -0
  75. data/{example → tests/rails}/app/views/layouts/application.html.erb +0 -0
  76. data/{example → tests/rails}/app/views/layouts/mailer.html.erb +0 -0
  77. data/{example → tests/rails}/app/views/layouts/mailer.text.erb +0 -0
  78. data/{example → tests/rails}/app/views/products/_form.html.erb +0 -0
  79. data/{example → tests/rails}/app/views/products/_product.json.jbuilder +0 -0
  80. data/{example → tests/rails}/app/views/products/edit.html.erb +0 -0
  81. data/{example → tests/rails}/app/views/products/index.html.erb +0 -0
  82. data/{example → tests/rails}/app/views/products/index.json.jbuilder +0 -0
  83. data/{example → tests/rails}/app/views/products/new.html.erb +0 -0
  84. data/{example → tests/rails}/app/views/products/show.html.erb +0 -0
  85. data/{example → tests/rails}/app/views/products/show.json.jbuilder +0 -0
  86. data/{example → tests/rails}/app/views/test_cases/xss.html.erb +0 -0
  87. data/{example → tests/rails}/bin/bundle +0 -0
  88. data/{example → tests/rails}/bin/rails +0 -0
  89. data/{example → tests/rails}/bin/rake +0 -0
  90. data/{example → tests/rails}/bin/setup +0 -0
  91. data/{example → tests/rails}/bin/spring +0 -0
  92. data/{example → tests/rails}/bin/update +0 -0
  93. data/{example → tests/rails}/bin/yarn +0 -0
  94. data/{example → tests/rails}/config.ru +0 -0
  95. data/{example → tests/rails}/config/application.rb +0 -0
  96. data/{example → tests/rails}/config/boot.rb +0 -0
  97. data/{example → tests/rails}/config/cable.yml +0 -0
  98. data/{example → tests/rails}/config/credentials.yml.enc +0 -0
  99. data/{example → tests/rails}/config/database.yml +0 -0
  100. data/{example → tests/rails}/config/environment.rb +0 -0
  101. data/{example → tests/rails}/config/environments/development.rb +0 -0
  102. data/{example → tests/rails}/config/environments/production.rb +0 -0
  103. data/{example → tests/rails}/config/environments/test.rb +0 -0
  104. data/{example → tests/rails}/config/initializers/application_controller_renderer.rb +0 -0
  105. data/{example → tests/rails}/config/initializers/assets.rb +0 -0
  106. data/{example → tests/rails}/config/initializers/backtrace_silencers.rb +0 -0
  107. data/{example → tests/rails}/config/initializers/content_security_policy.rb +0 -0
  108. data/{example → tests/rails}/config/initializers/cookies_serializer.rb +0 -0
  109. data/{example → tests/rails}/config/initializers/filter_parameter_logging.rb +0 -0
  110. data/{example → tests/rails}/config/initializers/inflections.rb +0 -0
  111. data/{example → tests/rails}/config/initializers/mime_types.rb +0 -0
  112. data/{example → tests/rails}/config/initializers/tainted_love.rb +0 -0
  113. data/{example → tests/rails}/config/initializers/wrap_parameters.rb +0 -0
  114. data/{example → tests/rails}/config/locales/en.yml +0 -0
  115. data/{example → tests/rails}/config/puma.rb +0 -0
  116. data/{example → tests/rails}/config/routes.rb +6 -0
  117. data/{example → tests/rails}/config/spring.rb +0 -0
  118. data/{example → tests/rails}/config/storage.yml +0 -0
  119. data/{example → tests/rails}/db/migrate/20190311220346_create_products.rb +0 -0
  120. data/{example → tests/rails}/db/schema.rb +0 -0
  121. data/{example → tests/rails}/db/seeds.rb +0 -0
  122. data/{example/log → tests/rails/lib/assets}/.keep +0 -0
  123. data/{example/storage → tests/rails/lib/tasks}/.keep +0 -0
  124. data/{example/test/controllers → tests/rails/log}/.keep +0 -0
  125. data/{example → tests/rails}/package.json +0 -0
  126. data/{example → tests/rails}/public/404.html +0 -0
  127. data/{example → tests/rails}/public/422.html +0 -0
  128. data/{example → tests/rails}/public/500.html +0 -0
  129. data/{example → tests/rails}/public/apple-touch-icon-precomposed.png +0 -0
  130. data/{example → tests/rails}/public/apple-touch-icon.png +0 -0
  131. data/{example → tests/rails}/public/favicon.ico +0 -0
  132. data/{example → tests/rails}/public/robots.txt +0 -0
  133. data/{example/test/fixtures → tests/rails/storage}/.keep +0 -0
  134. data/tests/rails/test.sh +1 -0
  135. data/{example → tests/rails}/test/application_system_test_case.rb +0 -0
  136. data/{example/test/fixtures/files → tests/rails/test/controllers}/.keep +0 -0
  137. data/tests/rails/test/controllers/graphql_controller_test.rb +28 -0
  138. data/{example → tests/rails}/test/controllers/products_controller_test.rb +0 -0
  139. data/tests/rails/test/controllers/test_cases_controller_test.rb +54 -0
  140. data/{example/test/helpers → tests/rails/test/fixtures}/.keep +0 -0
  141. data/{example/test/integration → tests/rails/test/fixtures/files}/.keep +0 -0
  142. data/{example → tests/rails}/test/fixtures/products.yml +0 -0
  143. data/{example/test/mailers → tests/rails/test/helpers}/.keep +0 -0
  144. data/{example/test/models → tests/rails/test/integration}/.keep +0 -0
  145. data/{example/test/system → tests/rails/test/mailers}/.keep +0 -0
  146. data/{example/tmp → tests/rails/test/models}/.keep +0 -0
  147. data/{example → tests/rails}/test/models/product_test.rb +0 -0
  148. data/{example → tests/rails}/test/replacers/replace_active_record_test.rb +28 -0
  149. data/tests/rails/test/replacers/replace_rails_user_input_test.rb +13 -0
  150. data/{example → tests/rails}/test/replacers/replace_sprokets_test.rb +0 -0
  151. data/{example/vendor → tests/rails/test/system}/.keep +0 -0
  152. data/{example → tests/rails}/test/system/products_test.rb +0 -0
  153. data/{example → tests/rails}/test/test_helper.rb +0 -0
  154. data/tests/rails/tmp/.keep +0 -0
  155. data/tests/rails/vendor/.keep +0 -0
  156. data/tests/sinatra/Gemfile +3 -0
  157. data/tests/sinatra/Gemfile.lock +29 -0
  158. data/tests/sinatra/app.rb +26 -0
  159. data/tests/sinatra/test.sh +1 -0
  160. data/tests/sinatra/views/xss.erb +1 -0
  161. data/tools/web/Gemfile +1 -1
  162. data/tools/web/application.rb +17 -2
  163. data/tools/web/public/application.css +38 -2
  164. data/tools/web/views/index.erb +5 -11
  165. data/tools/web/views/input.erb +4 -0
  166. data/tools/web/views/line.erb +2 -2
  167. metadata +146 -111
  168. data/example/app/controllers/test_cases_controller.rb +0 -20
  169. data/example/test/controllers/test_cases_controller_test.rb +0 -39
File without changes
File without changes
@@ -0,0 +1,43 @@
1
+ class GraphqlController < ApplicationController
2
+ def execute
3
+ variables = ensure_hash(params[:variables])
4
+ query = params[:query]
5
+ operation_name = params[:operationName]
6
+ context = {
7
+ # Query context goes here, for example:
8
+ # current_user: current_user,
9
+ }
10
+ result = ExampleSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
11
+ render json: result
12
+ rescue => e
13
+ raise e unless Rails.env.development?
14
+ handle_error_in_development e
15
+ end
16
+
17
+ private
18
+
19
+ # Handle form data, JSON body, or a blank value
20
+ def ensure_hash(ambiguous_param)
21
+ case ambiguous_param
22
+ when String
23
+ if ambiguous_param.present?
24
+ ensure_hash(JSON.parse(ambiguous_param))
25
+ else
26
+ {}
27
+ end
28
+ when Hash, ActionController::Parameters
29
+ ambiguous_param
30
+ when nil
31
+ {}
32
+ else
33
+ raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
34
+ end
35
+ end
36
+
37
+ def handle_error_in_development(e)
38
+ logger.error e.message
39
+ logger.error e.backtrace.join("\n")
40
+
41
+ render json: { error: { message: e.message, backtrace: e.backtrace }, data: {} }, status: 500
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TestCasesController < ApplicationController
4
+ layout false
5
+
6
+ def xss
7
+ end
8
+
9
+ def unsafe_render
10
+ render(params[:file])
11
+ end
12
+
13
+ def render_inline
14
+ render(inline: params[:template])
15
+ end
16
+
17
+ def unsafe_redirect
18
+ redirect_to(params[:to])
19
+ end
20
+
21
+ def taint_test
22
+ cookies[:something] = 'asdf'
23
+
24
+ values = {
25
+ 'route_parameter_value' => params[:route_param],
26
+ 'get_parameter' => params[:get_param],
27
+ 'get_array_parameter_0' => params[:get_array_param][0],
28
+ 'get_array_parameter_1' => params[:get_array_param][1],
29
+ 'parameter_name' => params.keys.first,
30
+ 'header_value' => request.headers['Host'],
31
+ 'header_name' => request.headers.to_h.keys.select { |k| k['HTTP_AAA'] }.first,
32
+ 'cookie_value' => cookies[:something],
33
+ 'cookie_name' => cookies.to_h.keys.first,
34
+ 'fullpath' => request.original_fullpath
35
+ }
36
+
37
+ description = values.keys
38
+ tainted = values.values.map(&:tainted?)
39
+ sources = values.values.map(&:tainted_love_tags)
40
+
41
+ render json: description.zip(values.values, tainted, sources)
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ class ExampleSchema < GraphQL::Schema
2
+ mutation(Types::MutationType)
3
+ query(Types::QueryType)
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types
2
+ class BaseEnum < GraphQL::Schema::Enum
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types
2
+ class BaseInputObject < GraphQL::Schema::InputObject
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Types
2
+ module BaseInterface
3
+ include GraphQL::Schema::Interface
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Types
2
+ class BaseObject < GraphQL::Schema::Object
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types
2
+ class BaseScalar < GraphQL::Schema::Scalar
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Types
2
+ class BaseUnion < GraphQL::Schema::Union
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module Types
2
+ class MutationType < Types::BaseObject
3
+ # TODO: remove me
4
+ field :test_field, String, null: false,
5
+ description: "An example field added by the generator"
6
+ def test_field
7
+ "Hello World"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Types
2
+ class ProductType < Types::BaseObject
3
+ field :id, Integer, null: false
4
+ field :name, String, null: true
5
+ field :price, Integer, null: false
6
+ field :description, String, null: true
7
+
8
+ field :products, [ProductType], null: true
9
+ end
10
+ end
@@ -0,0 +1,46 @@
1
+ module Types
2
+ class QueryType < Types::BaseObject
3
+ field :object_argument_taint_test_case, Boolean, null: false do
4
+ argument :input, TaintTestCaseInput, required: true
5
+ end
6
+
7
+ field :string_argument_taint_test_case, Boolean, null: false do
8
+ argument :input, String, required: true
9
+ argument :input_default, String, required: false, default_value: "something"
10
+ end
11
+
12
+ field :mutate_context, Integer, null: true do
13
+ argument :id, Integer, required: true
14
+ end
15
+
16
+ field :whoami, Integer, null: true
17
+
18
+ field :products, [ProductType], null: true
19
+
20
+ def object_argument_taint_test_case(input:)
21
+ [
22
+ input.top_level_string.tainted?,
23
+ !input.default_value_untainted.tainted?
24
+ ].all?
25
+ end
26
+
27
+ def string_argument_taint_test_case(input:, input_default:)
28
+ [
29
+ input.tainted?,
30
+ !input_default.tainted?
31
+ ].all?
32
+ end
33
+
34
+ def mutate_context(id:)
35
+ context[:user_id] = id
36
+ end
37
+
38
+ def whoami
39
+ context[:user_id]
40
+ end
41
+
42
+ def products
43
+ Product.all
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Types
4
+ class TaintTestCaseInput < Types::BaseInputObject
5
+ argument :top_level_string, String, required: true
6
+ argument :default_value_untainted, String, required: false, default_value: "A value"
7
+ end
8
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Rails.application.routes.draw do
4
+ if Rails.env.development?
5
+ mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
6
+ end
7
+ post "/graphql", to: "graphql#execute"
4
8
  get 'test_cases/xss'
5
9
  get 'test_cases/unsafe_render'
6
10
  get 'test_cases/render_inline'
7
11
  get 'test_cases/unsafe_redirect'
12
+ get 'test_cases/taint_test/:route_param' => 'test_cases#taint_test', as: 'test_cases_taint_test'
13
+
8
14
  resources :products
9
15
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
10
16
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1 @@
1
+ bundle exec rails test
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class GraphQLControllerTest < ActionDispatch::IntegrationTest
6
+ test "string arguments are tainted" do
7
+ response = run_graphql('{ stringArgumentTaintTestCase(input:"asdf") }')
8
+
9
+ assert_equal({ "data" => { "stringArgumentTaintTestCase" => true } }, response)
10
+ end
11
+
12
+ test "string in object arguments are tainted" do
13
+ response = run_graphql('{ objectArgumentTaintTestCase(input: { topLevelString: "Asdf" }) }')
14
+
15
+ assert_equal({ "data" => { "objectArgumentTaintTestCase" => true } }, response)
16
+ end
17
+
18
+ def run_graphql(query)
19
+ params = {
20
+ query: query
21
+ }
22
+
23
+ post graphql_url, params: params
24
+ assert_response :success
25
+
26
+ response.parsed_body
27
+ end
28
+ end