X-Git-Url: https://sipb.mit.edu/gitweb.cgi/ikiwiki.git/blobdiff_plain/b6a4da775945856b07fdc8f1330a86f67e329ac9..f2dc003cfbe08acb9f10a28503d78abefa7d67b3:/doc/plugins/po.mdwn diff --git a/doc/plugins/po.mdwn b/doc/plugins/po.mdwn index 1412cfea2..2f413e275 100644 --- a/doc/plugins/po.mdwn +++ b/doc/plugins/po.mdwn @@ -6,6 +6,8 @@ gettext, using [po4a](http://po4a.alioth.debian.org/). It depends on the Perl `Locale::Po4a::Po` library (`apt-get install po4a`). +[[!toc levels=2]] + Introduction ============ @@ -13,7 +15,7 @@ A language is chosen as the "master" one, and any other supported language is a "slave" one. A page written in the "master" language is a "master" page. It can be -of any page type supported by ikiwiki, but PO. It does not have to be +of any page type supported by ikiwiki, except `po`. It does not have to be named a special way: migration to this plugin does not imply any page renaming work. @@ -22,7 +24,7 @@ English; if `usedirs` is enabled, it is rendered as `bla/page/index.en.html`, else as `bla/page.en.html`. Any translation of a "master" page into a "slave" language is called -a "slave" page; it is written in the gettext PO format. PO is now +a "slave" page; it is written in the gettext PO format. `po` is now a page type supported by ikiwiki. Example: `bla/page.fr.po` is the PO "message catalog" used to @@ -56,9 +58,8 @@ The `po_translatable_pages` setting configures what pages are translatable. It is a [[ikiwiki/PageSpec]], so you have lots of control over what kind of pages are translatable. -The PO translations files are anyway not considered as being -translatable, so you don't need to worry about excluding them -explicitly from this [[ikiwiki/PageSpec]]. +The `.po` files are not considered as being translatable, so you don't need to +worry about excluding them explicitly from this [[ikiwiki/PageSpec]]. Internal links -------------- @@ -180,72 +181,300 @@ Additional PageSpec tests This plugin enhances the regular [[ikiwiki/PageSpec]] syntax with some additional tests that are documented [[here|ikiwiki/pagespec/po]]. -Automatic PO files update -------------------------- +Automatic PO file update +------------------------ Committing changes to a "master" page: -1. updates the POT file and the PO files for the supported languages - (this is done in the `needsbuild` hook); the updated PO files are - then put under version control -2. triggers a refresh of the corresponding HTML slave pages (this is - achieved by making any "slave" page dependent on the corresponding - "master" page, in the `needsbuild` hook) +1. updates the POT file and the PO files for the "slave" languages; + the updated PO files are then put under version control; +2. triggers a refresh of the corresponding HTML slave pages. Also, when the plugin has just been enabled, or when a page has just been declared as being translatable, the needed POT and PO files are created, and the PO files are checked into version control. -TODO -==== +Discussion pages +---------------- + +Discussion should happen in the language in which the pages are +written for real, *i.e.* the "master" one. If discussion pages are +enabled, "slave" pages therefore link to the "master" page's +discussion page. -OTHERLANGUAGES dependencies ---------------------------- +Translating +----------- -Pages using `OTHERLANGUAGES` depend on any "master" and "slave" pages -whose status is being displayed. It is supposed to trigger dependency -loops, but no practical bugs were noticed yet. +One can edit the PO files using ikiwiki's CGI (a message-by-message +interface could also be implemented at some point). -Should pages using the `OTHERLANGUAGES` template loop be declared as -linking to the same page in other versions? To be rigorous, they -should, but this may clutter the backlinks. +If [[tips/untrusted_git_push]] is setup, one can edit the PO files in one's +preferred `$EDITOR`, without needing to be online. + +TODO +==== Security checks --------------- -- `refreshpofiles` uses `system()`, whose args have to be checked more - thoroughly to prevent any security issue (command injection, etc.). -- `refreshpofiles` and `refreshpot` create new files; this may need - some checks, e.g. using `IkiWiki::prep_writefile()` +### Security history + +The only past security issues I could find in GNU gettext and po4a +are: + +- [CVE-2004-0966](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-0966), + *i.e.* [Debian bug #278283](http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=278283): + the autopoint and gettextize scripts in the GNU gettext package + 1.14 and later versions, as used in Trustix Secure Linux 1.5 + through 2.1 and other operating systems, allows local users to + overwrite files via a symlink attack on temporary files. +- [CVE-2007-4462](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-4462): + `lib/Locale/Po4a/Po.pm` in po4a before 0.32 allows local users to + overwrite arbitrary files via a symlink attack on the + gettextization.failed.po temporary file. + +**FIXME**: check whether this plugin would have been a possible attack +vector to exploit these vulnerabilities. + +Depending on my mood, the lack of found security issues can either +indicate that there are none, or reveal that no-one ever bothered to +find (and publish) them. + +### PO file features + +Can any sort of directives be put in po files that will cause mischief +(ie, include other files, run commands, crash gettext, whatever)? + +> No [documented](http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files) +> directive is supposed to do so. [[--intrigeri]] + +### Running po4a on untrusted content + +Are there any security issues on running po4a on untrusted content? + +To say the least, this issue is not well covered, at least publicly: + +- the documentation does not talk about it; +- grep'ing the source code for `security` or `trust` gives no answer. + +On the other hand, a po4a developer answered my questions in +a convincing manner, stating that processing untrusted content was not +an initial goal, and analysing in detail the possible issues. + +#### Already checked + +- the core (`Po.pm`, `Transtractor.pm`) should be safe +- po4a source code was fully checked for other potential symlink + attacks, after discovery of one such issue +- the only external program run by the core is `diff`, in `Po.pm` (in + parts of its code we don't use) +- `Locale::gettext`: only used to display translated error messages +- Nicolas François "hopes" `DynaLoader` is safe, and has "no reason to + think that `Encode` is not safe" +- Nicolas François has "no reason to think that `Encode::Guess` is not + safe". The po plugin nevertheless avoids using it by defining the + input charset (`file_in_charset`) before asking `Transtractor` to + read any file. NB: this hack depends on po4a internals to stay + the same. + +#### To be checked + +##### Locale::Po4a modules + +The modules we want to use have to be checked, as not all are safe +(e.g. the LaTeX module's behaviour is changed by commands included in +the content); they may use regexps generated from the content. + +`Chooser.pm` only loads the plugin we tell it too: currently, this +means the `Text` module only. + +`Text` module (I checked the CVS version): + +- it does not run any external program +- only `do_paragraph()` builds regexp's that expand untrusted + variables; they seem safe to me, but someone more expert than me + will need to check. Joey? + +##### Text::WrapI18N + +`Text::WrapI18N` can cause DoS (see the +[Debian bug #470250](http://bugs.debian.org/470250)), but it is +optional and we do not need the features it provides. + +It is loaded if available by `Locale::Po4a::Common`; looking at the +code, I'm not sure we can prevent this at all, but maybe some symbol +table manipulation tricks could work; overriding +`Locale::Po4a::Common::wrapi18n` may be easier. I'm no expert at all +in this field. Joey? [[--intrigeri]] + +> Update: Nicolas François suggests we add an option to po4a to +> disable it. It would do the trick, but only for people running +> a brand new po4a (probably too late for Lenny). Anyway, this option +> would have to take effect in a `BEGIN` / `eval` that I'm not +> familiar with. I can learn and do it, in case no Perl wizard +> volunteers to provide the po4a patch. [[--intrigeri]] + +##### Term::ReadKey + +`Term::ReadKey` is not a hard dependency in our case, *i.e.* po4a +works nicely without it. But the po4a Debian package recommends +`libterm-readkey-perl`, so it will probably be installed on most +systems using the po plugin. + +If `$ENV{COLUMNS}` is not set, `Locale::Po4a::Common` uses +`Term::ReadKey::GetTerminalSize()` to get the terminal size. How safe +is this? + +Part of `Term::ReadKey` is written in C. Depending on the runtime +platform, this function use ioctl, environment, or C library function +calls, and may end up running the `resize` command (without +arguments). + +IMHO, using Term::ReadKey has too far reaching implications for us to +be able to guarantee anything wrt. security. Since it is anyway of no +use in our case, I suggest we define `ENV{COLUMNS}` before loading +`Locale::Po4a::Common`, just to be on the safe side. Joey? +[[--intrigeri]] + +> Update: adding an option to disable `Text::WrapI18N`, as Nicolas +> François suggested, would as a bonus disable `Term::ReadKey` +> as well. [[--intrigeri]] + +### msgmerge + +`refreshpofiles()` runs this external program. A po4a developer +answered he does "not expect any security issues from it". + +### Fuzzing input + +I was not able to find any public information about gettext or po4a +having been tested with a fuzzing program, such as `zzuf` or `fusil`. +Moreover, some gettext parsers seem to be quite +[easy to crash](http://fusil.hachoir.org/trac/browser/trunk/fuzzers/fusil-gettext), +so it might be useful to bang msgmerge/po4a's heads against such +a program in order to easily detect some of the most obvious DoS. +[[--intrigeri]] + +> po4a was not fuzzy-tested, but according to one of its developers, +> "it would be really appreciated". [[--intrigeri]] + +Test conditions: + +- a 21M file containing 100 concatenated copies of all the files in my + `/usr/share/common-licenses/`; I had no existing PO file or + translated versions at hand, which renders these tests + quite incomplete. +- po4a was the Debian 0.34-2 package; the same tests were also run + after replacing the `Text` module with the CVS one (the core was not + changed in CVS since 0.34-2 was released), without any significant + difference in the results. +- Perl 5.10.0-16 + +#### po4a-gettextize + +`po4a-gettextize` uses more or less the same po4a features as our +`refreshpot` function. + +Without specifying an input charset, zzuf'ed `po4a-gettextize` quickly +errors out, complaining it was not able to detect the input charset; +it leaves no incomplete file on disk. + +So I had to pretend the input was in UTF-8, as does the po plugin. + +Two ways of crashing were revealed by this command-line: + + zzuf -vc -s 0:100 -r 0.1:0.5 \ + po4a-gettextize -f text -o markdown -M utf-8 -L utf-8 \ + -m LICENSES >/dev/null + +They are: + + Malformed UTF-8 character (UTF-16 surrogate 0xdcc9) in substitution iterator at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + Malformed UTF-8 character (fatal) at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + +and + + Malformed UTF-8 character (UTF-16 surrogate 0xdcec) in substitution (s///) at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + Malformed UTF-8 character (fatal) at /usr/share/perl5/Locale/Po4a/Po.pm line 1443. + +Perl seems to exit cleanly, and an incomplete PO file is written on +disk. I not sure whether if this is a bug in Perl or in `Po.pm`. + +#### po4a-translate + +`po4a-translate` uses more or less the same po4a features as our +`filter` function. + +Without specifying an input charset, same behaviour as +`po4a-gettextize`, so let's specify UTF-8 as input charset as of now. + + zzuf -cv \ + po4a-translate -d -f text -o markdown -M utf-8 -L utf-8 \ + -k 0 -m LICENSES -p LICENSES.fr.po -l test.fr + +... prints tons of occurences of the following error, but a complete +translated document is written (obviously with some weird chars +inside): + + Use of uninitialized value in string ne at /usr/share/perl5/Locale/Po4a/TransTractor.pm line 854. + Use of uninitialized value in string ne at /usr/share/perl5/Locale/Po4a/TransTractor.pm line 840. + Use of uninitialized value in pattern match (m//) at /usr/share/perl5/Locale/Po4a/Po.pm line 1002. + +While: + + zzuf -cv -s 0:10 -r 0.001:0.3 \ + po4a-translate -d -f text -o markdown -M utf-8 -L utf-8 \ + -k 0 -m LICENSES -p LICENSES.fr.po -l test.fr + +... seems to lose the fight, at the `readpo(LICENSES.fr.po)` step, +against some kind of infinite loop, deadlock, or any similar beast. +It does not seem to eat memory, though. + +Whatever format module is used does not change anything. This is thus +probably a bug in po4a's core or in a lib it depends on. + +The sub `read`, in `TransTractor.pm`, seems to be a good debugging +starting point. + +#### msgmerge + +`msgmerge` is run in our `refreshpofiles` function. I did not manage +to crash it with `zzuf`. gettext/po4a rough corners -------------------------- -- fix the duplicated PO header mysterious bug -- fix the "duplicate message definition" error when updating a PO - file; do PO files need normalizing? (may be a side effect of - previous bug) - fix infinite loop when synchronizing two ikiwiki (when checkouts live in different directories): say bla.fr.po has been updated in repo2; pulling repo2 from repo1 seems to trigger a PO update, that changes bla.fr.po in repo1; then pushing repo1 to repo2 triggers - a PO update, that changes bla.fr.po in repo2; etc.; fixed in - `629968fc89bced6727981c0a1138072631751fee` ? + a PO update, that changes bla.fr.po in repo2; etc.; quickly fixed in + `629968fc89bced6727981c0a1138072631751fee`, by disabling references + in Pot files. Using `Locale::Po4a::write_if_needed` might be + a cleaner solution. (warning: this function runs the external + `diff` program, have to check security) +- new translations created in the web interface must get proper + charset/encoding gettext metadata, else the next automatic PO update + removes any non-ascii chars; possible solution: put such metadata + into the Pot file, and let it propagate; should be fixed in + `773de05a7a1ee68d2bed173367cf5e716884945a`, time will tell. Misc. improvements ------------------ -### automatic POT/PO update - -Use the `change` hook instead of `needsbuild`? - ### page titles -Use nice page titles from meta plugin in links, as inline already does. +Use nice page titles from meta plugin in links, as inline already +does. This is actually a duplicate for +[[bugs/pagetitle_function_does_not_respect_meta_titles]], which might +be fixed by something like [[todo/using_meta_titles_for_parentlinks]]. -### websetup +### source files format -Which configuration settings are safe enough for websetup? +Markdown is supported, great, but what about others? The set of file +formats supported both in ikiwiki and po4a probably is greater than +`{markdown}`. Warning: the po4a modules are the place where one can +expect security issues. Translation quality assurance ----------------------------- @@ -258,43 +487,3 @@ A new `cansave` type of hook would be needed to implement this. Note: committing to the underlying repository is a way to bypass this check. - -Translating online ------------------- - -As PO is a wiki page type, we already have an online PO editor, that -is ikiwiki's CGI. - -**FIXME** preview does not work for PO files. - -A message-by-message interface could also be implemented at some -point; a nice way to do offline translation work (without VCS access) -still has to be offered, though. - -Translating offline without VCS access --------------------------------------- - -The following workflow should be made possible for translators without -VCS access who need to edit the PO files in another editor than a web -browser: - -- download the page's PO file -- use any PO editor to update the translation -- upload the updated PO file - -Implementation note: a generic mechanism to upload a page's source is -needed: it's only an alternative way to allow saving a the modified -page's source with the CGI. - -### Short-term workflow - -A possible workaround is: - -- pretend to edit the PO file online -- copy the PO file content from the textarea -- cancel the edit -- paste the content into a local file. -- edit the local file in any PO editor -- pretend to edit the PO file online -- paste the modified local file's content into the edit textarea -- save