Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added Whitelist and other security measures to template processing. F…

…ixed attribute rendering issues Closes #1262
  • Loading branch information...
commit b18d6fc7fb54d577be8e3dc2461fedda7b418e55 1 parent c5a97b6
@dpp dpp authored
View
2  core/util/src/main/scala/net/liftweb/util/HtmlParser.scala
@@ -39,7 +39,7 @@ trait Html5Writer {
m match {
case null =>
case Null =>
- case md if (null eq md.value) => // issue 807. Don't do empty
+ case md if (null eq md.value) => writeAttributes(md.next, writer)
case up: UnprefixedAttribute => {
writer.append(' ')
writer.append(up.key)
View
30 web/webkit/src/main/scala/net/liftweb/http/LiftRules.scala
@@ -782,6 +782,36 @@ class LiftRules() extends Factory with FormVendor with LazyLoggable {
val resourceForCurrentLoc: FactoryMaker[() => List[ResourceBundle]] =
new FactoryMaker(() => () => DefaultRoutines.resourceForCurrentReq()) {}
+
+ /**
+ * There may be times when you want to entirely control the templating process. You can insert
+ * a function to this factory that will do your custom template resolution. If the PartialFunction
+ * isDefinedAt the given locale/path, then that's the template returned. In this way, you can
+ * return Empty for a template that's not found and the template will not be found. Otherwise,
+ * if the function is not defined for the locale/path pair, the normal templating system will
+ * be used. Also, keep in mind how FactoryMaker can be used... it can be global, per request, etc.
+ */
+ val externalTemplateResolver: FactoryMaker[() => PartialFunction[(Locale, List[String]), Box[NodeSeq]]] =
+ new FactoryMaker(() => (() => Map.empty: PartialFunction[(Locale, List[String]), Box[NodeSeq]])) {}
+
+ /**
+ * There may be times when you want to entirely control the templating process. You can insert a function
+ * that creates a white list of snippets. The white list is the exhaustive list of snippets. The
+ * snippets are class/method pairs. If the partial function is defined and the result is a Full Box,
+ * the function is run. If the Box is an EmptyBox, then the result is a snippet lookup failure. If the
+ * partial function is not defined, then the normal snippet resolution mechanism is used. Please note that
+ * in Scala a Map is PartialFunction and you can create Maps that have a default value using the withDefaultValue
+ * method.
+ */
+ val snippetWhiteList: FactoryMaker[() => PartialFunction[(String, String), Box[NodeSeq => NodeSeq]]] =
+ new FactoryMaker(() => (() => Map.empty: PartialFunction[(String, String), Box[NodeSeq => NodeSeq]])) {}
+
+ /**
+ * This FactoryMaker can be used to disable the little used attributeSnippets
+ */
+ val allowAttributeSnippets: FactoryMaker[() => Boolean] =
+ new FactoryMaker(() => () => true) {}
+
private var _sitemap: Box[SiteMap] = Empty
private var sitemapFunc: Box[() => SiteMap] = Empty
View
41 web/webkit/src/main/scala/net/liftweb/http/LiftSession.scala
@@ -547,6 +547,11 @@ class LiftSession(private[http] val _contextPath: String, val uniqueId: String,
private val sessionVarSync = new Object
/**
+ * Cache the value of allowing snippet attribute processing
+ */
+ private object allowAttributeProcessing extends TransientRequestVar(LiftRules.allowAttributeSnippets.vend())
+
+ /**
* A mapping between pages denoted by RenderVersion and
* functions to execute at the end of the page rendering
*/
@@ -1376,19 +1381,21 @@ class LiftSession(private[http] val _contextPath: String, val uniqueId: String,
} yield res
}
- private def processAttributes(in: MetaData): MetaData = {
+ private def processAttributes(in: MetaData, allow: Boolean): MetaData = {
+ if (!allow) in else {
in match {
case Null => Null
case mine: PrefixedAttribute if (mine.pre == "lift") => {
mine.key match {
- case s if s.indexOf('.') > -1 => findAttributeSnippet(s, processAttributes(in.next), mine)
- case "snippet" => findAttributeSnippet(mine.value.text, processAttributes(in.next))
- case _ => mine.copy(processAttributes(in.next))
+ case s if s.indexOf('.') > -1 => findAttributeSnippet(s, processAttributes(in.next, allow), mine)
+ case "snippet" => findAttributeSnippet(mine.value.text, processAttributes(in.next, allow))
+ case _ => mine.copy(processAttributes(in.next, allow))
}
}
- case notMine => notMine.copy(processAttributes(in.next))
+ case notMine => notMine.copy(processAttributes(in.next, allow))
}
}
+ }
/**
* See if there's a object singleton with the right name
@@ -1578,14 +1585,28 @@ class LiftSession(private[http] val _contextPath: String, val uniqueId: String,
}
}
+ def runWhitelist(snippet: String, cls: String, method: String, kids: NodeSeq)(f: => NodeSeq): NodeSeq = {
+ val pf = LiftRules.snippetWhiteList.vend()
+ val pair = (cls, method)
+ if (pf.isDefinedAt(pair)) {
+ val func = pf(pair)
+ func.map(_.apply(kids)) openOr reportSnippetError(page, snippetName,
+ LiftRules.SnippetFailures.MethodNotFound,
+ NodeSeq.Empty,
+ wholeTag)
+ } else f
+ }
+
val ret: NodeSeq =
try {
- snippetName.map(snippet =>
+
+ snippetName.map{snippet =>
+ val (cls, method) = splitColonPair(snippet)
S.doSnippet(snippet)(
- (S.locateMappedSnippet(snippet).map(_(kids)) or
+ runWhitelist(snippet, cls, method, kids){(S.locateMappedSnippet(snippet).map(_(kids)) or
locSnippet(snippet)).openOr(
S.locateSnippet(snippet).map(_(kids)) openOr {
- val (cls, method) = splitColonPair(snippet)
+
(locateAndCacheSnippet(cls)) match {
// deal with a stateless request when a snippet has
// different behavior in stateless mode
@@ -1715,7 +1736,7 @@ class LiftSession(private[http] val _contextPath: String, val uniqueId: String,
wholeTag)
}
- }))).openOr {
+ })})}.openOr {
reportSnippetError(page, snippetName,
LiftRules.SnippetFailures.NoNameSpecified,
NodeSeq.Empty,
@@ -1907,7 +1928,7 @@ class LiftSession(private[http] val _contextPath: String, val uniqueId: String,
}
case v: Elem =>
- Elem(v.prefix, v.label, processAttributes(v.attributes),
+ Elem(v.prefix, v.label, processAttributes(v.attributes, this.allowAttributeProcessing.is),
v.scope, processSurroundAndInclude(page, v.child): _*)
case pcd: scala.xml.PCData => pcd
View
10 web/webkit/src/main/scala/net/liftweb/http/Templates.scala
@@ -171,13 +171,20 @@ object Templates {
yield better performance. Please don't change this method without chatting with
me first. Thanks! DPP
*/
+
+ val resolver = LiftRules.externalTemplateResolver.vend()
+ val key = (locale, places)
+
+ if (resolver.isDefinedAt(key)) {
+ resolver(key)
+ } else {
val lrCache = LiftRules.templateCache
val cache = if (lrCache.isDefined) lrCache.open_! else NoCache
val parserFunction: InputStream => Box[NodeSeq] =
S.htmlProperties.htmlParser
- val key = (locale, places)
+
val tr = cache.get(key)
if (tr.isDefined) tr
@@ -247,6 +254,7 @@ object Templates {
}
}
}
+}
private def lookForClasses(places: List[String]): Box[NodeSeq] = {
val (controller, action) = places match {
Please sign in to comment.
Something went wrong with that request. Please try again.