@@ -2810,15 +2810,9 @@ int cbm_cmd_update(int argc, char **argv) {
28102810 fclose (out );
28112811 free (bin_data );
28122812 } else {
2813- /* Windows: unzip — validate paths before shell interpolation */
2814- if (!cbm_validate_shell_arg (bin_dir ) || !cbm_validate_shell_arg (tmp_archive )) {
2815- fprintf (stderr , "error: path contains unsafe characters\n" );
2816- cbm_unlink (tmp_archive );
2817- return 1 ;
2818- }
2819- snprintf (cmd , sizeof (cmd ), "unzip -o -d '%s' '%s' 2>/dev/null" , bin_dir , tmp_archive );
2820- // NOLINTNEXTLINE(cert-env33-c) — intentional CLI subprocess for extraction
2821- rc = system (cmd );
2813+ /* Zip extraction: exec unzip directly without shell interpretation */
2814+ const char * unzip_argv [] = {"unzip" , "-o" , "-d" , bin_dir , tmp_archive , NULL };
2815+ rc = cbm_exec_no_shell (unzip_argv );
28222816 cbm_unlink (tmp_archive );
28232817 if (rc != 0 ) {
28242818 fprintf (stderr , "error: extraction failed\n" );
@@ -2839,12 +2833,11 @@ int cbm_cmd_update(int argc, char **argv) {
28392833 int skill_count = cbm_install_skills (skills_dir , true, false);
28402834 printf ("Updated %d skill(s).\n" , skill_count );
28412835
2842- /* Step 7: Verify new version */
2836+ /* Step 7: Verify new version (exec directly, no shell interpretation) */
28432837 printf ("\nUpdate complete. Verifying:\n" );
2844- if (cbm_validate_shell_arg (bin_dest )) {
2845- snprintf (cmd , sizeof (cmd ), "'%s' --version" , bin_dest );
2846- // NOLINTNEXTLINE(cert-env33-c,clang-analyzer-optin.taint.GenericTaint)
2847- (void )system (cmd );
2838+ {
2839+ const char * ver_argv [] = {bin_dest , "--version" , NULL };
2840+ (void )cbm_exec_no_shell (ver_argv );
28482841 }
28492842
28502843 printf ("\nAll project indexes were cleared. They will be rebuilt\n" );
0 commit comments