Permalink
Browse files

Merge pull request #1 from sirdarckcat/sirdarckcat-cspnonces

Adding CSP nonces support and introducing an XSS
  • Loading branch information...
2 parents b4c0299 + 3ff90cb commit fc5161df0a6b778471bde879bbc44cb8a9eade59 @sirdarckcat committed on GitHub Dec 27, 2016
Showing with 75 additions and 23 deletions.
  1. +5 −0 app.yaml
  2. +5 −0 appengine_config.py
  3. +4 −0 cron.yaml
  4. +26 −7 guestbook.py
  5. +34 −16 index.html
  6. +1 −0 lib/README.md
View
@@ -11,8 +11,13 @@ handlers:
- url: /bootstrap
static_dir: bootstrap
+- url: /tasks/.*
+ script: guestbook.app
+ login: admin
+
- url: /.*
script: guestbook.app
+
# [END handlers]
# [START libraries]
View
@@ -0,0 +1,5 @@
+# appengine_config.py
+from google.appengine.ext import vendor
+
+# Add any libraries install in the "lib" folder.
+vendor.add('lib')
View
@@ -0,0 +1,4 @@
+cron:
+- description: daily purge
+ url: /tasks/purge
+ schedule: every 24 hours
View
@@ -16,12 +16,14 @@
# [START imports]
import os
+import random
import urllib
from google.appengine.api import users
from google.appengine.ext import ndb
import jinja2
+import simplejson
import webapp2
JINJA_ENVIRONMENT = jinja2.Environment(
@@ -65,12 +67,10 @@ class Greeting(ndb.Model):
class MainPage(webapp2.RequestHandler):
def get(self):
+ random.seed(os.urandom(8))
+ nonce = ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for i in range(8))
guestbook_name = self.request.get('guestbook_name',
DEFAULT_GUESTBOOK_NAME)
- greetings_query = Greeting.query(
- ancestor=guestbook_key(guestbook_name)).order(-Greeting.date)
- greetings = greetings_query.fetch(10)
-
user = users.get_current_user()
if user:
url = users.create_logout_url(self.request.uri)
@@ -81,19 +81,27 @@ def get(self):
template_values = {
'user': user,
- 'greetings': greetings,
'guestbook_name': urllib.quote_plus(guestbook_name),
'url': url,
'url_linktext': url_linktext,
+ 'nonce': nonce,
}
template = JINJA_ENVIRONMENT.get_template('index.html')
+ self.response.headers.add("Content-Security-Policy","script-src 'nonce-%s'; object-src 'none'"%nonce)
self.response.write(template.render(template_values))
# [END main_page]
-
# [START guestbook]
class Guestbook(webapp2.RequestHandler):
+ def get(self):
+ guestbook_name = self.request.get('guestbook_name',
+ DEFAULT_GUESTBOOK_NAME)
+ greetings_query = Greeting.query(
+ ancestor=guestbook_key(guestbook_name)).order(-Greeting.date)
+ greetings = greetings_query.fetch(10)
+ self.response.write(simplejson.dumps(list({"content": g.content} for g in greetings),
+ cls=simplejson.encoder.JSONEncoderForHTML))
def post(self):
# We set the same parent key on the 'Greeting' to ensure each
@@ -118,9 +126,20 @@ def post(self):
# [END guestbook]
+# [START purge]
+class Purge(webapp2.RequestHandler):
+ def post(self):
+ greetings_query = Greeting.query().fetch(1000, keys_only=True)
+ ndb.delete_multi(list(greetings_query))
+
+# [END purge]
+
+
# [START app]
app = webapp2.WSGIApplication([
('/', MainPage),
- ('/sign', Guestbook),
+ ('/guestbook', Guestbook),
+ # Tasks
+ ('/tasks/purge', Purge),
], debug=True)
# [END app]
View
@@ -50,36 +50,54 @@
</div>
<div class="container">
<!-- [START greetings] -->
- {% for greeting in greetings %}
- <div class="row">
- {% if greeting.author %}
- <b>{{ greeting.author.email }}
- {% if user and user.user_id() == greeting.author.identity %}
- (You)
- {% endif %}
- </b> wrote:
- {% else %}
- An anonymous person wrote:
- {% endif %}
- <blockquote>{{ greeting.content }}</blockquote>
+ <div class="row" id="gbrow">
+ <blockquote></blockquote>
</div>
- {% endfor %}
<!-- [END greetings] -->
- <form action="/sign?guestbook_name={{ guestbook_name }}" method="post">
+ <form action="guestbook" method="post" id="gbsubmit">
+ <input value="{{ guestbook_name }}" name="guestbook_name" type="hidden">
<div><textarea name="content" class="input-block-level" rows="3"></textarea></div>
<div><input type="submit" class="btn btn-large btn-primary" value="Sign Guestbook"></div>
</form>
<hr>
- <form>Guestbook name:
- <input value="{{ guestbook_name }}" name="guestbook_name">
+ <form id="gbform">Guestbook name:
+ <input value="{{ guestbook_name }}" name="guestbook_name" id="gbname">
<input type="submit" value="switch">
</form>
<a href="{{ url|safe }}">{{ url_linktext }}</a>
</div>
+ <script nonce="{{ nonce }}">
+ var submit = document.getElementById("gbsubmit");
+ var name = document.getElementById("gbname");
+ var form = document.getElementById("gbform");
+ var row = document.getElementById("gbrow");
+ var rows = [];
+ var table = row.parentNode;
+ function updateGuestbook() {
+ var url = "?guestbook_name="+encodeURIComponent(gbname.value);
+ submit.elements.guestbook_name.value = gbname.value;
+ history.pushState(null, "", url);
+ // Remove old rows.
+ rows.forEach(r=>r.parentNode.removeChild(r));
+ rows=[];
+ fetch("/guestbook" + url, {credentials: "include"}).then(r=>r.json()).then(j=>{
+ j.forEach(m=>{
+ var elem = table.insertBefore(row.cloneNode(true), table.firstChild);
+ rows.push(elem);
+ // Note this is an XSS.
+ elem.getElementsByTagName("blockquote")[0].innerHTML=m.content;
+ });
+ }).catch(e=>{
+ alert("Error loading guestbook comments");
+ });
+ return false;
+ }
+ gbform.onsubmit = window.onload = updateGuestbook;
+ </script>
</body>
</html>
{% endautoescape %}
View
@@ -0,0 +1 @@
+please run `pip install -t . simplejson` on this directory.

0 comments on commit fc5161d

Please sign in to comment.