From e0c14fb18f4ecf7762b9112ab3cb11c3c25d914b Mon Sep 17 00:00:00 2001 From: thosakwe Date: Thu, 12 Jan 2017 18:57:13 -0500 Subject: [PATCH] 0.0.0-alpha --- .gitignore | 2 + .travis.yml | 1 + README.md | 14 +++++ lib/angel_security.dart | 4 ++ lib/src/sanitize.dart | 50 +++++++++++++++++ pubspec.yaml | 12 ++++ test/sanitize_test.dart | 121 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 204 insertions(+) create mode 100644 .travis.yml create mode 100644 lib/angel_security.dart create mode 100644 lib/src/sanitize.dart create mode 100644 pubspec.yaml create mode 100644 test/sanitize_test.dart diff --git a/.gitignore b/.gitignore index 7c280441..2aa48381 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ doc/api/ # Don't commit pubspec lock file # (Library packages only! Remove pattern if developing an application package) pubspec.lock + +log.txt \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..de2210c9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: dart \ No newline at end of file diff --git a/README.md b/README.md index 4d56adea..329c2ea0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,16 @@ # security +[![version 0.0.0](https://img.shields.io/badge/pub-v0.0.0--alpha-red.svg)](https://pub.dartlang.org/packages/angel_security) +[![build status](https://travis-ci.org/angel-dart/security.svg)](https://travis-ci.org/angel-dart/security) + Angel middleware designed to enhance application security. + +Currently far from finished, with incomplete code coverage - **USE AT YOUR OWN RISK!!!** + +## Sanitizing HTML + +```dart +app.before.add(sanitizeHtmlInput()); + +// Or: +app.chain(sanitizeHtmlInput()).get(...) +``` \ No newline at end of file diff --git a/lib/angel_security.dart b/lib/angel_security.dart new file mode 100644 index 00000000..08722d75 --- /dev/null +++ b/lib/angel_security.dart @@ -0,0 +1,4 @@ +/// Angel middleware designed to enhance application security. +library angel_security; + +export 'src/sanitize.dart'; \ No newline at end of file diff --git a/lib/src/sanitize.dart b/lib/src/sanitize.dart new file mode 100644 index 00000000..549868f7 --- /dev/null +++ b/lib/src/sanitize.dart @@ -0,0 +1,50 @@ +import 'dart:convert'; +import 'package:angel_framework/angel_framework.dart'; + +final Map DEFAULT_SANITIZERS = { + new RegExp( + r'<\s*s\s*c\s*r\s*i\s*p\s*t\s*>.*<\s*\/\s*s\s*c\s*r\s*i\s*p\s*t\s*>', + caseSensitive: false): '' +}; + +/// Mitigates XSS risk by sanitizing user HTML input. +/// +/// You can also provide a Map of patterns to [replace]. +RequestMiddleware sanitizeHtmlInput( + {bool body: true, + bool query: true, + Map replace: const {}}) { + var sanitizers = {}..addAll(DEFAULT_SANITIZERS)..addAll(replace ?? {}); + + return (RequestContext req, res) async { + if (body) _sanitizeMap(req.body, sanitizers); + if (query) _sanitizeMap(req.query, sanitizers); + return true; + }; +} + +_sanitize(v, Map sanitizers) { + if (v is String) { + var str = v; + + sanitizers.forEach((needle, replace) { + str = str.replaceAll(needle, replace); + }); + + return HTML_ESCAPE.convert(str); + } else if (v is Map) { + _sanitizeMap(v, sanitizers); + return v; + } else if (v is Iterable) { + bool isList = v is List; + var mapped = v.map((x) => _sanitize(x, sanitizers)); + return isList ? mapped.toList() : mapped; + } else + return v; +} + +void _sanitizeMap(Map data, Map sanitizers) { + data.forEach((k, v) { + data[k] = _sanitize(v, sanitizers); + }); +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..379eb9c5 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,12 @@ +name: angel_security +version: 0.0.0-alpha +description: Angel middleware designed to enhance application security. +author: Tobe O +homepage: https://github.com/angel-dart/security +dependencies: + angel_framework: ^1.0.0-dev +dev_dependencies: + angel_diagnostics: ^1.0.0-dev + angel_validate: ^1.0.0-beta + angel_test: ^1.0.0-dev + test: ^0.12.0 \ No newline at end of file diff --git a/test/sanitize_test.dart b/test/sanitize_test.dart new file mode 100644 index 00000000..316f5778 --- /dev/null +++ b/test/sanitize_test.dart @@ -0,0 +1,121 @@ +import 'dart:io'; +import 'package:angel_diagnostics/angel_diagnostics.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_security/angel_security.dart'; +import 'package:angel_test/angel_test.dart'; +import 'package:angel_validate/server.dart'; +import 'package:matcher/matcher.dart'; +import 'package:test/test.dart'; + +final Validator untrustedSchema = new Validator({'html*': isString}); + +main() async { + Angel app; + TestClient client; + + setUp(() async { + app = new Angel() + ..chain(validate(untrustedSchema)) + .chain(sanitizeHtmlInput()) + .post('/untrusted', (RequestContext req, ResponseContext res) async { + String untrusted = req.body['html']; + res + ..contentType = ContentType.HTML + ..write(''' + + + + Potential Security Hole + + $untrusted + '''); + }) + ..chain(validate(untrustedSchema)).post('/attribute', + (RequestContext req, ResponseContext res) async { + String untrusted = req.body['html']; + res + ..contentType = ContentType.HTML + ..write(''' + + + + Potential Security Hole + + + + + '''); + }); + + await app.configure(logRequests(new File('log.txt'))); + client = await connectTo(app); + }); + + tearDown(() => client.close()); + + group('script tag', () { + test('normal', () async { + var xss = ""; + var response = await client.post('/untrusted', body: {'html': xss}); + print(response.body); + expect(response.body.contains(xss), isFalse); + expect(response.body.toLowerCase().contains('"; + var response = await client.post('/untrusted', body: {'html': xss}); + print(response.body); + expect(response.body.contains(xss), isFalse); + expect(response.body.toLowerCase().contains('"'; + var response = await client.post('/attribute', body: {'html': xss}); + print(response.body); + expect(response.body.contains(xss), isFalse); + expect(response.body.toLowerCase().contains('