Please note: this is not an official Google product.
This scaffold is for users of App Engine's Python 2.7 runtime. For websites deployed to the Python 3 runtime, please see Secure Scaffold for Python 3.
This contains a boilerplate AppEngine application meant to provide a secure base on which to build additional functionality. Structure:
Javascript resources for your application can be written using Closure, and compiled by Google's Closure Compiler (detailed below in the dependencies section).
The scaffold provides the following basic security guarantees by default through
a set of base classes found in src/base/handlers.py
. These handlers:
_SetCommonResponseHeaders()
and
SetAjaxResponseHeaders()
.render()
, render_json()
methods in BaseHandler
and
BaseAjaxHandler
. For contextual autoescaping, you should use Closure
Templates in strict mode (https://developers.google.com/closure/templates/docs/security).dispatch()
method in BaseCronHandler
and BaseTaskHandler
._RequestContainsValidXsrfToken()
method for more information.In addition to the protections above, the scaffold monkey patches assorted APIs
that use insecure or dangerous defaults (see src/base/api_fixer.py
).
Obviously no framework is perfect, and the flexibility of Python offers many ways for a motivated developer to circumvent the protections offered. Under the assumption that developers are not malicious, using the scaffold should centralize many security mechanisms, provide safe defaults, and structure the code in a way that facilitates security review.
Sample implementations can be found in src/handlers.py
. These demonstrate
basic functionality, and should be removed / replaced by code specific to
your application.
These instructions have been tested with the following software:
From the root of the repository:
git submodule init
git submodule update
cd closure-compiler
- refer to closure-compiler/README.md on how to build
the compiler. Feel free to use this GWT-skipping variant:
mvn -pl externs/pom.xml,pom-main.xml,pom-main-shaded.xml
cd ../closure-templates && mvn && cd ..
npm install
mkdir $HOME/bin; cd $HOME/bin
npm install grunt-cli
sudo npm install -g grunt-cli
will install system-wide
and you may skip the next step.export PATH=$HOME/bin/node_modules/grunt-cli/bin:$PATH
To install dependencies for unit testing:
sudo easy_install pip
sudo pip install unittest2
To run unit tests:
python run_tests.py ~/bin/google_appengine src
To run the development appserver locally:
grunt clean
grunt
grunt appengine:run:app
Note that the development appserver will be running on a snapshot of code
at the time you run it. If you make changes, you can run the various Grunt
tasks in order to propagate them to the local appserver. For instance,
grunt copy
will refresh the source code (local and third party), static files,
and templates. grunt closureSoys
and/or grunt closureBuilder
will rebuild
the templates or your provided Javascript and the updated versions will be
written in the output directory.
To deploy to AppEngine:
grunt clean
grunt --appid=<appid>
grunt appengine:update:app --appid=<appid>
Specifying --appid=
will override any value set in config.json
. You may
modify the config.json
file to avoid having to pass this parameter on
every invocation.
Files in js/
are compiled by the Closure Compiler (if available) and placed in
out/static/app.js
. Included in this compilation pass is the the output of
the closureSoys:js
task (intermediate artifacts: out/generated/js/*.js).
Closure Templates that you provide are also compiled using the Python backend, and are available using the constants.CLOSURE template strategy (the default). The generated source code is stored in out/generated/*.py. To use them, pass the callable template as the first argument to render(), and a dictionary containing the template values as the second argument, e.g.:
from generated import helloworld
[...]
self.render(helloworld.helloWorld, { 'name': 'first last' })
The /static
and /template
directories are replicated in out/
, and the
files in src/
are rebased into out/
(so src/base/foo.py
becomes
out/base/foo.py
).