-
Notifications
You must be signed in to change notification settings - Fork 20
add support for reporting on scan status #106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,9 @@ | |
| #include "hpsm.h" | ||
| #include <dlfcn.h> | ||
| #include <getopt.h> | ||
| #include <errno.h> | ||
| #include <libgen.h> | ||
| #include <sys/stat.h> | ||
|
|
||
| struct ldb_table oss_url; | ||
| struct ldb_table oss_file; | ||
|
|
@@ -70,6 +73,7 @@ int scan_range_tolerance = SNIPPETS_DEFAULT_RANGE_TOLERANCE; // Maximum number o | |
| bool scan_adjust_tolerance = SNIPPETS_DEFAULT_ADJUST_TOLERANCE; /** Adjust tolerance based on file size */ | ||
| int scan_ranking_threshold = -1; //disable by defaults | ||
| bool scan_honor_file_extension = SNIPPETS_DEFAULT_HONOR_FILE_EXTENSION; | ||
| bool scan_report_progress = false; | ||
|
|
||
| bool lib_encoder_present = false; | ||
| #define LDB_VER_MIN "4.1.0" | ||
|
|
@@ -216,7 +220,7 @@ void recurse_directory(char *name) | |
|
|
||
| if (wfp) | ||
| wfp_scan(path, scan_max_snippets, scan_max_components, scan_adjust_tolerance, | ||
| scan_ranking_threshold, scan_min_match_hits, scan_min_match_lines, scan_range_tolerance, scan_honor_file_extension); | ||
| scan_ranking_threshold, scan_min_match_hits, scan_min_match_lines, scan_range_tolerance, scan_honor_file_extension, scan_report_progress); | ||
| else | ||
| { | ||
| scan_data_t * scan = scan_data_init(path, scan_max_snippets, scan_max_components, scan_adjust_tolerance, | ||
|
|
@@ -248,6 +252,206 @@ bool validate_alpha(char *txt) | |
| return true; | ||
| } | ||
|
|
||
| #define STATUS_DIR "/tmp/engine/batch-scan" | ||
| #define RESULT_DIR "/tmp/engine/batch-result" | ||
| #define MAX_AGE_SECONDS (2 * 60 * 60) /* 2 hours */ | ||
|
|
||
| /** | ||
| * @brief Extracts scan ID from file path (filename without extension) | ||
| * @param path File path | ||
| * @return Allocated string with scan ID, caller must free | ||
| */ | ||
| static char* get_scanid_from_path(const char *path) | ||
| { | ||
| if (!path) return NULL; | ||
|
|
||
| /* Get basename */ | ||
| char *path_copy = strdup(path); | ||
| char *base = basename(path_copy); | ||
|
|
||
| /* Remove extension */ | ||
| char *scanid = strdup(base); | ||
| char *dot = strrchr(scanid, '.'); | ||
| if (dot) *dot = '\0'; | ||
|
|
||
| free(path_copy); | ||
| return scanid; | ||
| } | ||
|
Comment on lines
+264
to
+279
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Path traversal vulnerability: user-supplied scan IDs are used unsanitized in file paths.
Add input validation to reject scan IDs containing 🛡️ Proposed fix: add a validation helper+/**
+ * `@brief` Validates a scan ID contains only safe characters
+ * `@param` scanid Scan ID to validate
+ * `@return` true if valid, false otherwise
+ */
+static bool is_valid_scanid(const char *scanid)
+{
+ if (!scanid || *scanid == '\0') return false;
+ for (const char *p = scanid; *p; p++) {
+ if (!isalnum(*p) && *p != '-' && *p != '_') return false;
+ }
+ return true;
+}Then guard usage: case 262: /* --batch-result */
- if (show_batch_result(optarg) == 0)
+ if (!is_valid_scanid(optarg)) {
+ fprintf(stderr, "Error: Invalid scan ID\n");
+ exit(EXIT_FAILURE);
+ }
+ if (show_batch_result(optarg) == 0)
exit(EXIT_SUCCESS);Apply the same guard for case 263 ( Also applies to: 313-365, 372-395 🧰 Tools🪛 Cppcheck (2.19.0)[warning] 274-274: If memory allocation fails, then there is a possible null pointer dereference (nullPointerOutOfMemory) 🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * @brief Setup output redirection to batch result file | ||
| * @param scanid Scan ID for this operation | ||
| * @return 0 on success, -1 on error | ||
| */ | ||
| static int setup_batch_result_output(const char *scanid) | ||
| { | ||
| if (!scanid) return -1; | ||
|
|
||
| /* Create directories */ | ||
| mkdir("/tmp/engine", 0755); | ||
| mkdir(RESULT_DIR, 0755); | ||
|
|
||
| /* Build result file path */ | ||
| char result_path[MAX_PATH]; | ||
| snprintf(result_path, sizeof(result_path), "%s/%s", RESULT_DIR, scanid); | ||
|
|
||
| /* Redirect stdout to file */ | ||
| if (freopen(result_path, "w", stdout) == NULL) { | ||
| fprintf(stderr, "Error: Could not redirect output to %s: %s\n", result_path, strerror(errno)); | ||
| return -1; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
Comment on lines
+286
to
+305
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
After 🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * @brief Show batch result file content to stdout | ||
| * Verifies scan status before returning results | ||
| * @param scanid Scan ID to retrieve | ||
| * @return 0 on success, -1 on error | ||
| */ | ||
| static int show_batch_result(const char *scanid) | ||
| { | ||
| if (!scanid) { | ||
| fprintf(stderr, "Error: No scan ID provided\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* First check the status file */ | ||
| char status_path[MAX_PATH]; | ||
| snprintf(status_path, sizeof(status_path), "%s/%s", STATUS_DIR, scanid); | ||
|
|
||
| FILE *status_fp = fopen(status_path, "r"); | ||
| if (!status_fp) { | ||
| printf("{\"message\":\"Scan not found\"}\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| char status_buffer[512]; | ||
| bool is_completed = false; | ||
|
|
||
| if (fgets(status_buffer, sizeof(status_buffer), status_fp) != NULL) { | ||
| char *status_ptr = strstr(status_buffer, "\"status\":\""); | ||
| if (status_ptr && strncmp(status_ptr + 10, "completed", 9) == 0) { | ||
| is_completed = true; | ||
| } | ||
| } | ||
| fclose(status_fp); | ||
|
|
||
| /* If not completed, return the status file content directly */ | ||
| if (!is_completed) { | ||
| printf("%s", status_buffer); | ||
| return 0; | ||
| } | ||
|
|
||
| /* Status is completed, return the result */ | ||
| char result_path[MAX_PATH]; | ||
| snprintf(result_path, sizeof(result_path), "%s/%s", RESULT_DIR, scanid); | ||
|
|
||
| FILE *fp = fopen(result_path, "r"); | ||
| if (!fp) { | ||
| printf("{\"message\":\"Result file not found\"}\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| char buffer[4096]; | ||
| size_t bytes; | ||
| while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { | ||
| fwrite(buffer, 1, bytes, stdout); | ||
| } | ||
|
|
||
| fclose(fp); | ||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Show batch status file content to stdout | ||
| * @param scanid Scan ID to retrieve | ||
| * @return 0 on success, -1 on error | ||
| */ | ||
| static int show_batch_status(const char *scanid) | ||
| { | ||
| if (!scanid) { | ||
| fprintf(stderr, "Error: No scan ID provided\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| char status_path[MAX_PATH]; | ||
| snprintf(status_path, sizeof(status_path), "%s/%s", STATUS_DIR, scanid); | ||
|
|
||
| FILE *fp = fopen(status_path, "r"); | ||
| if (!fp) { | ||
| fprintf(stderr, "Error: Status file not found: %s\n", status_path); | ||
| return -1; | ||
| } | ||
|
|
||
| char buffer[512]; | ||
| if (fgets(buffer, sizeof(buffer), fp) != NULL) { | ||
| printf("%s", buffer); | ||
| } | ||
|
|
||
| fclose(fp); | ||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * @brief Clean old completed status files from the batch-scan directory | ||
| * Removes files that have status "completed" and are older than 2 hours | ||
| * @return Number of files cleaned | ||
| */ | ||
| int clean_old_status_files(void) | ||
| { | ||
| DIR *dir = opendir(STATUS_DIR); | ||
| if (!dir) { | ||
| printf("Status directory does not exist: %s\n", STATUS_DIR); | ||
| return 0; | ||
| } | ||
|
|
||
| struct dirent *entry; | ||
| int cleaned = 0; | ||
| time_t now = time(NULL); | ||
| char filepath[MAX_PATH]; | ||
| char buffer[512]; | ||
|
|
||
| while ((entry = readdir(dir)) != NULL) { | ||
| if (entry->d_name[0] == '.') continue; | ||
|
|
||
| snprintf(filepath, sizeof(filepath), "%s/%s", STATUS_DIR, entry->d_name); | ||
|
|
||
| FILE *fp = fopen(filepath, "r"); | ||
| if (!fp) continue; | ||
|
|
||
| if (fgets(buffer, sizeof(buffer), fp) != NULL) { | ||
| /* Parse JSON: {"started":TIMESTAMP,"status":"STATUS","progress":NUM} */ | ||
| char *status_ptr = strstr(buffer, "\"status\":\""); | ||
| char *started_ptr = strstr(buffer, "\"started\":"); | ||
|
|
||
| if (status_ptr && started_ptr) { | ||
| /* Check if status is "completed" */ | ||
| if (strncmp(status_ptr + 10, "completed", 9) == 0) { | ||
| /* Extract timestamp */ | ||
| time_t started = atol(started_ptr + 10); | ||
|
|
||
| /* Check if older than MAX_AGE_SECONDS */ | ||
| if ((now - started) >= MAX_AGE_SECONDS) { | ||
| fclose(fp); | ||
| if (unlink(filepath) == 0) { | ||
| cleaned++; | ||
| printf("Cleaned: %s\n", entry->d_name); | ||
| } | ||
| continue; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| fclose(fp); | ||
| } | ||
|
|
||
| closedir(dir); | ||
| printf("Total files cleaned: %d\n", cleaned); | ||
| return cleaned; | ||
| } | ||
|
Comment on lines
+402
to
+453
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a completed status file is cleaned, the corresponding result file in Proposed addition inside the cleanup block if (unlink(filepath) == 0) {
cleaned++;
printf("Cleaned: %s\n", entry->d_name);
+ /* Also remove the corresponding result file */
+ char result_path[MAX_PATH];
+ snprintf(result_path, sizeof(result_path), "%s/%s", RESULT_DIR, entry->d_name);
+ unlink(result_path);
}🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * @brief Read flags from /etc/scanoss_flags.cfg | ||
| * @return //TODO | ||
|
|
@@ -299,6 +503,10 @@ static struct option long_options[] = { | |
| {"debug", no_argument, 0, 'd'}, | ||
| {"quiet", no_argument, 0, 'q'}, | ||
| {"hpsm", no_argument, 0, 'H'}, | ||
| {"report", no_argument, 0, 'R'}, | ||
| {"clean", no_argument, 0, 'C'}, | ||
| {"batch-result", required_argument, 0, 262}, | ||
| {"batch-status", required_argument, 0, 263}, | ||
| {0, 0, 0, 0} | ||
| }; | ||
|
|
||
|
|
@@ -325,7 +533,7 @@ int main(int argc, char **argv) | |
|
|
||
| bool force_wfp = false; | ||
| bool force_bfp = false; | ||
|
|
||
| microseconds_start = microseconds_now(); | ||
|
|
||
| /* Parse arguments */ | ||
|
|
@@ -334,7 +542,7 @@ int main(int argc, char **argv) | |
| bool invalid_argument = false; | ||
| char * ldb_db_name = NULL; | ||
|
|
||
| while ((option = getopt_long(argc, argv, ":r:T:s:b:c:k:a:F:l:n:M:N:wtLvhdqH", long_options, &option_index)) != -1) | ||
| while ((option = getopt_long(argc, argv, ":r:T:s:b:c:k:a:F:l:n:M:N:wtLvhdqHRC", long_options, &option_index)) != -1) | ||
| { | ||
| /* Check valid alpha is entered */ | ||
| if (optarg) | ||
|
|
@@ -487,6 +695,30 @@ int main(int argc, char **argv) | |
| exit(EXIT_FAILURE); | ||
| } | ||
| break; | ||
|
|
||
| case 'R': | ||
| scan_report_progress = true; | ||
| scanlog("Progress reporting enabled\n"); | ||
| break; | ||
|
|
||
| case 'C': | ||
| clean_old_status_files(); | ||
| exit(EXIT_SUCCESS); | ||
| break; | ||
|
|
||
| case 262: /* --batch-result */ | ||
| if (show_batch_result(optarg) == 0) | ||
| exit(EXIT_SUCCESS); | ||
| else | ||
| exit(EXIT_FAILURE); | ||
| break; | ||
|
|
||
| case 263: /* --batch-status */ | ||
| if (show_batch_status(optarg) == 0) | ||
| exit(EXIT_SUCCESS); | ||
| else | ||
| exit(EXIT_FAILURE); | ||
| break; | ||
| } | ||
| if (invalid_argument) break; | ||
| } | ||
|
|
@@ -533,6 +765,14 @@ int main(int argc, char **argv) | |
| strcpy (target, argv[argc-1]); | ||
| for (int i=strlen(target)-1; i>=0; i--) if (target[i]=='/') target[i]=0; else break; | ||
|
|
||
| /* Redirect output to batch result file if report mode is enabled */ | ||
| char *batch_scanid = NULL; | ||
| if (scan_report_progress) { | ||
| batch_scanid = get_scanid_from_path(target); | ||
| if (batch_scanid) { | ||
| setup_batch_result_output(batch_scanid); | ||
| } | ||
| } | ||
|
Comment on lines
+768
to
+775
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Batch output setup silently skipped if scanid extraction fails. If 🤖 Prompt for AI Agents |
||
|
|
||
| /* Open main report structure */ | ||
| json_open(); | ||
|
|
@@ -560,7 +800,7 @@ int main(int argc, char **argv) | |
| /* Scan wfp file */ | ||
| if (wfp_extension) | ||
| wfp_scan(target, scan_max_snippets, scan_max_components, scan_adjust_tolerance, | ||
| scan_ranking_threshold, scan_min_match_hits, scan_min_match_lines, scan_range_tolerance, scan_honor_file_extension); | ||
| scan_ranking_threshold, scan_min_match_hits, scan_min_match_lines, scan_range_tolerance, scan_honor_file_extension, scan_report_progress); | ||
|
|
||
| else if (bfp_extension) | ||
| binary_scan(target); | ||
|
|
@@ -579,7 +819,8 @@ int main(int argc, char **argv) | |
| /* Close main report structure */ | ||
| json_close(); | ||
|
|
||
| if (target) free (target); | ||
| if (target) free(target); | ||
| if (batch_scanid) free(batch_scanid); | ||
| } | ||
|
|
||
| if (ignore_components) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing NULL check after
strdup(static analysis: nullPointerOutOfMemory).If
strdup(path)on line 269 returns NULL (OOM),basename(path_copy)on line 270 dereferences it. Same forstrdup(base)on line 273—if it fails,strrchr(scanid, '.')on line 274 dereferences NULL.Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 Cppcheck (2.19.0)
[warning] 274-274: If memory allocation fails, then there is a possible null pointer dereference
(nullPointerOutOfMemory)
🤖 Prompt for AI Agents