Commit 29631cabd62f8a9790b5ea9c358fc6557fa17d2b
1 parent
47a88683
Fix bash completion issue
Reported by Stephane Chazelas. Potentially sensitive arguments could be leaked to the environment during completion computation.
Showing
9 changed files
with
72 additions
and
30 deletions
completions/bash/qpdf
completions/zsh/_qpdf
generate_auto_job
| ... | ... | @@ -351,6 +351,16 @@ class Main: |
| 351 | 351 | for k, v in hashes.items(): |
| 352 | 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 | 364 | def generate_doc(self, df, f, f_man): |
| 355 | 365 | """ |
| 356 | 366 | Generates documentation and help-related functionalities for a given parser. |
| ... | ... | @@ -498,8 +508,10 @@ class Main: |
| 498 | 508 | elif state == st_topic: |
| 499 | 509 | if append_long_text(line, topic): |
| 500 | 510 | self.all_topics.add(topic) |
| 511 | + r_quote_open, r_quote_close = self.quotes(long_text) | |
| 501 | 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 | 515 | print(f'.SH {topic.upper()} ({short_text})', file=f_man) |
| 504 | 516 | print(manify(long_text), file=f_man, end='') |
| 505 | 517 | help_lines += 1 |
| ... | ... | @@ -523,8 +535,11 @@ class Main: |
| 523 | 535 | f' lineno={lineno}') |
| 524 | 536 | if option not in self.help_options: |
| 525 | 537 | self.jdata[option[2:]]['help'] = short_text |
| 538 | + r_quote_open, r_quote_close = self.quotes(long_text) | |
| 526 | 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 | 543 | if last_option_topic != topic: |
| 529 | 544 | print('.PP\nRelated Options:', file=f_man) |
| 530 | 545 | last_option_topic = topic | ... | ... |
job.sums
| 1 | 1 | # Generated by generate_auto_job |
| 2 | 2 | CMakeLists.txt 18214e276670dc8beb2ab83f789c6d94941bc92b199b353f3943024cfd41d3bc |
| 3 | -generate_auto_job 280b75d5307c537385a75ec588493496cfb0bc754d48c34ca8c42bbc55dd717b | |
| 3 | +generate_auto_job 8e3175a515aa8837d8a01bba0346b04b3d777d70330ba5b7d52f691316054a34 | |
| 4 | 4 | include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 |
| 5 | 5 | include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 |
| 6 | 6 | include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 |
| ... | ... | @@ -9,12 +9,12 @@ include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa8 |
| 9 | 9 | include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62 |
| 10 | 10 | job.yml e136726a6c7e43736b1f75f4de347fa50baf5f38ed1da58647ce2e980751fb29 |
| 11 | 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 | 13 | libqpdf/qpdf/auto_job_init.hh 02c526c37ad4051cac956ac7c12ae1d020517264f3f3d3beabb066ae2529e4bf |
| 14 | 14 | libqpdf/qpdf/auto_job_json_decl.hh 04965f6321e54b8b3b1dd2ca101d763a22ab44fa81c69e4b6fc0fd6bb7f50f92 |
| 15 | 15 | libqpdf/qpdf/auto_job_json_init.hh b49378f00d521a9f3e0ce9086e30b082bc6ef8e43c845e2a3c99857b72448307 |
| 16 | 16 | libqpdf/qpdf/auto_job_schema.hh f6a3e8b663714bba50b594f5e31437bbcb96ca4609d2c150c3bbc172e3b000fa |
| 17 | 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 | 20 | manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b | ... | ... |
libqpdf/QPDFArgParser.cc
| ... | ... | @@ -183,13 +183,27 @@ QPDFArgParser::completionCommon(bool zsh) |
| 183 | 183 | } |
| 184 | 184 | } |
| 185 | 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 | 207 | // Put output before error so calling from zsh works properly |
| 194 | 208 | std::string path = progname; |
| 195 | 209 | size_t slash = path.find('/'); |
| ... | ... | @@ -376,9 +390,19 @@ QPDFArgParser::checkCompletion() |
| 376 | 390 | if (p > m->bash_line.length()) { |
| 377 | 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 | 407 | // p is equal to length of the string. Walk backwards looking for the first separator. |
| 384 | 408 | // bash_cur is everything after the last separator, possibly empty. | ... | ... |
libqpdf/qpdf/auto_job_help.hh
| ... | ... | @@ -45,11 +45,11 @@ ap.addHelpTopic("exit-status", "meanings of qpdf's exit codes", R"(Meaning of ex |
| 45 | 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 | 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 | 50 | to enable. The QPDF_EXECUTABLE environment variable overrides the |
| 51 | 51 | path to qpdf that these commands output. |
| 52 | -)"); | |
| 52 | +)/"); | |
| 53 | 53 | ap.addOptionHelp("--completion-bash", "completion", "enable bash completion", R"(Output a command that enables bash completion |
| 54 | 54 | )"); |
| 55 | 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 | 244 | .. help-topic completion: shell completion |
| 245 | 245 | |
| 246 | 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 | 248 | to enable. The QPDF_EXECUTABLE environment variable overrides the |
| 249 | 249 | path to qpdf that these commands output. |
| 250 | 250 | |
| 251 | 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 | 255 | use an absolute path to qpdf in the above invocation. If you invoke it |
| 256 | 256 | with a relative path, it will warn you, and the completion won't work |
| 257 | 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 | 78 | combined with --no-warn, warnings are completely ignored. |
| 79 | 79 | .SH COMPLETION (shell completion) |
| 80 | 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 | 82 | to enable. The QPDF_EXECUTABLE environment variable overrides the |
| 83 | 83 | path to qpdf that these commands output. |
| 84 | 84 | .PP | ... | ... |
manual/release-notes.rst
| ... | ... | @@ -55,12 +55,15 @@ more detail. |
| 55 | 55 | |
| 56 | 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 | 68 | - Other enhancements |
| 66 | 69 | ... | ... |