gitlab-secret_detection 0.12.0 → 0.13.0
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.
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f72d7d8262749305fdf1811df74637af4f7ba4bbf693fc41d2cdb34d1ca25238
         | 
| 4 | 
            +
              data.tar.gz: 6896b4eb32dc734640c0dc125dd75e79b765f27aa618f73fec81c83c0c20df93
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 2d35dfa136b9524f851a6ed20885aa6d50b9334f12166fd9d131ff523ed38bed1264bad953fd6fa58e63fe2c5f9c62be129d3be231258eef25990803b9b67d4e
         | 
| 7 | 
            +
              data.tar.gz: 331a74ba7a779f275717c769c0f63785d7e74d25b07da5d07f44c08a1a0c7f343eec3ba7fd8285d549d298fac4d93bdc3f2bc122ea7cb30bd567e4c75e43efdb
         | 
| @@ -7,14 +7,16 @@ module Gitlab | |
| 7 7 | 
             
                  #
         | 
| 8 8 | 
             
                  # +status+:: One of values from Gitlab::SecretDetection::Core::Status indicating the scan operation's status
         | 
| 9 9 | 
             
                  # +results+:: Array of Gitlab::SecretDetection::Core::Finding values. Default value is nil.
         | 
| 10 | 
            -
                  # +metadata+:: Hash object containing additional meta information about the response. It is currently used
         | 
| 11 10 | 
             
                  #   to embed more information on error.
         | 
| 11 | 
            +
                  # +applied_exclusions+:: Array of Exclusions that were applied during this scan.
         | 
| 12 | 
            +
                  # +metadata+:: Hash object containing additional meta information about the response. It is currently used
         | 
| 12 13 | 
             
                  class Response
         | 
| 13 | 
            -
                    attr_reader :status, :results, :metadata
         | 
| 14 | 
            +
                    attr_reader :status, :results, :applied_exclusions, :metadata
         | 
| 14 15 |  | 
| 15 | 
            -
                    def initialize(status | 
| 16 | 
            +
                    def initialize(status:, results: [], applied_exclusions: [], metadata: {})
         | 
| 16 17 | 
             
                      @status = status
         | 
| 17 18 | 
             
                      @results = results
         | 
| 19 | 
            +
                      @applied_exclusions = applied_exclusions
         | 
| 18 20 | 
             
                      @metadata = metadata
         | 
| 19 21 | 
             
                    end
         | 
| 20 22 |  | 
| @@ -25,15 +27,21 @@ module Gitlab | |
| 25 27 | 
             
                    def to_h
         | 
| 26 28 | 
             
                      {
         | 
| 27 29 | 
             
                        status:,
         | 
| 28 | 
            -
                         | 
| 29 | 
            -
                         | 
| 30 | 
            +
                        results: results&.map(&:to_h),
         | 
| 31 | 
            +
                        applied_exclusions:,
         | 
| 32 | 
            +
                        metadata:
         | 
| 30 33 | 
             
                      }
         | 
| 31 34 | 
             
                    end
         | 
| 32 35 |  | 
| 33 36 | 
             
                    protected
         | 
| 34 37 |  | 
| 35 38 | 
             
                    def state
         | 
| 36 | 
            -
                      [ | 
| 39 | 
            +
                      [
         | 
| 40 | 
            +
                        status,
         | 
| 41 | 
            +
                        results,
         | 
| 42 | 
            +
                        applied_exclusions,
         | 
| 43 | 
            +
                        metadata
         | 
| 44 | 
            +
                      ]
         | 
| 37 45 | 
             
                    end
         | 
| 38 46 | 
             
                  end
         | 
| 39 47 | 
             
                end
         | 
| @@ -90,7 +90,7 @@ module Gitlab | |
| 90 90 | 
             
                      subprocess: RUN_IN_SUBPROCESS
         | 
| 91 91 | 
             
                    )
         | 
| 92 92 |  | 
| 93 | 
            -
                      return Core::Response.new(Core::Status::INPUT_ERROR) unless validate_scan_input(payloads)
         | 
| 93 | 
            +
                      return Core::Response.new(status: Core::Status::INPUT_ERROR) unless validate_scan_input(payloads)
         | 
| 94 94 |  | 
| 95 95 | 
             
                      # assign defaults since grpc passing zero timeout value to `Timeout.timeout(..)` makes it effectively useless.
         | 
| 96 96 | 
             
                      timeout = DEFAULT_SCAN_TIMEOUT_SECS unless timeout.positive?
         | 
| @@ -102,24 +102,26 @@ module Gitlab | |
| 102 102 |  | 
| 103 103 | 
             
                        matched_payloads = filter_by_keywords(keyword_matcher, payloads)
         | 
| 104 104 |  | 
| 105 | 
            -
                        next Core::Response.new(Core::Status::NOT_FOUND) if matched_payloads.empty?
         | 
| 105 | 
            +
                        next Core::Response.new(status: Core::Status::NOT_FOUND) if matched_payloads.empty?
         | 
| 106 106 |  | 
| 107 107 | 
             
                        scan_args = {
         | 
| 108 | 
            -
                          payloads: matched_payloads, | 
| 108 | 
            +
                          payloads: matched_payloads,
         | 
| 109 | 
            +
                          payload_timeout:,
         | 
| 109 110 | 
             
                          pattern_matcher: build_pattern_matcher(tags:),
         | 
| 110 | 
            -
                          raw_value_exclusions:, | 
| 111 | 
            +
                          raw_value_exclusions:,
         | 
| 112 | 
            +
                          rule_exclusions:
         | 
| 111 113 | 
             
                        }
         | 
| 112 114 |  | 
| 113 | 
            -
                        secrets = subprocess ? run_scan_within_subprocess(**scan_args) : run_scan(**scan_args)
         | 
| 115 | 
            +
                        secrets, applied_exclusions = subprocess ? run_scan_within_subprocess(**scan_args) : run_scan(**scan_args)
         | 
| 114 116 |  | 
| 115 117 | 
             
                        scan_status = overall_scan_status(secrets)
         | 
| 116 118 |  | 
| 117 | 
            -
                        Core::Response.new(scan_status, secrets)
         | 
| 119 | 
            +
                        Core::Response.new(status: scan_status, results: secrets, applied_exclusions:)
         | 
| 118 120 | 
             
                      end
         | 
| 119 121 | 
             
                    rescue Timeout::Error => e
         | 
| 120 122 | 
             
                      logger.error "Secret detection operation timed out: #{e}"
         | 
| 121 123 |  | 
| 122 | 
            -
                      Core::Response.new(Core::Status::SCAN_TIMEOUT)
         | 
| 124 | 
            +
                      Core::Response.new(status: Core::Status::SCAN_TIMEOUT)
         | 
| 123 125 | 
             
                    end
         | 
| 124 126 |  | 
| 125 127 | 
             
                    private
         | 
| @@ -208,25 +210,42 @@ module Gitlab | |
| 208 210 | 
             
                    # literal values to exclude from the input before the scan, also SD rules to exclude during
         | 
| 209 211 | 
             
                    # the scan when performed on the payloads.
         | 
| 210 212 | 
             
                    def run_scan(
         | 
| 211 | 
            -
                      payloads:, | 
| 212 | 
            -
                       | 
| 213 | 
            +
                      payloads:,
         | 
| 214 | 
            +
                      payload_timeout:,
         | 
| 215 | 
            +
                      pattern_matcher:,
         | 
| 216 | 
            +
                      raw_value_exclusions: [],
         | 
| 217 | 
            +
                      rule_exclusions: []
         | 
| 218 | 
            +
                    )
         | 
| 219 | 
            +
                      all_applied_exclusions = Set.new
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                      all_findings = payloads.flat_map do |payload|
         | 
| 213 222 | 
             
                        Timeout.timeout(payload_timeout) do
         | 
| 214 | 
            -
                          find_secrets_in_payload(
         | 
| 223 | 
            +
                          findings, applied_exclusions = find_secrets_in_payload(
         | 
| 215 224 | 
             
                            payload:,
         | 
| 216 225 | 
             
                            pattern_matcher:,
         | 
| 217 | 
            -
                            raw_value_exclusions:, | 
| 226 | 
            +
                            raw_value_exclusions:,
         | 
| 227 | 
            +
                            rule_exclusions:
         | 
| 218 228 | 
             
                          )
         | 
| 229 | 
            +
                          all_applied_exclusions.merge(applied_exclusions)
         | 
| 230 | 
            +
                          findings
         | 
| 219 231 | 
             
                        end
         | 
| 220 232 | 
             
                      rescue Timeout::Error => e
         | 
| 221 233 | 
             
                        logger.error "Secret Detection scan timed out on the payload(id:#{payload.id}): #{e}"
         | 
| 234 | 
            +
             | 
| 222 235 | 
             
                        Core::Finding.new(payload.id,
         | 
| 223 236 | 
             
                          Core::Status::PAYLOAD_TIMEOUT)
         | 
| 224 237 | 
             
                      end
         | 
| 238 | 
            +
                      [all_findings.freeze, all_applied_exclusions.to_a.freeze]
         | 
| 225 239 | 
             
                    end
         | 
| 226 240 |  | 
| 227 241 | 
             
                    def run_scan_within_subprocess(
         | 
| 228 | 
            -
                      payloads:, | 
| 229 | 
            -
                       | 
| 242 | 
            +
                      payloads:,
         | 
| 243 | 
            +
                      payload_timeout:,
         | 
| 244 | 
            +
                      pattern_matcher:,
         | 
| 245 | 
            +
                      raw_value_exclusions: [],
         | 
| 246 | 
            +
                      rule_exclusions: []
         | 
| 247 | 
            +
                    )
         | 
| 248 | 
            +
                      all_applied_exclusions = Set.new
         | 
| 230 249 | 
             
                      payload_sizes = payloads.map(&:size)
         | 
| 231 250 | 
             
                      grouped_payload_indices = group_by_chunk_size(payload_sizes)
         | 
| 232 251 |  | 
| @@ -239,19 +258,22 @@ module Gitlab | |
| 239 258 | 
             
                      ) do |grouped_payload|
         | 
| 240 259 | 
             
                        grouped_payload.flat_map do |payload|
         | 
| 241 260 | 
             
                          Timeout.timeout(payload_timeout) do
         | 
| 242 | 
            -
                            find_secrets_in_payload(
         | 
| 261 | 
            +
                            findings, applied_exclusions = find_secrets_in_payload(
         | 
| 243 262 | 
             
                              payload:,
         | 
| 244 263 | 
             
                              pattern_matcher:,
         | 
| 245 264 | 
             
                              raw_value_exclusions:, rule_exclusions:
         | 
| 246 265 | 
             
                            )
         | 
| 266 | 
            +
                            all_applied_exclusions.merge(applied_exclusions)
         | 
| 267 | 
            +
                            findings
         | 
| 247 268 | 
             
                          end
         | 
| 248 269 | 
             
                        rescue Timeout::Error => e
         | 
| 249 270 | 
             
                          logger.error "Secret Detection scan timed out on the payload(id:#{payload.id}): #{e}"
         | 
| 271 | 
            +
             | 
| 250 272 | 
             
                          Core::Finding.new(payload.id, Core::Status::PAYLOAD_TIMEOUT)
         | 
| 251 273 | 
             
                        end
         | 
| 252 274 | 
             
                      end
         | 
| 253 275 |  | 
| 254 | 
            -
                      found_secrets.freeze
         | 
| 276 | 
            +
                      [found_secrets.freeze, all_applied_exclusions.to_a.freeze]
         | 
| 255 277 | 
             
                    end
         | 
| 256 278 |  | 
| 257 279 | 
             
                    # Finds secrets in the given payload guarded with a timeout as a circuit breaker. It accepts
         | 
| @@ -259,6 +281,7 @@ module Gitlab | |
| 259 281 | 
             
                    # the scan.
         | 
| 260 282 | 
             
                    def find_secrets_in_payload(payload:, pattern_matcher:, raw_value_exclusions: [], rule_exclusions: [])
         | 
| 261 283 | 
             
                      findings = []
         | 
| 284 | 
            +
                      applied_exclusions = Set.new
         | 
| 262 285 |  | 
| 263 286 | 
             
                      payload_offset = payload.respond_to?(:offset) ? payload.offset : 0
         | 
| 264 287 |  | 
| @@ -268,6 +291,7 @@ module Gitlab | |
| 268 291 | 
             
                        unless raw_value_exclusions.empty?
         | 
| 269 292 | 
             
                          raw_value_exclusions.each do |value|
         | 
| 270 293 | 
             
                            line.gsub!(value, '') # replace input that doesn't contain allowed value in it
         | 
| 294 | 
            +
                            applied_exclusions << value # TODO we need the id of the exclusion
         | 
| 271 295 | 
             
                          end
         | 
| 272 296 | 
             
                        end
         | 
| 273 297 |  | 
| @@ -284,19 +308,30 @@ module Gitlab | |
| 284 308 | 
             
                        matches.each do |match_idx|
         | 
| 285 309 | 
             
                          rule = rules[match_idx]
         | 
| 286 310 |  | 
| 287 | 
            -
                          next if  | 
| 311 | 
            +
                          next if applied_rule_exclusion?(rule[:id], rule_exclusions, applied_exclusions)
         | 
| 288 312 |  | 
| 289 313 | 
             
                          title = rule[:title].nil? ? rule[:description] : rule[:title]
         | 
| 290 | 
            -
             | 
| 291 | 
            -
             | 
| 314 | 
            +
             | 
| 315 | 
            +
                          findings << Core::Finding.new(
         | 
| 316 | 
            +
                            payload.id,
         | 
| 317 | 
            +
                            Core::Status::FOUND,
         | 
| 318 | 
            +
                            line_no,
         | 
| 319 | 
            +
                            rule[:id],
         | 
| 320 | 
            +
                            title
         | 
| 321 | 
            +
                          )
         | 
| 292 322 | 
             
                        end
         | 
| 293 323 | 
             
                      end
         | 
| 294 324 |  | 
| 295 | 
            -
                      findings.freeze
         | 
| 325 | 
            +
                      [findings.freeze, applied_exclusions]
         | 
| 296 326 | 
             
                    rescue StandardError => e
         | 
| 297 327 | 
             
                      logger.error "Secret Detection scan failed on the payload(id:#{payload.id}): #{e}"
         | 
| 298 328 |  | 
| 299 | 
            -
                      Core::Finding.new(payload.id, Core::Status::SCAN_ERROR)
         | 
| 329 | 
            +
                      [[Core::Finding.new(payload.id, Core::Status::SCAN_ERROR)], []]
         | 
| 330 | 
            +
                    end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                    def applied_rule_exclusion?(type, rule_exclusions, applied_exclusions)
         | 
| 333 | 
            +
                      applied_exclusion = rule_exclusions&.find { |rule_exclusion| rule_exclusion == type }
         | 
| 334 | 
            +
                      applied_exclusion && (applied_exclusions << applied_exclusion)
         | 
| 300 335 | 
             
                    end
         | 
| 301 336 |  | 
| 302 337 | 
             
                    # Validates the given payloads by verifying the type and
         | 
| @@ -17,10 +17,11 @@ module Gitlab | |
| 17 17 | 
             
                    # Time to wait for the response from the service
         | 
| 18 18 | 
             
                    REQUEST_TIMEOUT_SECONDS = 10 # 10 seconds
         | 
| 19 19 |  | 
| 20 | 
            -
                    def initialize(host, secure: false, compression: true)
         | 
| 20 | 
            +
                    def initialize(host, secure: false, compression: true, logger: nil)
         | 
| 21 21 | 
             
                      @host = host
         | 
| 22 22 | 
             
                      @secure = secure
         | 
| 23 23 | 
             
                      @compression = compression
         | 
| 24 | 
            +
                      @logger = logger.nil? ? LOGGER : logger
         | 
| 24 25 | 
             
                    end
         | 
| 25 26 |  | 
| 26 27 | 
             
                    # Triggers Secret Detection service's `/Scan` gRPC endpoint. To keep it consistent with SDS gem interface,
         | 
| @@ -116,14 +117,18 @@ module Gitlab | |
| 116 117 | 
             
                    def with_rescued_errors
         | 
| 117 118 | 
             
                      yield
         | 
| 118 119 | 
             
                    rescue ::GRPC::Unauthenticated
         | 
| 119 | 
            -
                      SecretDetection::Core::Response.new(SecretDetection::Core::Status::AUTH_ERROR)
         | 
| 120 | 
            +
                      SecretDetection::Core::Response.new(status: SecretDetection::Core::Status::AUTH_ERROR)
         | 
| 120 121 | 
             
                    rescue ::GRPC::InvalidArgument => e
         | 
| 121 122 | 
             
                      SecretDetection::Core::Response.new(
         | 
| 122 | 
            -
                        SecretDetection::Core::Status::INPUT_ERROR, | 
| 123 | 
            +
                        status: SecretDetection::Core::Status::INPUT_ERROR,
         | 
| 124 | 
            +
                        results: nil,
         | 
| 125 | 
            +
                        metadata: { message: e.details, **e.metadata }
         | 
| 123 126 | 
             
                      )
         | 
| 124 127 | 
             
                    rescue ::GRPC::Unknown, ::GRPC::BadStatus => e
         | 
| 125 128 | 
             
                      SecretDetection::Core::Response.new(
         | 
| 126 | 
            -
                        SecretDetection::Core::Status::SCAN_ERROR, | 
| 129 | 
            +
                        status: SecretDetection::Core::Status::SCAN_ERROR,
         | 
| 130 | 
            +
                        results: nil,
         | 
| 131 | 
            +
                        metadata: { message: e.details }
         | 
| 127 132 | 
             
                      )
         | 
| 128 133 | 
             
                    end
         | 
| 129 134 |  | 
| @@ -131,13 +136,13 @@ module Gitlab | |
| 131 136 | 
             
                      response = grpc_response.to_h
         | 
| 132 137 |  | 
| 133 138 | 
             
                      SecretDetection::Core::Response.new(
         | 
| 134 | 
            -
                        response[:status],
         | 
| 135 | 
            -
                        response[:results],
         | 
| 136 | 
            -
                        response[:metadata]
         | 
| 139 | 
            +
                        status: response[:status],
         | 
| 140 | 
            +
                        results: response[:results],
         | 
| 141 | 
            +
                        metadata: response[:metadata]
         | 
| 137 142 | 
             
                      )
         | 
| 138 143 | 
             
                    rescue StandardError => e
         | 
| 139 | 
            -
                      logger.error("Failed to convert to core response: #{e}")
         | 
| 140 | 
            -
                      SecretDetection::Core::Response.new(SecretDetection::Core::Status::SCAN_ERROR)
         | 
| 144 | 
            +
                      @logger.error("Failed to convert to core response: #{e}")
         | 
| 145 | 
            +
                      SecretDetection::Core::Response.new(status: SecretDetection::Core::Status::SCAN_ERROR)
         | 
| 141 146 | 
             
                    end
         | 
| 142 147 | 
             
                  end
         | 
| 143 148 | 
             
                end
         |