def test_decrypt_raises_error_when_config_missing( set_unset_qradar_app_uuid_env_var, patch_get_store_path): enc = Encryption({"name": "test_name", "user": "******"}) enc_string = enc.encrypt('testing123') assert enc_string != 'testing123' os.remove(DB_STORE) enc = Encryption({"name": "test_name", "user": "******"}) with pytest.raises(ValueError) as ex: enc.decrypt() assert str(ex.value) == "Encryption : no secret to decrypt"
def test_decrypt_handles_enginev3_secrets(v3_uuid_env_var, tmpdir): db_store = 'v3user_e.db' copy_dbstore(db_store, tmpdir.strpath) with patch('qpylib.qpylib.get_store_path') as mock_get_store_path: mock_get_store_path.return_value = os.path.join( tmpdir.strpath, db_store) mykey = Encryption({'name': 'mykey', 'user': '******'}) assert mykey.decrypt() == '12345678' mytoken = Encryption({'name': 'mytoken', 'user': '******'}) assert mytoken.decrypt() == 'abcdefghij'
def test_decrypt_raises_error_when_secret_not_in_config_db( uuid_env_var, tmpdir): db_store = 'missing_secret_e.db' db_store_path = os.path.join(tmpdir.strpath, db_store) with open(db_store_path, 'w') as db_file: db_file.write('{"secret_thing": {"version": 3}}') with patch('qpylib.qpylib.get_store_path') as mock_get_store_path: mock_get_store_path.return_value = db_store_path enc = Encryption({'name': 'secret_thing', 'user': '******'}) with pytest.raises(EncryptionError, match='No secret found for name secret_thing'): enc.decrypt()
def test_decrypt_raises_error_on_decryption_failure(uuid_env_var, tmpdir): db_store = 'badsecret_e.db' copy_dbstore(db_store, tmpdir.strpath) with patch('qpylib.qpylib.get_store_path') as mock_get_store_path: mock_get_store_path.return_value = os.path.join( tmpdir.strpath, db_store) enc = Encryption({'name': 'secret_thing', 'user': '******'}) with pytest.raises( EncryptionError, match= 'Failed to decrypt secret for name secret_thing: InvalidToken' ): enc.decrypt()
def test_decrypt_raise_value_error_on_engine_version_mismatch( set_unset_qradar_app_uuid_env_var, patch_get_store_path): enc = Encryption({"name": "test_name", "user": "******"}) enc_string = enc.encrypt('testing123') assert enc_string != 'testing123' with open(DB_STORE) as db_file: file_json = json.load(db_file) file_json['test_name']['version'] = -1 with open(DB_STORE, 'w') as db_file: json.dump(file_json, db_file) enc = Encryption({"name": "test_name", "user": "******"}) with pytest.raises(ValueError) as ex: enc.decrypt() assert "Encryption : secret engine mismatch." in str(ex.value)
def test_decrypt_raises_error_on_invalid_config_engine_version( uuid_env_var, tmpdir): db_store = 'bad_engine_version_e.db' db_store_path = os.path.join(tmpdir.strpath, db_store) with open(db_store_path, 'w') as db_file: db_file.write( '{"secret_thing": {"version": 1, "secret": "522ae11b6b2b88cb1bb16adea76f7b96"}}' ) with patch('qpylib.qpylib.get_store_path') as mock_get_store_path: mock_get_store_path.return_value = db_store_path enc = Encryption({ 'name': 'secret_thing', 'user': '******' }) with pytest.raises(EncryptionError) as ex: enc.decrypt() assert str( ex.value ) == 'Config for name secret_thing contains invalid engine version 1'
def test_decrypt_v3_secret_reencrypts_with_latest_engine( v3_uuid_env_var, tmpdir): db_store = 'v3user_e.db' copy_dbstore(db_store, tmpdir.strpath) with patch('qpylib.qpylib.get_store_path') as mock_get_store_path: db_store_path = os.path.join(tmpdir.strpath, db_store) mock_get_store_path.return_value = db_store_path mytoken = Encryption({'name': 'mytoken', 'user': '******'}) assert mytoken.config['mytoken']['version'] == 3 assert mytoken.decrypt() == 'abcdefghij' # DB config for 'mytoken' has now been updated with latest engine version details. mytoken2 = Encryption({'name': 'mytoken', 'user': '******'}) assert mytoken2.config['mytoken'][ 'version'] == Encryption.latest_engine_version assert mytoken2.decrypt() == 'abcdefghij' with open(db_store_path) as db_file: store_json = json.load(db_file) assert store_json['mytoken'][ 'version'] == Encryption.latest_engine_version
def create_app(): # Create a Flask instance. qflask = Flask(__name__) csrf = CSRFProtect() csrf.init_app(qflask) # Retrieve QRadar app id. qradar_app_id = qpylib.get_app_id() # Create unique session cookie name for this app. qflask.config['SESSION_COOKIE_NAME'] = 'session_{0}'.format(qradar_app_id) secret_key = "" try: # Read in secret key secret_key_store = Encryption({'name': 'secret_key', 'user': '******'}) secret_key = secret_key_store.decrypt() except EncryptionError: # If secret key file doesn't exist/fail to decrypt it, # generate a new random password for it and encrypt it secret_key = secrets.token_urlsafe(64) secret_key_store = Encryption({'name': 'secret_key', 'user': '******'}) secret_key_store.encrypt(secret_key) qflask.config["SECRET_KEY"] = secret_key # Hide server details in endpoint responses. # pylint: disable=unused-variable @qflask.after_request def obscure_server_header(resp): resp.headers['Server'] = 'QRadar App {0}'.format(qradar_app_id) return resp # Register q_url_for function for use with Jinja2 templates. qflask.add_template_global(qpylib.q_url_for, 'q_url_for') # Initialize logging. qpylib.create_log() # To enable app health checking, the QRadar App Framework # requires every Flask app to define a /debug endpoint. # The endpoint function should contain a trivial implementation # that returns a simple confirmation response message. @qflask.route('/debug') def debug(): return 'Pong!' # Import additional endpoints. # For more information see: # https://flask.palletsprojects.com/en/1.1.x/tutorial/views from . import views qflask.register_blueprint(views.viewsbp) return qflask
def test_decrypt_returns_incorrect_plaintext_with_altered_salt( set_unset_qradar_app_uuid_env_var, patch_get_store_path): enc = Encryption({"name": "test_name", "user": "******"}) enc_string = enc.encrypt('testing123') assert enc_string != 'testing123' with open(DB_STORE) as db_file: file_json = json.load(db_file) file_json['test_name']['salt'] = 'incorrect' with open(DB_STORE, 'w') as db_file: json.dump(file_json, db_file) enc = Encryption({"name": "test_name", "user": "******"}) assert enc.decrypt() != 'testing123'
def decrypt(key): # Set up encdec encryption enc = Encryption({'name': key, 'user': '******'}) try: # Attempt to decrypt the value decrypted = enc.decrypt() return render_template('decrypt.html', key=key, decrypted=decrypted, key_found=True) except EncryptionError: # EncryptionError raised, handle the error with a template describing # that the decryption failed (for example if the key doesn't exist) return render_template('decrypt.html', key=key, key_found=False)
def create_app(): # Create a Flask instance. qflask = Flask(__name__) csrf = CSRFProtect() csrf.init_app(qflask) # Retrieve QRadar app id. qradar_app_id = qpylib.get_app_id() # Create unique session cookie name for this app. qflask.config['SESSION_COOKIE_NAME'] = 'session_{0}'.format(qradar_app_id) secret_key = "" try: # Read in secret key secret_key_store = Encryption({'name': 'secret_key', 'user': '******'}) secret_key = secret_key_store.decrypt() except EncryptionError: # If secret key file doesn't exist/fail to decrypt it, # generate a new random password for it and encrypt it secret_key = secrets.token_urlsafe(64) secret_key_store = Encryption({'name': 'secret_key', 'user': '******'}) secret_key_store.encrypt(secret_key) qflask.config["SECRET_KEY"] = secret_key # Initialize database settings and flask configuration options via json file with open(qpylib.get_root_path( "container/conf/config.json")) as config_json_file: config_json = json.load(config_json_file) qflask.config.update(config_json) # Hide server details in endpoint responses. # pylint: disable=unused-variable @qflask.after_request def obscure_server_header(resp): resp.headers['Server'] = 'QRadar App {0}'.format(qradar_app_id) return resp # Register q_url_for function for use with Jinja2 templates. qflask.add_template_global(qpylib.q_url_for, 'q_url_for') # Initialize logging. qpylib.create_log() # To enable app health checking, the QRadar App Framework # requires every Flask app to define a /debug endpoint. # The endpoint function should contain a trivial implementation # that returns a simple confirmation response message. @qflask.route('/debug') def debug(): return 'Pong!' # Import additional endpoints. # For more information see: # https://flask.palletsprojects.com/en/1.1.x/tutorial/views from . import views qflask.register_blueprint(views.viewsbp) # NOTE: This sample app does not deal with migration of db schema between app versions as its v1.0.0. # If you have multiple versions of your application and the schema changes between them you would # need to add your own migration process at this point so that the schema is updated and loaded. # Also worth versioning your schema changes as well so you can perform the migration. db_host = qflask.config["DB_HOST"] db_port = qflask.config["DB_PORT"] db_user = qflask.config["DB_USER"] db_name = qflask.config["DB_NAME"] # create db if it doesnt exist and load schema if not db_exists(db_host, db_port, db_user, db_name): schema_file_path = qpylib.get_root_path("container/conf/db/schema.sql") create_db(db_host, db_port, db_user, db_name) execute_schema_sql(db_host, db_port, db_user, db_name, schema_file_path) return qflask
def test_encrypt_decrypt_whitespace(uuid_env_var, patch_get_store_path): enc = Encryption({'name': 'secret_thing', 'user': '******'}) assert enc.encrypt(' \n \t ') assert enc.decrypt() == ' \n \t '
def test_encrypt_decrypt_empty_string(uuid_env_var, patch_get_store_path): enc = Encryption({'name': 'secret_thing', 'user': '******'}) enc.encrypt('') assert enc.decrypt() == ''
def test_encrypt_decrypt_null_char(uuid_env_var, patch_get_store_path): enc = Encryption({'name': 'secret_thing', 'user': '******'}) enc.encrypt('\x00') assert enc.decrypt() == '\x00'
def test_decrypt_returns_plaintext_after_encryption( set_unset_qradar_app_uuid_env_var, patch_get_store_path): enc = Encryption({"name": "test_name", "user": "******"}) enc_string = enc.encrypt('testing123') assert enc_string != 'testing123' assert enc.decrypt() == 'testing123'
def test_decrypt_raises_error_when_config_empty(uuid_env_var, patch_get_store_path): enc = Encryption({'name': 'secret_thing', 'user': '******'}) with pytest.raises(EncryptionError, match='No config found for name secret_thing'): enc.decrypt()
def test_decrypt_returns_original_value_after_encryption( uuid_env_var, patch_get_store_path): enc = Encryption({'name': 'secret_thing', 'user': '******'}) enc_string = enc.encrypt('testing123') assert enc_string != 'testing123' assert enc.decrypt() == 'testing123'