Skip to content

Commit ce72d83

Browse files
authored
Merge pull request sleuthkit#3408 from singhpratik5/develop
Added tests for hash database management
2 parents 818da9a + 006a9a5 commit ce72d83

File tree

2 files changed

+378
-0
lines changed

2 files changed

+378
-0
lines changed

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ test_catch_runner_SOURCES = \
780780
test/tsk/hashdb/test_hashkeeper.cpp \
781781
test/tsk/hashdb/test_md5sum.cpp \
782782
test/tsk/hashdb/test_sqlite_hdb.cpp \
783+
test/tsk/hashdb/test_tsk_hashdb.cpp \
783784
test/tsk/fs/test_fs_name.cpp \
784785
test/tsk/fs/test_fs_types.cpp \
785786
test/tsk/fs/test_ntfs.cpp \
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
/*
2+
* File Name: test_tsk_hashdb.cpp
3+
* Tests the public API functions for hash database management
4+
* Author: Pratik Singh (@singhpratik5)
5+
*/
6+
7+
#include "tsk/base/tsk_os.h"
8+
#include "tsk/hashdb/tsk_hashdb_i.h"
9+
#include "catch.hpp"
10+
#include <memory>
11+
#include <cstring>
12+
#include <cstdio>
13+
#include <string>
14+
#include <cstdlib>
15+
#include <vector>
16+
17+
#ifdef TSK_WIN32
18+
#include <windows.h>
19+
#endif
20+
21+
// Helper to get temp file path with proper format
22+
static std::string get_temp_path(const char *suffix) {
23+
static int counter = 0;
24+
char buffer[512];
25+
#ifdef TSK_WIN32
26+
snprintf(buffer, sizeof(buffer), ".\\test_tsk_hashdb_%d_%s", counter++, suffix);
27+
#else
28+
snprintf(buffer, sizeof(buffer), "./test_tsk_hashdb_%d_%s", counter++, suffix);
29+
#endif
30+
return std::string(buffer);
31+
}
32+
33+
// Convert string to TSK_TCHAR* - keep wide string alive
34+
#ifdef TSK_WIN32
35+
static std::wstring g_wstr_holder;
36+
static const wchar_t* string_to_tchar(const std::string& str) {
37+
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
38+
g_wstr_holder.resize(size_needed);
39+
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &g_wstr_holder[0], size_needed);
40+
return g_wstr_holder.c_str();
41+
}
42+
#define STR_TO_TCHAR(s) (string_to_tchar(s))
43+
#else
44+
#define STR_TO_TCHAR(s) (s.c_str())
45+
#endif
46+
47+
// Remove file helper
48+
static void remove_test_file(const std::string& path) {
49+
#ifdef TSK_WIN32
50+
_wunlink(string_to_tchar(path));
51+
#else
52+
unlink(path.c_str());
53+
#endif
54+
}
55+
56+
// Create sample NSRL format database for testing
57+
static std::string create_nsrl_test_db() {
58+
std::string path = get_temp_path("nsrl.txt");
59+
FILE *f = fopen(path.c_str(), "w");
60+
if (f) {
61+
fprintf(f, "\"SHA-1\",\"MD5\",\"CRC32\",\"FileName\",\"FileSize\",\"ProductCode\",\"OpSystemCode\",\"SpecialCode\"\n");
62+
fprintf(f, "\"0000000000000000000000000000000000000000\",\"00000000000000000000000000000000\",\"00000000\",\"test1.txt\",\"100\",\"1\",\"1\",\"\"\n");
63+
fprintf(f, "\"1111111111111111111111111111111111111111\",\"11111111111111111111111111111111\",\"11111111\",\"test2.txt\",\"200\",\"2\",\"2\",\"\"\n");
64+
fclose(f);
65+
}
66+
return path;
67+
}
68+
69+
// Create sample md5sum format database for testing
70+
static std::string create_md5sum_test_db() {
71+
std::string path = get_temp_path("md5sum.txt");
72+
FILE *f = fopen(path.c_str(), "w");
73+
if (f) {
74+
fprintf(f, "d41d8cd98f00b204e9800998ecf8427e empty.txt\n");
75+
fprintf(f, "5d41402abc4b2a76b9719d911017c592 hello.txt\n");
76+
fclose(f);
77+
}
78+
return path;
79+
}
80+
81+
// Create sample EnCase format database for testing
82+
static std::string create_encase_test_db() {
83+
std::string path = get_temp_path("encase.hash");
84+
FILE *f = fopen(path.c_str(), "wb");
85+
if (f) {
86+
// Write EnCase binary header
87+
fwrite("HASH\x0d\x0a\xff\x00", 1, 8, f);
88+
// Write padding and minimal structure (1144 bytes of zeros)
89+
std::vector<char> zeros(1144, 0);
90+
fwrite(zeros.data(), 1, zeros.size(), f);
91+
fclose(f);
92+
}
93+
return path;
94+
}
95+
96+
// Create sample HashKeeper format database for testing
97+
static std::string create_hk_test_db() {
98+
std::string path = get_temp_path("hk.txt");
99+
FILE *f = fopen(path.c_str(), "w");
100+
if (f) {
101+
// Write proper HashKeeper header
102+
fprintf(f, "\"file_id\",\"hashset_id\",\"file_name\",\"directory\",\"hash\",\"file_size\",\"date_modified\",\"time_modified\",\"time_zone\",\"comments\",\"date_accessed\",\"time_accessed\"\n");
103+
fprintf(f, "1,1,\"test1.txt\",\"C:\\\\Windows\",\"d41d8cd98f00b204e9800998ecf8427e\",100,\"2023-01-01\",\"12:00:00\",\"UTC\",\"Test file 1\",\"2023-01-01\",\"12:00:00\"\n");
104+
fprintf(f, "2,1,\"test2.txt\",\"C:\\\\Windows\",\"5d41402abc4b2a76b9719d911017c592\",200,\"2023-01-02\",\"13:00:00\",\"UTC\",\"Test file 2\",\"2023-01-02\",\"13:00:00\"\n");
105+
fclose(f);
106+
}
107+
return path;
108+
}
109+
110+
// ========================================================================
111+
// Tests for tsk_hdb_create
112+
// ========================================================================
113+
114+
TEST_CASE("tsk_hdb_create with NULL path", "[tsk_hashdb]") {
115+
uint8_t result = tsk_hdb_create(NULL);
116+
REQUIRE(result == 1);
117+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
118+
}
119+
120+
TEST_CASE("tsk_hdb_create with non-kdb extension", "[tsk_hashdb]") {
121+
std::string path = get_temp_path("test.db");
122+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
123+
uint8_t result = tsk_hdb_create(tpath);
124+
REQUIRE(result == 1);
125+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
126+
remove_test_file(path);
127+
}
128+
129+
TEST_CASE("tsk_hdb_create with valid .kdb extension", "[tsk_hashdb]") {
130+
std::string path = get_temp_path("test.kdb");
131+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
132+
uint8_t result = tsk_hdb_create(tpath);
133+
REQUIRE(result == 0);
134+
remove_test_file(path);
135+
}
136+
137+
// ========================================================================
138+
// Tests for tsk_hdb_open
139+
// ========================================================================
140+
141+
TEST_CASE("tsk_hdb_open with NULL path", "[tsk_hashdb]") {
142+
TSK_HDB_INFO *hdb = tsk_hdb_open(NULL, TSK_HDB_OPEN_NONE);
143+
REQUIRE(hdb == nullptr);
144+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
145+
}
146+
147+
TEST_CASE("tsk_hdb_open with non-existent file", "[tsk_hashdb]") {
148+
std::string path = get_temp_path("nonexistent.txt");
149+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
150+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
151+
REQUIRE(hdb == nullptr);
152+
}
153+
154+
TEST_CASE("tsk_hdb_open NSRL database", "[tsk_hashdb]") {
155+
std::string path = create_nsrl_test_db();
156+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
157+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
158+
REQUIRE(hdb != nullptr);
159+
REQUIRE(hdb->db_type == TSK_HDB_DBTYPE_NSRL_ID);
160+
tsk_hdb_close(hdb);
161+
remove_test_file(path);
162+
}
163+
164+
TEST_CASE("tsk_hdb_open md5sum database", "[tsk_hashdb]") {
165+
std::string path = create_md5sum_test_db();
166+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
167+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
168+
REQUIRE(hdb != nullptr);
169+
REQUIRE(hdb->db_type == TSK_HDB_DBTYPE_MD5SUM_ID);
170+
tsk_hdb_close(hdb);
171+
remove_test_file(path);
172+
}
173+
174+
TEST_CASE("tsk_hdb_open EnCase database", "[tsk_hashdb]") {
175+
std::string path = create_encase_test_db();
176+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
177+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
178+
// EnCase requires proper binary format - test may not work with minimal data
179+
if (hdb) {
180+
CHECK(hdb->db_type == TSK_HDB_DBTYPE_ENCASE_ID);
181+
tsk_hdb_close(hdb);
182+
}
183+
remove_test_file(path);
184+
}
185+
186+
TEST_CASE("tsk_hdb_open HashKeeper database", "[tsk_hashdb]") {
187+
std::string path = create_hk_test_db();
188+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
189+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
190+
REQUIRE(hdb != nullptr);
191+
CHECK(hdb->db_type == TSK_HDB_DBTYPE_HK_ID);
192+
tsk_hdb_close(hdb);
193+
remove_test_file(path);
194+
}
195+
196+
TEST_CASE("tsk_hdb_open SQLite database", "[tsk_hashdb]") {
197+
std::string path = get_temp_path("test.kdb");
198+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
199+
tsk_hdb_create(tpath);
200+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
201+
REQUIRE(hdb != nullptr);
202+
REQUIRE(hdb->db_type == TSK_HDB_DBTYPE_SQLITE_ID);
203+
tsk_hdb_close(hdb);
204+
remove_test_file(path);
205+
}
206+
/*
207+
TEST_CASE("tsk_hdb_open with index file path md5", "[tsk_hashdb]") {
208+
std::string db_path = create_md5sum_test_db();
209+
std::string idx_path = db_path + "-md5.idx";
210+
// Create a proper binary index file header
211+
FILE *idx_f = fopen(idx_path.c_str(), "wb");
212+
if (idx_f) {
213+
// Write index header markers
214+
fprintf(idx_f, "%s\n", TSK_HDB_IDX_HEAD_TYPE_STR);
215+
fprintf(idx_f, "md5\n");
216+
fprintf(idx_f, "%s\n", TSK_HDB_IDX_HEAD_NAME_STR);
217+
fprintf(idx_f, "%s\n", db_path.c_str());
218+
// Write a sample index entry (hash,offset)
219+
fprintf(idx_f, "00000000000000000000000000000000,0000000000000000\n");
220+
fclose(idx_f);
221+
}
222+
// Remove the database file so that IDXONLY is triggered
223+
remove_test_file(db_path);
224+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(idx_path));
225+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
226+
REQUIRE(hdb != nullptr);
227+
CHECK(hdb->db_type == TSK_HDB_DBTYPE_IDXONLY_ID);
228+
tsk_hdb_close(hdb);
229+
remove_test_file(idx_path);
230+
}
231+
232+
TEST_CASE("tsk_hdb_open with index file path sha1", "[tsk_hashdb]") {
233+
std::string db_path = create_nsrl_test_db();
234+
std::string idx_path = db_path + "-sha1.idx";
235+
// Create a proper binary index file header
236+
FILE *idx_f = fopen(idx_path.c_str(), "wb");
237+
if (idx_f) {
238+
// Write index header markers
239+
fprintf(idx_f, "%s\n", TSK_HDB_IDX_HEAD_TYPE_STR);
240+
fprintf(idx_f, "sha1\n");
241+
fprintf(idx_f, "%s\n", TSK_HDB_IDX_HEAD_NAME_STR);
242+
fprintf(idx_f, "%s\n", db_path.c_str());
243+
// Write a sample index entry (hash,offset)
244+
fprintf(idx_f, "0000000000000000000000000000000000000000,0000000000000000\n");
245+
fclose(idx_f);
246+
}
247+
// Remove the database file so that IDXONLY is triggered
248+
remove_test_file(db_path);
249+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(idx_path));
250+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
251+
REQUIRE(hdb != nullptr);
252+
CHECK(hdb->db_type == TSK_HDB_DBTYPE_IDXONLY_ID);
253+
tsk_hdb_close(hdb);
254+
remove_test_file(idx_path);
255+
}
256+
257+
TEST_CASE("tsk_hdb_open with IDXONLY flag", "[tsk_hashdb]") {
258+
std::string path = get_temp_path("dummy.txt");
259+
FILE *idx_f = fopen(path.c_str(), "w");
260+
if (idx_f) {
261+
fprintf(idx_f, "00000000000000000000000000000000,0\n");
262+
fclose(idx_f);
263+
}
264+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
265+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_IDXONLY);
266+
REQUIRE(hdb != nullptr);
267+
REQUIRE(hdb->db_type == TSK_HDB_DBTYPE_IDXONLY_ID);
268+
tsk_hdb_close(hdb);
269+
remove_test_file(path);
270+
}
271+
*/
272+
// ========================================================================
273+
// Tests for tsk_hdb_get_db_path
274+
// ========================================================================
275+
276+
TEST_CASE("tsk_hdb_get_db_path with NULL hdb_info", "[tsk_hashdb]") {
277+
const TSK_TCHAR *result = tsk_hdb_get_db_path(NULL);
278+
REQUIRE(result == nullptr);
279+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
280+
}
281+
282+
TEST_CASE("tsk_hdb_get_db_path with valid hdb_info", "[tsk_hashdb]") {
283+
std::string path = create_md5sum_test_db();
284+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
285+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
286+
REQUIRE(hdb != nullptr);
287+
const TSK_TCHAR *result = tsk_hdb_get_db_path(hdb);
288+
REQUIRE(result != nullptr);
289+
tsk_hdb_close(hdb);
290+
remove_test_file(path);
291+
}
292+
293+
// ========================================================================
294+
// Tests for tsk_hdb_get_display_name
295+
// ========================================================================
296+
297+
TEST_CASE("tsk_hdb_get_display_name with NULL hdb_info", "[tsk_hashdb]") {
298+
const char *result = tsk_hdb_get_display_name(NULL);
299+
REQUIRE(result == nullptr);
300+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
301+
}
302+
303+
TEST_CASE("tsk_hdb_get_display_name with valid hdb_info", "[tsk_hashdb]") {
304+
std::string path = create_md5sum_test_db();
305+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
306+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
307+
REQUIRE(hdb != nullptr);
308+
const char *result = tsk_hdb_get_display_name(hdb);
309+
REQUIRE(result != nullptr);
310+
REQUIRE(strlen(result) > 0);
311+
tsk_hdb_close(hdb);
312+
remove_test_file(path);
313+
}
314+
315+
// ========================================================================
316+
// Tests for tsk_hdb_uses_external_indexes
317+
// ========================================================================
318+
319+
TEST_CASE("tsk_hdb_uses_external_indexes with NULL hdb_info", "[tsk_hashdb]") {
320+
uint8_t result = tsk_hdb_uses_external_indexes(NULL);
321+
REQUIRE(result == 0);
322+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
323+
}
324+
325+
TEST_CASE("tsk_hdb_uses_external_indexes with text db", "[tsk_hashdb]") {
326+
std::string path = create_md5sum_test_db();
327+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
328+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
329+
REQUIRE(hdb != nullptr);
330+
uint8_t result = tsk_hdb_uses_external_indexes(hdb);
331+
REQUIRE(result == 1);
332+
tsk_hdb_close(hdb);
333+
remove_test_file(path);
334+
}
335+
336+
TEST_CASE("tsk_hdb_uses_external_indexes with SQLite db", "[tsk_hashdb]") {
337+
std::string path = get_temp_path("test.kdb");
338+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
339+
tsk_hdb_create(tpath);
340+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
341+
REQUIRE(hdb != nullptr);
342+
uint8_t result = tsk_hdb_uses_external_indexes(hdb);
343+
REQUIRE(result == 0);
344+
tsk_hdb_close(hdb);
345+
remove_test_file(path);
346+
}
347+
348+
// ========================================================================
349+
// Tests for tsk_hdb_get_idx_path
350+
// ========================================================================
351+
352+
TEST_CASE("tsk_hdb_get_idx_path with NULL hdb_info", "[tsk_hashdb]") {
353+
const TSK_TCHAR *result = tsk_hdb_get_idx_path(NULL, TSK_HDB_HTYPE_MD5_ID);
354+
REQUIRE(result == nullptr);
355+
REQUIRE(tsk_error_get_errno() == TSK_ERR_HDB_ARG);
356+
}
357+
/*
358+
TEST_CASE("tsk_hdb_get_idx_path with valid hdb_info", "[tsk_hashdb]") {
359+
std::string path = create_md5sum_test_db();
360+
TSK_TCHAR *tpath = const_cast<TSK_TCHAR*>(STR_TO_TCHAR(path));
361+
TSK_HDB_INFO *hdb = tsk_hdb_open(tpath, TSK_HDB_OPEN_NONE);
362+
REQUIRE(hdb != nullptr);
363+
// Create the index first so get_idx_path can return a valid path
364+
TSK_TCHAR idx_type[] = _TSK_T("md5");
365+
uint8_t make_result = tsk_hdb_make_index(hdb, idx_type);
366+
CHECK(make_result == 0);
367+
if (make_result == 0) {
368+
const TSK_TCHAR *result = tsk_hdb_get_idx_path(hdb, TSK_HDB_HTYPE_MD5_ID);
369+
CHECK(result != nullptr);
370+
// Cleanup index file
371+
std::string idx_path = path + "-md5.idx";
372+
remove_test_file(idx_path);
373+
}
374+
tsk_hdb_close(hdb);
375+
remove_test_file(path);
376+
}
377+
*/

0 commit comments

Comments
 (0)