3

Background

Looking to replace a string within a document with an equivalent string created using a macro. For example, I'd like to replace "McAnulty" with "\Mac Anulty".

Problem

When using an XML document as the input source, replacing the string means not being able to flush the inner XML elements. The result:

flush output

Code

The following code creates an XHTML document, replaces "McAnulty" with "\Mac Anulty", but cannot flush the XML.

\startbuffer[main]
<html>
  <p>“Mr. McAnulty, I presume?”</p>
  <p>Regular text. <em>Irregular text.</em></p>
</html>
\stopbuffer

\startxmlsetups xml:xhtml
  \xmlsetsetup{\xmldocument}{*}{-}
  \xmlsetsetup{\xmldocument}{html|p|em}{xml:*}
\stopxmlsetups

\startxmlsetups xml:html
  \startdocument
    \xmlflush{#1}
  \stopdocument
\stopxmlsetups

\startxmlsetups xml:p
  \xmlfunction{#1}{p}
  \par
\stopxmlsetups

\startxmlsetups xml:em
  \dontleavehmode{\em\xmlflush{#1}}
\stopxmlsetups

\startluacode
function xml.functions.p( t )
  rep = { [1] = { "McAnulty", "\\Mac Anulty" } }
  x = lpeg.replacer( rep ):match( tostring( xml.text( t ) ) )

  buffers.assign( "p", context( x ) )
  context.getbuffer{ "p" }
end
\stopluacode

\xmlregistersetup{xml:xhtml}

\def\Mac{%
  % Determine the sizes of 'M' and 'c'.
  \newbox\MacMBox%
  \setbox\MacMBox\hbox{M}%
  \newbox\MacCBox%
  \setbox\MacCBox\hbox{c}%
  %
  % Cheat to dynamically derive the kerning size by putting Mc in a box.
  %
  \newbox\MacKernBox%
  \setbox\MacKernBox\hbox{\inframed[offset=\zeropoint, width=fit]{Mc}}%
  \def\MacDelta{\dimexpr\wd\MacKernBox-\wd\MacMBox-\wd\MacCBox\relax}%
  \def\MacUWidth{\dimexpr\wd\MacCBox-.75\MacDelta\relax}%
  \def\MacRule{\vrule width \MacUWidth height .04em depth \zeropoint \relax}%
  \def\MacKern{\dimexpr\wd\MacKernBox-\wd\MacMBox-\wd\MacCBox\relax}%
  \def\MacHeight{\dimexpr\ht\MacMBox-\ht\MacCBox\relax}%
  %
  % Write Mc, where c has a macron, to the document.
  %
  M{%
    \dontleavehmode{\raisebox{\MacHeight}\hbox{c}}%
    \kern-1.04\MacUWidth
    \MacRule
    \kern.08\MacUWidth
  }%
}%

\xmlprocessbuffer{main}{main}{}

Question

How can pre-processing occur (e.g., string replacement) while still being able to apply other XML setups to XHTML elements?

Related

1 Answer 1

2

Using \cldcontext allows malicious users to execute arbitrary commands by abusing string escaping. Instead of expanding a TeX parameter inside of a Lua string, define a new TeX command that takes the string as an argument, such as:

\startluacode
local function processmac( t )
  rep = { [1] = { "McAnulty", "\\Mac Anulty" } }
  context(lpeg.replacer( rep ):match( t ))
end

interfaces.implement {
  name      = "processmac",
  arguments = { "string" },
  public    = true,
  actions   = processmac,
}
\stopluacode

\startxmlsetups xml:p
  \expandafter\processmac{\xmlflush{#1}}
  \par
\stopxmlsetups

\startxmlsetups xml:em
  \dontleavehmode{\em\xmlflush{#1}}
\stopxmlsetups

You can test with this HTML input:

<html>
  <p>“Mr. McAnulty, I presume?”</p>
  <p>]] .. (os.execute("touch /tmp/filename.txt") and '') .. [[</p>
  <p>Regular text. <em>Irregular text.</em></p>
</html>

to see that the malicious content has not been executed.


A more general solution defines the replacement values inside userdata, which can then be overloaded for different documents. Here's the skeleton:

\startluacode
userdata = userdata or {}

userdata.TextReplacements = {}

local function TextReplacement( text )
  context( lpeg.replacer( userdata.TextReplacements ):match( text ) )
end

interfaces.implement {
  name      = "TextReplacement",
  arguments = { "string" },
  public    = true,
  actions   = TextReplacement,
}
\stopluacode

Then, elsewhere, flesh out the replacements, such as:

\startluacode
userdata = userdata or {}

userdata.TextReplacements = { 
  [1] = { "McGenius", "\\Mac Genius" },
  [2] = { "a.m.", "\\cap{am}" },
  [3] = { "p.m.", "\\cap{pm}" },
}
\stopluacode

Using this approach, the replacement texts can vary depending on the needs of a particular document.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .