<commit_before>require 'attr_encrypted'

module HashRedactor
  class HashRedactor
	  def initialize(opts = {})
		@options = default_options.merge opts

		@options[:encode] = @options[:default_encoding] if @options[:encode] == true
		@options[:encode_iv] = @options[:default_encoding] if @options[:encode_iv] == true
	  end
  
  	  def default_options
  	  	{
  	  	  digest_salt: 		 "",
		  encryption_key:	 nil,
		  encode:            true,
		  encode_iv:         true,
		  default_encoding:  'm'
  	  	}
  	  end
  
	  # Removes, digests or encrypts fields in a hash
	  # NOTE: This should NOT be used to protect password fields or similar
	  # The purpose of hashing is to reduce data to a form that can be *quickly*
	  # compared against other records without revealing the original value.
	  # To allow for this, all hashes are created using the *same salt* which is
	  # not secure enough for password protection
	  # For passwords, use BCrypt
	  def redact(data, opts = {})
	    options = @options.merge(opts)
	    
  		redact_hash = options[:redact]
  		
  		raise "Don't know what to redact. Please configure the redact hash when initializing or pass as an argument to redact." unless redact_hash && redact_hash.any?
  
		result = data.clone
  
		redact_hash.each do |hash_key,how|
		  if data.has_key? hash_key
			case how
			  when :remove
				nil
			  when :digest
				result_key = (hash_key.to_s + '_digest').to_sym

				result[result_key] = Digest::SHA256.base64digest(
											result[hash_key].to_s + options[:digest_salt])
			  when :encrypt
				raise "No encryption key specified. Please pass :encryption_key in options to new or redact" unless options[:encryption_key]
			  
				crypt_key = options[:encryption_key]
				iv = SecureRandom.random_bytes(12)
				data_key = ('encrypted_' + hash_key.to_s).to_sym
				iv_key = ('encrypted_' + hash_key.to_s + '_iv').to_sym
			
				encrypted_value = EncryptorInterface.encrypt(:data, result[hash_key], iv: iv, key: crypt_key)
				
				encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
				iv = [iv].pack(options[:encode_iv]) if options[:encode_iv]
			
				result[data_key] = encrypted_value
				result[iv_key] = iv
			  else
				raise "redact called with unknown operation on #{hash_key.to_s}: #{how.to_s}"
			end

			result.delete hash_key
		  end
		end
  
		result
	  end

	  def decrypt(data, opts = {})
	    options = @options.merge opts

  		redact_hash = options[:redact]

  		raise "Don't know what to decrypt. Please configure the redact hash when initializing or pass as an argument to #decrypt." unless redact_hash && redact_hash.any?

		raise "No encryption key specified. Please pass :encryption_key in options to new or decrypt" unless options[:encryption_key]
  
		result = data.clone
  
		redact_hash.each do |hash_key,how|
		  if (how == :encrypt)<commit_msg>Remove .to_s from interpolated variables<commit_after>require 'attr_encrypted'

module HashRedactor
  class HashRedactor
	  def initialize(opts = {})
		@options = default_options.merge opts

		@options[:encode] = @options[:default_encoding] if @options[:encode] == true
		@options[:encode_iv] = @options[:default_encoding] if @options[:encode_iv] == true
	  end
  
  	  def default_options
  	  	{
  	  	  digest_salt: 		 "",
		  encryption_key:	 nil,
		  encode:            true,
		  encode_iv:         true,
		  default_encoding:  'm'
  	  	}
  	  end
  
	  # Removes, digests or encrypts fields in a hash
	  # NOTE: This should NOT be used to protect password fields or similar
	  # The purpose of hashing is to reduce data to a form that can be *quickly*
	  # compared against other records without revealing the original value.
	  # To allow for this, all hashes are created using the *same salt* which is
	  # not secure enough for password protection
	  # For passwords, use BCrypt
	  def redact(data, opts = {})
	    options = @options.merge(opts)
	    
  		redact_hash = options[:redact]
  		
  		raise "Don't know what to redact. Please configure the redact hash when initializing or pass as an argument to redact." unless redact_hash && redact_hash.any?
  
		result = data.clone
  
		redact_hash.each do |hash_key,how|
		  if data.has_key? hash_key
			case how
			  when :remove
				nil
			  when :digest
				result_key = (hash_key.to_s + '_digest').to_sym

				result[result_key] = Digest::SHA256.base64digest(
											result[hash_key].to_s + options[:digest_salt])
			  when :encrypt
				raise "No encryption key specified. Please pass :encryption_key in options to new or redact" unless options[:encryption_key]
			  
				crypt_key = options[:encryption_key]
				iv = SecureRandom.random_bytes(12)
				data_key = ('encrypted_' + hash_key.to_s).to_sym
				iv_key = ('encrypted_' + hash_key.to_s + '_iv').to_sym
			
				encrypted_value = EncryptorInterface.encrypt(:data, result[hash_key], iv: iv, key: crypt_key)
				
				encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
				iv = [iv].pack(options[:encode_iv]) if options[:encode_iv]
			
				result[data_key] = encrypted_value
				result[iv_key] = iv
			  else
				raise "redact called with unknown operation on #{hash_key}: #{how}"
			end

			result.delete hash_key
		  end
		end
  
		result
	  end

	  def decrypt(data, opts = {})
	    options = @options.merge opts

  		redact_hash = options[:redact]

  		raise "Don't know what to decrypt. Please configure the redact hash when initializing or pass as an argument to #decrypt." unless redact_hash && redact_hash.any?

		raise "No encryption key specified. Please pass :encryption_key in options to new or decrypt" unless options[:encryption_key]
  
		result = data.clone
  
		redact_hash.each do |hash_key,how|
		  if (how == :encrypt)
{'fraction_non_alphanumeric': 0.09056144620198157, 'fraction_numerical': 0.00243351294976534, 'mean_word_length': 3.2685459940652817, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2628531', 'n_tokens_mistral': 2106, 'n_tokens_neox': 1815, 'n_words': 551}