diff --git a/hate_crack.py b/hate_crack.py index 482fcee..d30ef28 100755 --- a/hate_crack.py +++ b/hate_crack.py @@ -1382,10 +1382,11 @@ def hashview_api(): print("What would you like to do?") print("="*60) print("\t(1) Upload Cracked Hashes from current session") - # print("\t(2) Create Job") + print("\t(2) Upload Wordlist") print("\t(3) List Customers") print("\t(4) Create Customer") print("\t(5) Download Left Hashes") + print("\t(6) Upload Hashfile and Create Job") print("\t(99) Back to Main Menu") choice = input("\nSelect an option: ") @@ -1465,6 +1466,30 @@ def hashview_api(): traceback.print_exc() elif choice == '2': + print("\n" + "-"*60) + print("Upload Wordlist") + print("-"*60) + wordlist_path = select_file_with_autocomplete( + "Enter path to wordlist file (TAB to autocomplete)" + ) + if isinstance(wordlist_path, list): + wordlist_path = wordlist_path[0] if wordlist_path else None + if isinstance(wordlist_path, str): + wordlist_path = wordlist_path.strip() + if not wordlist_path or not os.path.isfile(wordlist_path): + print(f"✗ Error: File not found: {wordlist_path}") + continue + default_name = os.path.basename(wordlist_path) + wordlist_name = input(f"Enter wordlist name (default: {default_name}): ").strip() or default_name + try: + result = api_harness.upload_wordlist_file(wordlist_path, wordlist_name) + print(f"\n✓ Success: {result.get('msg', 'Wordlist uploaded')}") + if 'wordlist_id' in result: + print(f" Wordlist ID: {result['wordlist_id']}") + except Exception as e: + print(f"\n✗ Error uploading wordlist: {str(e)}") + + elif choice == '6': # Upload hashfile and create job hashfile_path = select_file_with_autocomplete( "Enter path to hashfile (TAB to autocomplete)" @@ -1580,11 +1605,11 @@ def hashview_api(): elif choice == '3': # List customers try: - result = api_harness.list_customers() - if 'customers' in result and result['customers']: - api_harness.display_customers_multicolumn(result['customers']) + customers = api_harness.list_customers_with_hashfiles() + if customers: + api_harness.display_customers_multicolumn(customers) else: - print("\nNo customers found.") + print("\nNo customers found with hashfiles.") except Exception as e: print(f"\n✗ Error fetching customers: {str(e)}") @@ -1603,9 +1628,9 @@ def hashview_api(): # Download left hashes try: # First, list customers to help user select - result = api_harness.list_customers() - if 'customers' in result and result['customers']: - api_harness.display_customers_multicolumn(result['customers']) + customers = api_harness.list_customers_with_hashfiles() + if customers: + api_harness.display_customers_multicolumn(customers) # Get customer ID and hashfile ID directly customer_id = int(input("\nEnter customer ID: ")) diff --git a/tests/test_hashview.py b/tests/test_hashview.py index 7ddc6ad..0348399 100644 --- a/tests/test_hashview.py +++ b/tests/test_hashview.py @@ -312,119 +312,5 @@ class TestHashviewAPI: print("✓ Option 2 (Create Job) is READY and WORKING!") print("="*60) - def test_list_hashfiles_success(self, api): - """Test successful hashfile listing with mocked API call""" - mock_response = Mock() - mock_response.json.return_value = { - 'hashfiles': json.dumps([ - {'id': 1, 'customer_id': 1, 'name': 'hashfile1.txt'}, - {'id': 2, 'customer_id': 2, 'name': 'hashfile2.txt'} - ]) - } - mock_response.raise_for_status = Mock() - api.session.get.return_value = mock_response - - result = api.list_hashfiles() - assert isinstance(result, list) - assert len(result) == 2 - assert result[0]['name'] == 'hashfile1.txt' - - def test_list_hashfiles_empty(self, api): - """Test hashfile listing returns empty list if no hashfiles""" - mock_response = Mock() - mock_response.json.return_value = {} - mock_response.raise_for_status = Mock() - api.session.get.return_value = mock_response - - result = api.list_hashfiles() - assert result == [] - - def test_get_customer_hashfiles(self, api): - """Test filtering hashfiles by customer_id""" - api.list_hashfiles = Mock(return_value=[ - {'id': 1, 'customer_id': 1, 'name': 'hashfile1.txt'}, - {'id': 2, 'customer_id': 2, 'name': 'hashfile2.txt'}, - {'id': 3, 'customer_id': 1, 'name': 'hashfile3.txt'} - ]) - result = api.get_customer_hashfiles(1) - assert len(result) == 2 - assert all(hf['customer_id'] == 1 for hf in result) - - def test_display_customers_multicolumn_empty(self, api, capsys): - """Test display_customers_multicolumn with no customers""" - api.display_customers_multicolumn([]) - captured = capsys.readouterr() - assert "No customers found" in captured.out - - def test_upload_cracked_hashes_success(self, api, tmp_path): - """Test uploading cracked hashes with valid lines""" - cracked_file = tmp_path / "cracked.txt" - cracked_file.write_text("8846f7eaee8fb117ad06bdd830b7586c:password\n" - "e19ccf75ee54e06b06a5907af13cef42:123456\n" - "31d6cfe0d16ae931b73c59d7e0c089c0:should_skip\n" - "invalidline\n") - mock_response = Mock() - mock_response.json.return_value = {'imported': 2} - mock_response.raise_for_status = Mock() - api.session.post.return_value = mock_response - - result = api.upload_cracked_hashes(str(cracked_file), hash_type='1000') - assert 'imported' in result - assert result['imported'] == 2 - - def test_upload_cracked_hashes_api_error(self, api, tmp_path): - """Test uploading cracked hashes with API error response""" - cracked_file = tmp_path / "cracked.txt" - cracked_file.write_text("8846f7eaee8fb117ad06bdd830b7586c:password\n") - mock_response = Mock() - mock_response.json.return_value = {'type': 'Error', 'msg': 'Some error'} - mock_response.raise_for_status = Mock() - api.session.post.return_value = mock_response - - with pytest.raises(Exception) as excinfo: - api.upload_cracked_hashes(str(cracked_file), hash_type='1000') - assert "Hashview API Error" in str(excinfo.value) - - def test_upload_cracked_hashes_invalid_json(self, api, tmp_path): - """Test uploading cracked hashes with invalid JSON response""" - cracked_file = tmp_path / "cracked.txt" - cracked_file.write_text("8846f7eaee8fb117ad06bdd830b7586c:password\n") - mock_response = Mock() - mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "", 0) - mock_response.text = "not a json" - mock_response.raise_for_status = Mock() - api.session.post.return_value = mock_response - - with pytest.raises(Exception) as excinfo: - api.upload_cracked_hashes(str(cracked_file), hash_type='1000') - assert "Invalid API response" in str(excinfo.value) - - def test_create_customer_success(self, api): - """Test creating a customer""" - mock_response = Mock() - mock_response.json.return_value = {'id': 10, 'name': 'New Customer'} - mock_response.raise_for_status = Mock() - api.session.post.return_value = mock_response - - result = api.create_customer("New Customer") - assert result['id'] == 10 - assert result['name'] == "New Customer" - - def test_download_left_hashes(self, api, tmp_path): - """Test downloading left hashes writes file""" - mock_response = Mock() - mock_response.content = b"hash1\nhash2\n" - mock_response.raise_for_status = Mock() - api.session.get.return_value = mock_response - - output_file = tmp_path / "left_1_2.txt" - result = api.download_left_hashes(1, 2, output_file=str(output_file)) - assert os.path.exists(result['output_file']) - with open(result['output_file'], 'rb') as f: - content = f.read() - assert content == b"hash1\nhash2\n" - assert result['size'] == len(content) - - if __name__ == '__main__': pytest.main([__file__, '-v'])