1 पॉइंट द्वारा GN⁺ 6 시간 전 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • Git का -- सामान्य option terminator नहीं है, बल्कि revision और pathspec को अलग करता है; इसलिए untrusted revision को सुरक्षित रूप से पास करने के लिए Git 2.24.0 से समर्थित --end-of-options ज़रूरी है
  • git log --end-of-options "$rev" -- "$path" में पहला marker options और revision को, जबकि बाद वाला -- revision और path को अलग करता है; दोनों एक-दूसरे की जगह इस्तेमाल नहीं किए जा सकते
  • shell के बिना सीधे argv array चलाने पर भी अगर dash से शुरू होने वाला input --upload-pack, core.sshCommand, ProxyCommand जैसे options के रूप में समझ लिया जाए, तो CWE-88 argument injection हो सकता है
  • जांचे गए 19 package managers में 17 Git binary को default या एकमात्र तरीके से चलाते थे, लेकिन --end-of-options इस्तेमाल करने वाला tool सिर्फ Go का cmd/go था
  • मूल समाधान के लिए subcommand के अनुसार Git का minimum version 2.24.0·2.30.0·2.43.1 तक बढ़ाने की compatibility cost है, जबकि Git libraries argument injection boundary हटाती हैं, लेकिन upstream के checkout safety fixes को सीधे track करना पड़ता है

-- और --end-of-options में फर्क

  • सामान्य Unix tools में -- option parsing के अंत को दिखाता है, इसलिए rm -- -f में -f को force delete option नहीं बल्कि filename माना जाता है
  • Git शुरू से -- को revision और pathspec के separator के रूप में इस्तेमाल करता है
    • git log foo में यह अस्पष्ट है कि foo branch है या file
    • git log main -- README.md का मतलब main के उन commits से है जिन्होंने README.md को बदला
  • इस design की वजह से revision position के लिए option termination marker नहीं था, और git log "$rev" में अगर $rev dash से शुरू हो, तो Git उसे option मान लेता है
  • --end-of-options लाने वाले commit में बताया गया कि मौजूदा -- पहले से revision और pathspec को अलग कर रहा था, इसलिए options और revision को अलग करने के लिए अलग marker चाहिए था
  • --end-of-options को gitcli(7) में document किया गया है, और यह Git 2.24.0 के नवंबर 2019 release में जोड़ा गया

कमांड के हिसाब से सही उपयोग

  • git clone -- "$url" में clone POSIX convention का पालन करता है, इसलिए URL से पहले का -- option parsing रोक देता है
  • git checkout "$ref" -- में बाद वाला -- $ref को filename नहीं बल्कि revision बताता है, लेकिन यह नहीं रोकता कि $ref पहले option की तरह parse हो जाए
  • untrusted revision और path को साथ में सुरक्षित रूप से पास करने के लिए git log --end-of-options "$rev" -- "$path" की तरह दोनों markers इस्तेमाल करने चाहिए
    • --end-of-options options और revision को अलग करता है
    • -- revision और path को अलग करता है
  • अगर दोनों markers को एक-दूसरे का विकल्प मान लिया जाए, तो dash से शुरू होने वाले input को नहीं रोका जा सकता

हर subcommand में support का अलग समय

  • --end-of-options support सभी Git commands में एक साथ नहीं आया, बल्कि हर subcommand में अलग-अलग जोड़ा गया
  • git rev-parse अपना अलग argument parser इस्तेमाल करता है, इसलिए शुरुआती introduction के एक साल बाद Git 2.30.0 में support शुरू हुआ
  • git checkout और git reset खुद -- को interpret करते हैं, और शुरुआती implementation --end-of-options को argument list में छोड़ देती थी, इसलिए वे इसे reject करते थे
    • यह समस्या Git 2.43.1 के फरवरी 2024 release में ठीक हुई

shell के बिना भी argument injection

  • Git, Mercurial और SSH में caller द्वारा तय command चलाने वाले options आधिकारिक feature के रूप में मौजूद हैं
    • git clone --upload-pack=<cmd> server-side binary तय करता है
    • हर Git call में -c core.sshCommand=<cmd> connection command बदल देता है
    • Mercurial का --config=alias.<subcmd>=!<shell> चलाए जाने वाले subcommand को मनचाहे shell script से redefine करता है
    • SSH का -oProxyCommand=<cmd> proxy command तय करता है
  • अगर wrapper program untrusted string को argument list में डाल दे, तो ये features attack vector बन सकते हैं
  • इस तरह की विफलता CWE-88 argument injection है और shell command injection से अलग है
    • यह तब भी हो सकता है जब program system() की जगह argv array और exec का इस्तेमाल करे
    • array बिना टूटे Git तक पहुँचता है, लेकिन Git dash से शुरू होने वाले argument को option मान लेता है
  • docker build का CVE-2019-13139 ऐसा ही मामला था, जिसमें Go का os/exec और argv array इस्तेमाल हुआ और shell से नहीं गुज़रा
    • Git context URL का #ref:dir fragment git fetch origin <ref> में पास हुआ, जहाँ <ref> को --upload-pack=<cmd> की तरह समझा गया

कई version control systems में दोहराई गई कमजोरी

  • अगस्त 2017 में एक ही दिन चार version control systems में यही pattern सार्वजनिक हुआ
  • चारों systems URL के hostname को SSH argument के रूप में पास करते थे, और -oProxyCommand= से शुरू होने वाला hostname SSH option माना जाता था
  • Phabricator postmortem के अनुसार उस समय सक्रिय रूप से maintain हो रहे तीन tools में सिर्फ Subversion hostname से पहले -- जोड़ता था
    • Git और Mercurial hostname format validate करते थे, क्योंकि सभी SSH implementations -- को support नहीं करतीं
  • -- के बिना code argument के dash से शुरू होने तक सामान्य दिखता और काम करता है, इसलिए यह mechanism मूल रूप से unsafe है

package managers तक पहुँचने वाला exposure path

  • package managers manifest, lock file और transitive dependency metadata से Git URL या ref लेकर उसे subprocess में पास करते हैं
    • Gemfile में gem 'foo', git: '...'
    • package.json में github:user/repo#ref
    • pyproject.toml, Cargo.toml, mix.exs, Package.swift, pubspec.yaml, conanfile.py, go.mod में इसके समकक्ष settings
  • जुलाई 2026 के HEAD के आधार पर जांचे गए 19 package managers में 17 Git binary को default या एकमात्र path के रूप में चलाते थे
  • बाकी दो default रूप से libraries इस्तेमाल करते हैं
    • Cargo libgit2 इस्तेमाल करता है और net.git-fetch-with-cli सक्षम होने पर Git process चलाता है
    • Poetry 1.2.0 से dulwich पर switch कर चुका है, और system-git-client setting से system Git इस्तेमाल कर सकता है
  • Nix local repository पढ़ने में libgit2 इस्तेमाल करता है, लेकिन libgit2, git-credential helper को support नहीं करता, इसलिए fetch के लिए Git process चलाता है
  • जांच में Bundler, Cargo, CocoaPods, Composer, Conan, Go, Helm, Homebrew, Mix, Nix, npm, pip, pnpm, Poetry, Pub, SwiftPM, uv, vcpkg, Yarn शामिल थे

package managers में दर्ज CVE

वास्तविक defense status और Go का fix

  • Git process चलाने वाले 17 package managers में --end-of-options इस्तेमाल करने वाला tool सिर्फ Go का cmd/go था
  • Go ने जून 2019 में सामान्य defense hardening के तहत repository URL से पहले -- जोड़ा
  • जनवरी 2026 में स्पष्ट हुआ कि सिर्फ -- काफी नहीं है, इसलिए CVE-2025-68119 के fix में --end-of-options को व्यापक रूप से जोड़ा गया
  • उसी fix में HGPLAIN=+strictflags भी शामिल था
    • यह setting Mercurial 4.4.2 के 2017 release से Mercurial की शुरुआती option parsing को सीमित करती है
  • Go के fix commit में कहा गया कि इस समस्या को दोबारा आने से रोकने के लिए और संरचनात्मक बदलावों की ज़रूरत हो सकती है, लेकिन फिलहाल यह मौजूदा समस्या हल करता है

ज़्यादातर defenses vulnerability disclosure के बाद जोड़ी गईं

minimum Git version से पैदा होने वाली compatibility constraints

  • Composer के CVE-2022-24828 advisory में --end-of-options को सही fix बताया गया, लेकिन पुराने Git versions को support करने की ज़रूरत के कारण dash से शुरू होने वाले branch names को reject करने का रास्ता चुना गया
  • vcpkg का Git integration minimum version Git 2.7.4 बताता है, और Linux के लिए Homebrew का HOMEBREW_MINIMUM_GIT_VERSION 2018 में सेट किया गया 2.7.0 है
  • Git 2.14.3 देने वाला Amazon Linux 2, जून 2026 में end-of-life पर पहुँचा, इसलिए जिन distributions का यह lower bound पीछा कर रहा था वे अब जाकर support scope से बाहर हो रहे हैं
  • Ubuntu का long-term support status भी एकसमान transition को कठिन बनाता है
    • Ubuntu 18.04, Git 2.17.0 देता है और 2028 तक extended support में है
    • Ubuntu 20.04, Git 2.25.1 देता है और 2030 तक extended support में है
    • Git 2.25.1 git fetch में --end-of-options स्वीकार करता है, लेकिन git rev-parse में reject करता है
  • --end-of-options पर निर्भर होने के लिए ज़्यादातर subcommands में Git 2.24.0, rev-parse के लिए 2.30.0, और checkoutreset के लिए 2.43.1 minimum version चाहिए
  • minimum version बढ़ाने का मतलब है कि distributions के साथ आने वाले पुराने Git इस्तेमाल करने वाले users को support न कर पाना

process execution की जगह Git libraries का उपयोग

  • libgit2, gitoxide, go-git, JGit, dulwich process के अंदर clone और fetch के लिए ज़रूरी Git transport protocol implement करते हैं
  • अलग argv boundary नहीं होती, इसलिए argument list में inject करने के लिए कोई target ही नहीं होता
  • Jujutsu Git integration के लिए gitoxide इस्तेमाल करता है और argument injection प्रकार का कोई सार्वजनिक CVE नहीं है
    • अब तक की दो advisories path traversal और library से विरासत में मिले SHA-1 collision check की कमी से जुड़ी हैं
  • go-git का CVE-2025-21613 सिर्फ file:// transport तक सीमित है
    • go-git में यही एकमात्र code path है जहाँ Git binary चलाई जाती है
  • अपनी Git implementation शामिल करने का मतलब है upstream Git के checkout safety fixes को लगातार track करना; libgit2 और JGit दोनों में इससे जुड़े fixes बार-बार आए हैं
  • यह cost वास्तविक है, लेकिन हर call site पर हमेशा argument validation याद रखने के बजाय मामला ठोस upstream patch flow को अपनाने का बन जाता है

Homebrew के लिए प्रस्तावित बदलाव की सीमा

  • Homebrew PR minimum Git version को 2.30.0 तक बढ़ाता है और इन जगहों पर --end-of-options जोड़ता है
    • clone, remote set-url, ls-remote में URL से पहले
    • rev-parse में ref से पहले
  • checkout और reset calls नहीं बदली गईं
    • इन दो commands को भी सुरक्षित करने के लिए फरवरी 2024 में जारी Git 2.43.1 चाहिए
    • यह version अभी कई supported distributions द्वारा दिए जाने वाले Git से नया है

1 टिप्पणियां

 
GN⁺ 6 시간 전
Lobste.rs की राय
  • इन दिनों मैं jj का काफ़ी दीवाना हो रहा हूँ। Git की इस अव्यवस्था की तुलना में यह मन को सुकून देता है, और अभी सीख ही रहा हूँ, फिर भी यह इरादे को अच्छी तरह दर्शाता है और मनचाहा workflow भी आसानी से समझ में आता है
    खास तौर पर, एक jj कमांड में सीखी गई बात दूसरी कमांडों पर भी स्वाभाविक रूप से लागू हो जाती है। git log दस्तावेज़ में अलग-अलग output options के flags की भरमार है, ऊपर से commit range syntax की “विशेष notation” और अतिरिक्त filter flags भी आपस में घुले-मिले हैं, और दूसरी Git कमांडों में ट्रांसफ़र होने वाला ज्ञान भी कम है
    इसके उलट, jj log दस्तावेज़ इतना संक्षिप्त है कि एक पेज पर भी जगह बच जाती है। इसने Git की बेतरतीब जटिलता को revision sets, file sets, और output template DSL — इन तीन चीज़ों से बदल दिया है, और ये jj भर में एकसमान तरीके से इस्तेमाल होती हैं, इसलिए यह कहीं अधिक सरल, composable और intuitive लगता है। यह मानना पड़ेगा कि Git ने 20 साल में बहुत सा अतिरिक्त बोझ जमा किया है, लेकिन jj उसे टालने के लिए कहीं बेहतर स्थिति में दिखता है
  • मुझे पता है कि कमांड में user-provided arguments से पहले आम तौर पर -- की ज़रूरत होती है, लेकिन यहाँ अगर अपना अलग variant rule भी माँगा जाए तो यह हद से ज़्यादा ख़तरनाक trap बन जाता है
  • यह किसी टूल को ज़रूरत से ज़्यादा जटिल बना देने का सीधा नतीजा है। Git अब किसी नए Bash जैसा दिखने लगा है
  • मुझे जिज्ञासा है कि मूल रूप से file arguments की ambiguity दूर करने के लिए -- क्यों चुना गया। फ़ाइलों को explicit arguments के रूप में लेने वाले साधारण तरीके की बजाय इसे चुनने के पीछे कोई छिपा हुआ design trade-off था क्या
    • पहले से स्थापित UNIX option parsing convention को बिल्कुल अलग मक़सद के लिए इस्तेमाल करना, उम्मीद के मुताबिक़, मूर्खतापूर्ण था — और बहुत ही Git-जैसा फ़ैसला भी
  • UNIX की everything is text फ़िलॉसफ़ी फिर से समस्या पैदा कर रही है। Command line structured data का एकदम सही उदाहरण है, फिर भी इस गड्ढे से बाहर निकलने के लिए सामूहिक समन्वय की लागत शायद बहुत ज़्यादा है
    1990 के दशक की Tcl बनाम Scheme बहस को देखें तो इस मामले में शायद “worse is better” सही था। JSON और XML समेत S-expression परिवार UNIX ecosystem में लगभग जगह नहीं बना सका, जबकि Tcl के पास strings के भीतर sub-languages को layer करने का अपेक्षाकृत सिद्धांतसम्मत तरीका था