<rdf:Description rdf:about="http://snarfed.org/space/snipsnap%20script%20macro%20and%20python">
  <dc:title> snipsnap script macro and python </dc:title>
  <dc:creator> Ryan Barrett &lt;snarfed at ryanb dot org&gt; </dc:creator>
  <dc:date> 2003-01-01T05:00:00Z </dc:date>
  <dc:language> en </dc:language>
  <dc:format> text/html </dc:format>
  <dc:rights> Copyright 2002-2009 Ryan Barrett </dc:rights>

  <content>
    <p><a href="http://snipsnap.org/"><img src="/space/snipsnap.png" alt="snipsnap.png" title="" /></a><a href="http://python.org/"><img src="/space/python_powered.png" alt="/space/python_powered.png" title="" /></a><a href="http://www.jython.org/"><img src="/space/jython.png" alt="/space/jython.png" title="" /></a></p>

<p><em>This
article is also <a href="http://snipsnap.org/space/script+macro+and+python">posted at
snipsnap.org</a>.</em></p>

<h3>It's a {script}, {script} world</h3>

<p>I recently tried out SnipSnap's {script} macro, which runs
<a href="http://python.org/">Python</a> code inside SnipSnap. It uses
<a href="http://www.jython.org/">Jython</a> as an embedded, plug-and-play Python
interpreter. It works great, and promised to turn SnipSnap into a lightweight
webapp development paradise. Unfortunately, I can't use it...but maybe you can
succeed where I failed!</p>

<h3>Can't it <em>do</em> anything?</h3>

<p>Recently, a <a href="http://www-cs-students.stanford.edu/~nstoll/">friend</a> asked me if I
could write a simple webapp on <a href="http://snarfed.org/">snarfed.org</a>, my
SnipSnap-based site. Unfortunately, I hesitated, and for good reason. I've
written my fair share of <a href="/space/snipsnap+macros">Snipsnap
macros</a>, and I like the macro
architecture, but it's not designed for interactive webapps. I considered
whipping up some JSPs and serving them through
<a href="http://mortbay.org/jetty/">Jetty</a>, but that's far from lightweight, and I'm far
from a JSP expert. I have <a href="http://maulik.net/">friends</a> who are
<a href="http://maulik.freeshell.net/Miki.pl?page=Reading+Log.miki">wizards</a>
<a href="http://maulik.freeshell.net/Miki.pl?page=Sample+Album+Script.miki">with</a>
<a href="http://maulik.freeshell.net/Miki.pl?page=2005-08-14.miki">AJAX</a>, but JavaScript
can't get very far without access to a backing store.</p>

<h3>Flip the switch</h3>

<p>Fortunately, I remembered my job desscription as SnipSnap admin and thought of
SnipSnap's {script} macro. I first tried the canonical "Hello world" script:</p>

<div class='p-shadow'><pre><code>{script}
print 'Hello world!'
{script}
</code></pre></div>

<p><br class='clearing' />I quickly noticed that the {script} macro isn't enabled by default. I looked
inside WEB-INF/lib/snipsnap-servlets.jar and found that the ScriptMacro line in
META-INF/services/org.radeox.macro.Macro was commented out:</p>

<div class='p-shadow'><pre><code>#org.snipsnap.render.macro.ScriptMacro
</code></pre></div>

<p><br class='clearing' />You can uncomment this line and rebuild snipsnap-servlets.jar, but it's probably
easier to just download my
<a href="/space/script.jar">script.jar</a>,
which just has ScriptMacro.class and an uncommented org.radeox.macro.Macro file,
put it in your WEB-INF/lib directory, and restart SnipSnap.</p>

<p>After I did that, the hello world script actually worked. Holy executable code,
Batman!</p>

<h3>Down the rabbit hole</h3>

<p>I quickly tested the waters. First, file system access. Any webapp worth its
salt needs a backing store, and while databases are de rigeur, they're also
overkill in many cases. Flat files are often more than good enough. So:</p>

<div class='p-shadow'><pre><code>{script}
f = open('foo.bar', 'w')
f.write('qwert')
f.close()

f = open('foo.bar')
print f.read()
f.close()
{script}
</code></pre></div>

<p><br class='clearing' />Great! It could talk to the server file system. The script output <em>qwert</em>, as
expected. Next, I tried network connectivity.</p>

<div class='p-shadow'><pre><code>{script}
from urllib import urlopen

page = urlopen('http://asdf.com/')
print page.read()
{script}
</code></pre></div>

<p><br class='clearing' />Sweet! It printed the (surprisingly small) HTML for the
<a href="http://asdf.com/">asdf.com</a> home page.</p>

<h3>I know where you live</h3>

<p>At this point, the {script} environment was still fairly isolated from SnipSnap.
My next task was to access basic SnipSnap site information, starting with the
snip that contains the macro. I poked around in the source and noticed this
telltale snippet in ScriptMacro.java:</p>

<div class='p-shadow'><pre><code>PythonInterpreter interp = new PythonInterpreter();

interp.setOut(writer);
interp.set("snip",
           params.getSnipRenderContext().getSnip());
</code></pre></div>

<p><br class='clearing' />Aha! Sure enough, this code snippet prints out the name of the outer snip:</p>

<div class='p-shadow'><pre><code>{script}
print snip
{script}
</code></pre></div>

<p><br class='clearing' />On its own, though, the snip name isn't too useful. The Jython interpreter is
running in the same JVM as SnipSnap, though, so maybe we can sneak in the back
door...</p>

<div class='p-shadow'><pre><code>{script}
import sys
sys.add_package('org.snipsnap.snip')
from org.snipsnap.snip import SnipSpaceFactory

factory = SnipSpaceFactory.getInstance()
ats = factory.load(str(snip)).getAttachments().getAll()
at_names = ', '.join([a.getName() for a in ats])

print """
   This snip is called **%s**.
   Its attachments are **%s**.
""" % (snip, at_names)
{script}
</code></pre></div>

<p><br class='clearing' />This script prints the snip name <em>and</em> the file names of each attachment. Ah,
now we're getting somewhere!</p>

<h3>Wait a minute...</h3>

<p>At this point, something had been nagging at the back of my mind for a while.
I'd ignored it for a while, but by the time I enabled {script} on my <a href="http://snarfed.org/">live
server</a>, it pushed into the front of my mind. Hmm, {script}
lets people run arbitrary code on my server, with filesystem and network access.
Worse, since it needs to bind to port 80, SnipSnap runs as root, so their code
would too. Yow! That's a gaping security hole to open on a production server.</p>

<p>I quickly turned {script} off, ran <a href="http://www.chkrootkit.org/">chkrootkit</a>, and
checked the web server logs to see if any script kiddies had noticed. SnipSnap
isn't exactly a common target, and {script} is probably disabled on all other
SnipSnap sites...but still, I wasn't taking any chances.</p>

<h3>But...! but...!</h3>

<p>This is where I am now. I'd love to be able to use {script} on my site. It turns
SnipSnap into a lightweight webapp development platform, in the same vein as
<a href="http://php.net/">PHP</a>, <a href="http://www.microsoft.com/net/">.NET</a>,
<a href="http://java.sun.com/j2ee/">J2EE</a>, and <a href="http://www.rubyonrails.com/">Rails</a>.
However, it has the notable advantage that you can develop an entire webapp,
from start to finish, <em>on the web site itself</em>, with nothing more than your
browser. I don't know of any other webapp platforms that can claim that!</p>

<p>Of course, it does have drawbacks. Other than pen and paper, a text form in a
web page is just about the worst development environment I can imagine. Sure,
you can code in your favorite text editor and transfer to SnipSnap for testing,
but that's a recipe for version skew madness.</p>

<p>Also, it may be lightweight, but it's not really designed for building webapps.
You'll have to build even the most basic functionality yourself, including
forms, templates, and user input. And don't even think about commonly used
features like sessions, databases, and testing.</p>

<p>And, of course, there's the aforementioned security...or more accurately,
lack of security. Still, I've drunk the SnipSnap kool-aid, and I'd love an easy
way to build webapps on top of it. Until I learn how to lock down and sandbox
it, though, {script} isn't the answer.
<a href="http://www.snipsnap.org/space/BeanShell">BeanShell</a> might be worth a look, but
at first glance, I'm guessing it's similarly wide open.</p>

<p>Got any ideas? If so, please <a href="/space/about">let me know</a>!</p>

<p>See also:</p>

<ul>
<li><a href="/space/snipsnap macros">snipsnap macros</a></li>
<li><a href="/space/snipsnap comment without login patch">snipsnap comment without login patch</a></li>
<li><a href="/space/snipsnap recent-changes snip name patch">snipsnap recent-changes snip name patch</a></li>
<li><a href="/space/snipsnap register without email">snipsnap register without email</a></li>
<li><a href="/space/servlets with snipsnap">servlets with snipsnap</a></li>
<li><a href="/space/virtual host redirection">virtual host redirection</a></li>
<li><a href="/space/snipsnap 1.0b1 virtual hostname patch">snipsnap 1.0b1 virtual hostname patch</a></li>
<li><a href="/space/snipscrape">snipscrape</a></li>
</ul>

  </content>

  <rdf:Seq>

  </rdf:Seq>
</rdf:Description>
</rdf:RDF>
