Commit 29631cabd62f8a9790b5ea9c358fc6557fa17d2b

Authored by Jay Berkenbilt
1 parent 47a88683

Fix bash completion issue

Reported by Stephane Chazelas. Potentially sensitive arguments could
be leaked to the environment during completion computation.
completions/bash/qpdf
1 -eval $(/usr/bin/qpdf --completion-bash) 1 +eval "$(/usr/bin/qpdf --completion-bash)"
completions/zsh/_qpdf
1 #compdef qpdf 1 #compdef qpdf
2 -eval $(/usr/bin/qpdf --completion-zsh) 2 +eval "$(/usr/bin/qpdf --completion-zsh)"
generate_auto_job
@@ -351,6 +351,16 @@ class Main: @@ -351,6 +351,16 @@ class Main:
351 for k, v in hashes.items(): 351 for k, v in hashes.items():
352 print(f'{k} {v}', file=f) 352 print(f'{k} {v}', file=f)
353 353
  354 + @staticmethod
  355 + def quotes(long_text):
  356 + if '"(' in long_text or ')"' in long_text:
  357 + r_quote_open = 'R"/('
  358 + r_quote_close = ')/"'
  359 + else:
  360 + r_quote_open = 'R"('
  361 + r_quote_close = ')"'
  362 + return (r_quote_open, r_quote_close)
  363 +
354 def generate_doc(self, df, f, f_man): 364 def generate_doc(self, df, f, f_man):
355 """ 365 """
356 Generates documentation and help-related functionalities for a given parser. 366 Generates documentation and help-related functionalities for a given parser.
@@ -498,8 +508,10 @@ class Main: @@ -498,8 +508,10 @@ class Main:
498 elif state == st_topic: 508 elif state == st_topic:
499 if append_long_text(line, topic): 509 if append_long_text(line, topic):
500 self.all_topics.add(topic) 510 self.all_topics.add(topic)
  511 + r_quote_open, r_quote_close = self.quotes(long_text)
501 print(f'ap.addHelpTopic("{topic}", "{short_text}",' 512 print(f'ap.addHelpTopic("{topic}", "{short_text}",'
502 - f' R"({long_text})");', file=f) 513 + f' {r_quote_open}{long_text}{r_quote_close});',
  514 + file=f)
503 print(f'.SH {topic.upper()} ({short_text})', file=f_man) 515 print(f'.SH {topic.upper()} ({short_text})', file=f_man)
504 print(manify(long_text), file=f_man, end='') 516 print(manify(long_text), file=f_man, end='')
505 help_lines += 1 517 help_lines += 1
@@ -523,8 +535,11 @@ class Main: @@ -523,8 +535,11 @@ class Main:
523 f' lineno={lineno}') 535 f' lineno={lineno}')
524 if option not in self.help_options: 536 if option not in self.help_options:
525 self.jdata[option[2:]]['help'] = short_text 537 self.jdata[option[2:]]['help'] = short_text
  538 + r_quote_open, r_quote_close = self.quotes(long_text)
526 print(f'ap.addOptionHelp("{option}", "{topic}",' 539 print(f'ap.addOptionHelp("{option}", "{topic}",'
527 - f' "{short_text}", R"({long_text})");', file=f) 540 + f' "{short_text}",'
  541 + f' {r_quote_open}{long_text}{r_quote_close});',
  542 + file=f)
528 if last_option_topic != topic: 543 if last_option_topic != topic:
529 print('.PP\nRelated Options:', file=f_man) 544 print('.PP\nRelated Options:', file=f_man)
530 last_option_topic = topic 545 last_option_topic = topic
job.sums
1 # Generated by generate_auto_job 1 # Generated by generate_auto_job
2 CMakeLists.txt 18214e276670dc8beb2ab83f789c6d94941bc92b199b353f3943024cfd41d3bc 2 CMakeLists.txt 18214e276670dc8beb2ab83f789c6d94941bc92b199b353f3943024cfd41d3bc
3 -generate_auto_job 280b75d5307c537385a75ec588493496cfb0bc754d48c34ca8c42bbc55dd717b 3 +generate_auto_job 8e3175a515aa8837d8a01bba0346b04b3d777d70330ba5b7d52f691316054a34
4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4
5 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 5 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42
6 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 6 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5
@@ -9,12 +9,12 @@ include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa8 @@ -9,12 +9,12 @@ include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa8
9 include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62 9 include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62
10 job.yml e136726a6c7e43736b1f75f4de347fa50baf5f38ed1da58647ce2e980751fb29 10 job.yml e136726a6c7e43736b1f75f4de347fa50baf5f38ed1da58647ce2e980751fb29
11 libqpdf/qpdf/auto_job_decl.hh 34ba07d3891c3e5cdd8712f991e508a0652c9db314c5d5bcdf4421b76e6f6e01 11 libqpdf/qpdf/auto_job_decl.hh 34ba07d3891c3e5cdd8712f991e508a0652c9db314c5d5bcdf4421b76e6f6e01
12 -libqpdf/qpdf/auto_job_help.hh bcfe600cc01447a7fae8661a8d101c7b15ce144bb06ba0beab656f3a655371d1 12 +libqpdf/qpdf/auto_job_help.hh d0cca031e99f10caa3f4b70ea574b36b0af63d24de333e7d6f0bf835e959f0be
13 libqpdf/qpdf/auto_job_init.hh 02c526c37ad4051cac956ac7c12ae1d020517264f3f3d3beabb066ae2529e4bf 13 libqpdf/qpdf/auto_job_init.hh 02c526c37ad4051cac956ac7c12ae1d020517264f3f3d3beabb066ae2529e4bf
14 libqpdf/qpdf/auto_job_json_decl.hh 04965f6321e54b8b3b1dd2ca101d763a22ab44fa81c69e4b6fc0fd6bb7f50f92 14 libqpdf/qpdf/auto_job_json_decl.hh 04965f6321e54b8b3b1dd2ca101d763a22ab44fa81c69e4b6fc0fd6bb7f50f92
15 libqpdf/qpdf/auto_job_json_init.hh b49378f00d521a9f3e0ce9086e30b082bc6ef8e43c845e2a3c99857b72448307 15 libqpdf/qpdf/auto_job_json_init.hh b49378f00d521a9f3e0ce9086e30b082bc6ef8e43c845e2a3c99857b72448307
16 libqpdf/qpdf/auto_job_schema.hh f6a3e8b663714bba50b594f5e31437bbcb96ca4609d2c150c3bbc172e3b000fa 16 libqpdf/qpdf/auto_job_schema.hh f6a3e8b663714bba50b594f5e31437bbcb96ca4609d2c150c3bbc172e3b000fa
17 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 17 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
18 -manual/cli.rst 6fae28c9589bfde5b55260c95a7c64ad48688875f14f195129606405b32a04c6  
19 -manual/qpdf.1 358dfe1bbeb49366d6dd17f74d883295344725b985183b8ca5e23226461654b3 18 +manual/cli.rst b7bd5e34495d3f9156ff6242988dba73a2e5dce33d71f75ec1415514a3843f35
  19 +manual/qpdf.1 d5785d23e77b02a77180419d87787002dc244d82d586d56008ab603299f565fd
20 manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b 20 manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b
libqpdf/QPDFArgParser.cc
@@ -183,13 +183,27 @@ QPDFArgParser::completionCommon(bool zsh) @@ -183,13 +183,27 @@ QPDFArgParser::completionCommon(bool zsh)
183 } 183 }
184 } 184 }
185 if (zsh) { 185 if (zsh) {
186 - std::cout << "autoload -U +X bashcompinit && bashcompinit && ";  
187 - }  
188 - std::cout << "complete -o bashdefault -o default";  
189 - if (!zsh) {  
190 - std::cout << " -o nospace"; 186 + // FIXME: we assume progname doesn't contain single quote
  187 + // characters. 's in progname like in "/opt/joe's software/qpdf"
  188 + // should ideally be escaped as '\''. Unlikely to be a problem
  189 + // in practice. '...' is preferable over "..." as inside the
  190 + // latter more characters ("$`\) are a problem and it's
  191 + // virtually impossible to escape those in a locale-independent
  192 + // way.
  193 + std::cout << "autoload -U +X bashcompinit && bashcompinit &&"
  194 + << "complete -o bashdefault -o default -C '" << progname << "' " << m->whoami
  195 + << "\n";
  196 + } else {
  197 + // we need a function wrapper that discards arguments to avoid
  198 + // leaking sensitive information in the process argument list
  199 + // which is public on most systems. Here putting the code on one
  200 + // line as old versions of the documentation were instructing
  201 + // users to do eval $(qpdf --completion-bash) instead of the
  202 + // correct eval "$(qpdf --completion-bash)"
  203 + std::cout << "qpdf_completer() { '" << progname << "'; }; "
  204 + << "complete -o bashdefault -o default -o nospace -F qpdf_completer " << m->whoami
  205 + << "\n";
191 } 206 }
192 - std::cout << " -C \"" << progname << "\" " << m->whoami << '\n';  
193 // Put output before error so calling from zsh works properly 207 // Put output before error so calling from zsh works properly
194 std::string path = progname; 208 std::string path = progname;
195 size_t slash = path.find('/'); 209 size_t slash = path.find('/');
@@ -376,9 +390,19 @@ QPDFArgParser::checkCompletion() @@ -376,9 +390,19 @@ QPDFArgParser::checkCompletion()
376 if (p > m->bash_line.length()) { 390 if (p > m->bash_line.length()) {
377 p = m->bash_line.length(); 391 p = m->bash_line.length();
378 } 392 }
379 - // Set bash_cur and bash_prev based on bash_line rather than relying on argv. This enables  
380 - // us to use bashcompinit to get completion in zsh too since bashcompinit sets COMP_LINE and  
381 - // COMP_POINT but doesn't invoke the command with options like bash does. 393 + // Set bash_cur and bash_prev based on bash_line rather than relying on
  394 + // argv. Using argv is unsafe as process argument lists are public on
  395 + // most systems. zsh doesn't pass information there, and we actively
  396 + // discard them for bash with our qpdf_completer to avoid that
  397 + // information disclosure vulnerability. Both bash and zsh set
  398 + // COMP_LINE and COMP_POINT which we can rely on instead.
  399 + //
  400 + // FIXME. For both bash and zsh, COMP_POINT is an offset in terms of
  401 + // *characters*, with characters decoded as per the shell's own locale.
  402 + // Here we interpret it as a *byte* offset which means it won't work
  403 + // properly if there are multibyte characters to the left of the
  404 + // cursor, but saves us having to decode the command line (which is hard
  405 + // to do in the same way the shell does in all cases).
382 406
383 // p is equal to length of the string. Walk backwards looking for the first separator. 407 // p is equal to length of the string. Walk backwards looking for the first separator.
384 // bash_cur is everything after the last separator, possibly empty. 408 // bash_cur is everything after the last separator, possibly empty.
libqpdf/qpdf/auto_job_help.hh
@@ -45,11 +45,11 @@ ap.addHelpTopic(&quot;exit-status&quot;, &quot;meanings of qpdf&#39;s exit codes&quot;, R&quot;(Meaning of ex @@ -45,11 +45,11 @@ ap.addHelpTopic(&quot;exit-status&quot;, &quot;meanings of qpdf&#39;s exit codes&quot;, R&quot;(Meaning of ex
45 ap.addOptionHelp("--warning-exit-0", "exit-status", "exit 0 even with warnings", R"(Use exit status 0 instead of 3 when warnings are present. When 45 ap.addOptionHelp("--warning-exit-0", "exit-status", "exit 0 even with warnings", R"(Use exit status 0 instead of 3 when warnings are present. When
46 combined with --no-warn, warnings are completely ignored. 46 combined with --no-warn, warnings are completely ignored.
47 )"); 47 )");
48 -ap.addHelpTopic("completion", "shell completion", R"(Shell completion is supported with bash and zsh. Use  
49 -eval $(qpdf --completion-bash) or eval $(qpdf --completion-zsh) 48 +ap.addHelpTopic("completion", "shell completion", R"/(Shell completion is supported with bash and zsh. Use
  49 +eval "$(qpdf --completion-bash)" or eval "$(qpdf --completion-zsh)"
50 to enable. The QPDF_EXECUTABLE environment variable overrides the 50 to enable. The QPDF_EXECUTABLE environment variable overrides the
51 path to qpdf that these commands output. 51 path to qpdf that these commands output.
52 -)"); 52 +)/");
53 ap.addOptionHelp("--completion-bash", "completion", "enable bash completion", R"(Output a command that enables bash completion 53 ap.addOptionHelp("--completion-bash", "completion", "enable bash completion", R"(Output a command that enables bash completion
54 )"); 54 )");
55 ap.addOptionHelp("--completion-zsh", "completion", "enable zsh completion", R"(Output a command that enables zsh completion 55 ap.addOptionHelp("--completion-zsh", "completion", "enable zsh completion", R"(Output a command that enables zsh completion
manual/cli.rst
@@ -244,14 +244,14 @@ Shell Completion @@ -244,14 +244,14 @@ Shell Completion
244 .. help-topic completion: shell completion 244 .. help-topic completion: shell completion
245 245
246 Shell completion is supported with bash and zsh. Use 246 Shell completion is supported with bash and zsh. Use
247 - eval $(qpdf --completion-bash) or eval $(qpdf --completion-zsh) 247 + eval "$(qpdf --completion-bash)" or eval "$(qpdf --completion-zsh)"
248 to enable. The QPDF_EXECUTABLE environment variable overrides the 248 to enable. The QPDF_EXECUTABLE environment variable overrides the
249 path to qpdf that these commands output. 249 path to qpdf that these commands output.
250 250
251 :command:`qpdf` provides its own completion support for zsh and bash. 251 :command:`qpdf` provides its own completion support for zsh and bash.
252 -You can enable bash completion with :command:`eval $(qpdf  
253 ---completion-bash)` and zsh completion with :command:`eval $(qpdf  
254 ---completion-zsh)`. If :command:`qpdf` is not in your path, you should 252 +You can enable bash completion with :command:`eval "$(qpdf
  253 +--completion-bash)"` and zsh completion with :command:`eval "$(qpdf
  254 +--completion-zsh)"`. If :command:`qpdf` is not in your path, you should
255 use an absolute path to qpdf in the above invocation. If you invoke it 255 use an absolute path to qpdf in the above invocation. If you invoke it
256 with a relative path, it will warn you, and the completion won't work 256 with a relative path, it will warn you, and the completion won't work
257 if you're in a different directory. 257 if you're in a different directory.
manual/qpdf.1
@@ -78,7 +78,7 @@ Use exit status 0 instead of 3 when warnings are present. When @@ -78,7 +78,7 @@ Use exit status 0 instead of 3 when warnings are present. When
78 combined with --no-warn, warnings are completely ignored. 78 combined with --no-warn, warnings are completely ignored.
79 .SH COMPLETION (shell completion) 79 .SH COMPLETION (shell completion)
80 Shell completion is supported with bash and zsh. Use 80 Shell completion is supported with bash and zsh. Use
81 -eval $(qpdf --completion-bash) or eval $(qpdf --completion-zsh) 81 +eval "$(qpdf --completion-bash)" or eval "$(qpdf --completion-zsh)"
82 to enable. The QPDF_EXECUTABLE environment variable overrides the 82 to enable. The QPDF_EXECUTABLE environment variable overrides the
83 path to qpdf that these commands output. 83 path to qpdf that these commands output.
84 .PP 84 .PP
manual/release-notes.rst
@@ -55,12 +55,15 @@ more detail. @@ -55,12 +55,15 @@ more detail.
55 55
56 - CLI Enhancements 56 - CLI Enhancements
57 57
58 - - Disallow option :qpdf:ref:`--deterministic-id` to be used together  
59 - with the incompatible options :qpdf:ref:`--encrypt` or  
60 - :qpdf:ref:`--copy-encryption`. 58 + - Disallow option :qpdf:ref:`--deterministic-id` to be used together
  59 + with the incompatible options :qpdf:ref:`--encrypt` or
  60 + :qpdf:ref:`--copy-encryption`.
61 61
62 - - Option :qpdf:ref:`--check` now includes additional basic checks of the  
63 - AcroForm, Dests, Outlines, and PageLabels structures. 62 + - Option :qpdf:ref:`--check` now includes additional basic checks of the
  63 + AcroForm, Dests, Outlines, and PageLabels structures.
  64 +
  65 + - Fix completion scripts and handling to avoid leaking arguments
  66 + into the environment during completion.
64 67
65 - Other enhancements 68 - Other enhancements
66 69