@@ -1750,13 +1750,15 @@ _apply_patches() {
17501750
17511751 # Process local patches - never overwrites/updates files, only user does this
17521752 _process_local_patches () {
1753- # Always start fresh with patch file
1754- true > " ${patch_file} "
17551753 local has_content=false
1754+ local temp_patch=" ${patch_file} .tmp"
1755+
1756+ # Start fresh with temp file
1757+ true > " ${temp_patch} "
17561758
17571759 # Step 1: Check if main patch file exists (highest priority for base)
17581760 if [[ -f " ${patch_dir} /patch" && -s " ${patch_dir} /patch" ]]; then
1759- cat " ${patch_dir} /patch" > " ${patch_file } "
1761+ cat " ${patch_dir} /patch" > " ${temp_patch } "
17601762 has_content=true
17611763 fi
17621764
@@ -1767,11 +1769,11 @@ _apply_patches() {
17671769
17681770 if _curl " ${patch_url} " -o " ${tmp_patch} " ; then
17691771 if [[ ${has_content} == true ]]; then
1770- printf ' \n\n# Merged from URL: %s\n' " ${patch_url} " >> " ${patch_file } "
1772+ printf ' \n\n# Merged from URL: %s\n' " ${patch_url} " >> " ${temp_patch } "
17711773 else
1772- printf ' # Downloaded from URL: %s\n' " ${patch_url} " > " ${patch_file } "
1774+ printf ' # Downloaded from URL: %s\n' " ${patch_url} " > " ${temp_patch } "
17731775 fi
1774- cat " ${tmp_patch} " >> " ${patch_file } "
1776+ cat " ${tmp_patch} " >> " ${temp_patch } "
17751777 rm -f " ${tmp_patch} "
17761778 has_content=true
17771779 else
@@ -1789,17 +1791,23 @@ _apply_patches() {
17891791 if [[ ${# additional_patches[@]} -gt 0 ]]; then
17901792 for patch_src in " ${additional_patches[@]} " ; do
17911793 if [[ ${has_content} == true ]]; then
1792- printf ' \n\n# Merged from: %s\n' " ${patch_src##*/ } " >> " ${patch_file } "
1794+ printf ' \n\n# Merged from: %s\n' " ${patch_src##*/ } " >> " ${temp_patch } "
17931795 else
1794- printf ' # From: %s\n' " ${patch_src##*/ } " > " ${patch_file } "
1796+ printf ' # From: %s\n' " ${patch_src##*/ } " > " ${temp_patch } "
17951797 has_content=true
17961798 fi
1797- cat " ${patch_src} " >> " ${patch_file } "
1799+ cat " ${patch_src} " >> " ${temp_patch } "
17981800 done
17991801 fi
18001802
1801- # Final validation
1802- [[ ${has_content} == true && -s ${patch_file} ]]
1803+ # Final validation and atomic move
1804+ if [[ ${has_content} == true && -s ${temp_patch} ]]; then
1805+ mv " ${temp_patch} " " ${patch_file} "
1806+ return 0
1807+ else
1808+ rm -f " ${temp_patch} "
1809+ return 1
1810+ fi
18031811 }
18041812
18051813 # Download remote patches function
@@ -1812,6 +1820,12 @@ _apply_patches() {
18121820
18131821 local qbt_patches_url_branch
18141822 qbt_patches_url_branch=" $( _git_git ls-remote -q --symref " https://github.com/${qbt_patches_url} " HEAD | awk ' /^ref:/{sub("refs/heads/", "", $2); print $2}' ) "
1823+
1824+ # Validate branch name for security (allow alphanumeric, dash, underscore, dot)
1825+ if [[ ! ${qbt_patches_url_branch} =~ ^[a-zA-Z0-9._-]+$ ]]; then
1826+ printf ' %b\n' " ${unicode_red_circle} Invalid branch name detected: ${qbt_patches_url_branch} "
1827+ return 1
1828+ fi
18151829 local remote_base=" https://raw.githubusercontent.com/${qbt_patches_url} /${qbt_patches_url_branch} /patches/${app_name} /${app_version[${app_name}]} "
18161830 local api_url=" https://api.github.com/repos/${qbt_patches_url} /contents/patches/${app_name} /${app_version[${app_name}]} "
18171831 local downloaded=false
@@ -1821,7 +1835,7 @@ _apply_patches() {
18211835 local dir_api_url=" ${1} "
18221836 local local_path=" ${2} "
18231837 local temp_json
1824- temp_json=" ${patch_dir} /temp_listing_$( basename " ${local_path} " ) .json"
1838+ temp_json=" ${patch_dir} /temp_listing_$$ _ $ ( basename " ${local_path} " ) .json"
18251839
18261840 if _curl " ${dir_api_url} " -o " ${temp_json} " 2> /dev/null; then
18271841 # Parse JSON to extract entries
@@ -1836,6 +1850,13 @@ _apply_patches() {
18361850 mapfile -t types < <( printf ' %s\n' " ${type_matches} " | sed ' s/.*"type"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' )
18371851 mapfile -t urls < <( printf ' %s\n' " ${url_matches} " | sed ' s/.*"download_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' )
18381852
1853+ # Validate array lengths match before processing
1854+ if [[ ${# names[@]} -ne ${# types[@]} ]] || [[ ${# names[@]} -ne ${# urls[@]} ]]; then
1855+ printf ' %b\n' " ${unicode_yellow_circle} Array length mismatch in JSON parsing, skipping"
1856+ rm -f " ${temp_json} "
1857+ return 1
1858+ fi
1859+
18391860 # Process each entry
18401861 for i in " ${! names[@]} " ; do
18411862 local name=" ${names[i]} " type=" ${types[i]} " url=" ${urls[i]} "
@@ -1889,18 +1910,27 @@ _apply_patches() {
18891910 else
18901911 method=" patch"
18911912 fi
1892- printf ' %b\n\n' " ${unicode_red_circle } Applying: ${color_cyan_light}${1##*/ }${color_end} using ${color_yellow_light}${method}${color_end} "
1913+ printf ' \n %b\n\n' " ${unicode_green_circle } Applying: ${color_cyan_light}${1##*/ }${color_end} using ${color_yellow_light}${method}${color_end} "
18931914 if [[ ${method} == " git" ]] && git -C " ${qbt_dl_folder_path} " apply --check " ${1} " & > /dev/null; then
1894- git -C " ${qbt_dl_folder_path} " apply " ${1} "
1915+ if git -C " ${qbt_dl_folder_path} " apply " ${1} " ; then
1916+ printf ' \n%b\n\n' " ${unicode_green_circle} Patch applied successfully using git"
1917+ else
1918+ printf ' \n%b\n\n' " ${unicode_red_circle} Failed to apply patch using git"
1919+ return 1
1920+ fi
18951921 else
18961922 _pushd " ${qbt_dl_folder_path} "
1897- patch -p1 < " ${1} "
1923+ if patch -p1 < " ${1} " ; then
1924+ printf ' \n%b\n\n' " ${unicode_green_circle} Patch applied successfully using patch"
1925+ else
1926+ printf ' \n%b\n\n' " ${unicode_red_circle} Failed to apply patch using patch"
1927+ _popd
1928+ return 1
1929+ fi
18981930 _popd
18991931 fi
19001932 }
19011933
1902- [[ ${source_default[${app_name}]} == " folder" && ! -d " ${qbt_cache_dir} /${app_name} " ]] && printf ' \n'
1903-
19041934 # Method 1: Source directory method (highest priority)
19051935 if [[ -d " ${patch_dir} /source" && -n " $( ls -A " ${patch_dir} /source" 2> /dev/null) " ]]; then
19061936 printf ' %b\n' " ${unicode_green_circle} Using source directory method"
@@ -1938,7 +1968,7 @@ _apply_patches() {
19381968 elif [[ -f " ${patch_dir} /Jamfile" ]]; then
19391969 cp -f " ${patch_dir} /Jamfile" " ${jamfile_dest} "
19401970 else
1941- local remote_jamfile=" https://raw.githubusercontent.com/${qbt_patches_url} /${qbt_patches_url_branch:- main } /patches/${app_name} /${app_version[${app_name}]} /Jamfile"
1971+ local remote_jamfile=" https://raw.githubusercontent.com/${qbt_patches_url} /${qbt_patches_url_branch} /patches/${app_name} /${app_version[${app_name}]} /Jamfile"
19421972 _curl " ${remote_jamfile} " -o " ${jamfile_dest} " 2> /dev/null
19431973 fi
19441974 fi
@@ -2075,7 +2105,7 @@ _download_folder() {
20752105 # Force clean clone if patches are detected
20762106 if [[ -f ${patch_file} && -s ${patch_file} ]] || [[ -f " ${patch_dir} /url" ]] || [[ -d " ${patch_dir} /source" && -n " $( ls -A " ${patch_dir} /source" 2> /dev/null) " ]]; then
20772107 needs_clean_clone=true
2078- [[ -d ${qbt_dl_folder_path} ]] && printf ' %b\n' " ${unicode_yellow_circle} Forcing clean clone due to patches"
2108+ [[ -d ${qbt_dl_folder_path} ]] && printf ' \n %b\n' " ${unicode_yellow_circle} Forcing clean clone due to patches"
20792109 fi
20802110
20812111 # Remove the source files in the build directory if present before we download or copy them again
@@ -2261,7 +2291,7 @@ _download_file() {
22612291 rm -rf " ${qbt_install_dir:? } /${archive_dir_name} " " ${qbt_install_dir} /${app_name} .tar.xz"
22622292 elif [[ ${needs_clean_extract} == true ]]; then
22632293 # Clean up without archive validation when forcing clean extract
2264- [[ ${needs_clean_extract} == true ]] && printf ' %b\n' " ${unicode_yellow_circle} Forcing clean extraction due to patches"
2294+ [[ ${needs_clean_extract} == true ]] && printf ' \n %b\n' " ${unicode_yellow_circle} Forcing clean extraction due to patches"
22652295 elif [[ -f ${qbt_dl_file_path} ]]; then
22662296 _error_tag " ${app_name} " " Failed to safely extract archive directory name from ${qbt_dl_file_path} "
22672297 fi
0 commit comments