snipsnap script macro and python
_This
article is also [posted at
snipsnap.org](http://snipsnap.org/space/script+macro+and+python)._
### It's a {script}, {script} world
I recently tried out SnipSnap's {script} macro, which runs
[Python](http://python.org/) code inside SnipSnap. It uses
[Jython](http://www.jython.org/) 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!
### Can't it _do_ anything?
Recently, a [friend](http://www-cs-students.stanford.edu/~nstoll/) asked me if I
could write a simple webapp on [snarfed.org](http://snarfed.org/), my
SnipSnap-based site. Unfortunately, I hesitated, and for good reason. I've
written my fair share of [SnipSnap macros](snipsnap_macros), and I like the
macro architecture, but it's not designed for interactive webapps. I considered
whipping up some JSPs and serving them through
[Jetty](http://mortbay.org/jetty/), but that's far from lightweight, and I'm far
from a JSP expert. I have [friends](http://maulik.net/) who are
[wizards](http://maulik.freeshell.net/Miki.pl?page=Reading+Log.miki)
[with](http://maulik.freeshell.net/Miki.pl?page=Sample+Album+Script.miki)
[AJAX](http://maulik.freeshell.net/Miki.pl?page=2005-08-14.miki), but JavaScript
can't get very far without access to a backing store.
### Flip the switch
Fortunately, I remembered my job desscription as SnipSnap admin and thought of
SnipSnap's {script} macro. I first tried the canonical "Hello world" script:
{script}
print 'Hello world!'
{script}
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:
#org.snipsnap.render.macro.ScriptMacro
You can uncomment this line and rebuild snipsnap-servlets.jar, but it's probably
easier to just download my
[script.jar](script.jar),
which just has ScriptMacro.class and an uncommented org.radeox.macro.Macro file,
put it in your WEB-INF/lib directory, and restart SnipSnap.
After I did that, the hello world script actually worked. Holy executable code,
Batman!
### Down the rabbit hole
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:
{script}
f = open('foo.bar', 'w')
f.write('qwert')
f.close()
f = open('foo.bar')
print f.read()
f.close()
{script}
Great! It could talk to the server file system. The script output _qwert_, as
expected. Next, I tried network connectivity.
{script}
from urllib import urlopen
page = urlopen('http://asdf.com/')
print page.read()
{script}
Sweet! It printed the (surprisingly small) HTML for the
[asdf.com](http://asdf.com/) home page.
### I know where you live
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:
PythonInterpreter interp = new PythonInterpreter();
interp.setOut(writer);
interp.set("snip",
params.getSnipRenderContext().getSnip());
Aha! Sure enough, this code snippet prints out the name of the outer snip:
{script}
print snip
{script}
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...
{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}
This script prints the snip name _and_ the file names of each attachment. Ah,
now we're getting somewhere!
### Wait a minute...
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 [live
server](http://snarfed.org/), 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.
I quickly turned {script} off, ran [chkrootkit](http://www.chkrootkit.org/), 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.
### But...! but...!
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
[PHP](http://php.net/), [.NET](http://www.microsoft.com/net/),
[J2EE](http://java.sun.com/j2ee/), and [Rails](http://www.rubyonrails.com/).
However, it has the notable advantage that you can develop an entire webapp,
from start to finish, _on the web site itself_, with nothing more than your
browser. I don't know of any other webapp platforms that can claim that!
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.
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.
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.
[BeanShell](http://www.snipsnap.org/space/BeanShell) might be worth a look, but
at first glance, I'm guessing it's similarly wide open.
Got any ideas? If so, please [let me know](about)!