Skip to content

Commit e2f7837

Browse files
committed
Merge pull request #3 from secdec/feature/jsp-support
Feature/jsp support
2 parents 3f7353d + 8ca56e6 commit e2f7837

18 files changed

+455
-46
lines changed

codepulse/src/main/scala/com/secdec/codepulse/data/bytecode/CodeForestBuilder.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ class CodeForestBuilder {
8989
recurse(startNode, treePath.child)
9090
}
9191

92+
def getOrAddJsp(path: List[String], size: Int): CodeTreeNode = {
93+
def recurse(parent: CodeTreeNode, path: List[String]): CodeTreeNode = path match {
94+
case className :: Nil => addChildMethod(parent, className, size)
95+
case packageNode :: rest => recurse(addChildPackage(parent, packageNode), rest)
96+
}
97+
98+
recurse(addRootPackage("JSPs"), path)
99+
}
100+
92101
protected def addRootPackage(name: String) = roots.find { node =>
93102
node.name == name && node.kind == CodeTreeNodeKind.Pkg
94103
} getOrElse {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Code Pulse: A real-time code coverage testing tool. For more information
3+
* see http://code-pulse.com
4+
*
5+
* Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package com.secdec.codepulse.data.jsp
21+
22+
import com.secdec.codepulse.data.bytecode.CodeForestBuilder
23+
import java.io.File
24+
import com.secdec.codepulse.data.bytecode.CodeTreeNode
25+
26+
/** Data adapter to help populate `CodeForestBuilder` with JSP info.
27+
* This implementation is specific to what Jasper does for JSP.
28+
*
29+
* @author robertf
30+
*/
31+
class JasperJspAdapter extends JspAdapter {
32+
private val jsps = List.newBuilder[String]
33+
private val webinfs = List.newBuilder[String]
34+
35+
def addJsp(path: String) = jsps += path
36+
def addWebinf(path: String) = webinfs += path
37+
38+
def build(codeForestBuilder: CodeForestBuilder): List[(String, Int)] = {
39+
val jsps = this.jsps.result
40+
val webinfs = this.webinfs.result
41+
42+
val jspClasses = Set.newBuilder[String]
43+
44+
for (webinf <- webinfs) {
45+
val parent = Option(new File(webinf).getParent) getOrElse ""
46+
val parentLen = parent.length
47+
jspClasses ++= jsps
48+
.filter(_.startsWith(parent))
49+
.map(_.substring(parentLen))
50+
}
51+
52+
// build up
53+
jspClasses.result.toList map { clazz =>
54+
val jspClassName = JasperUtils.makeJavaClass(clazz)
55+
val displayName = clazz.split('/').filter(!_.isEmpty).toList
56+
val node = codeForestBuilder.getOrAddJsp(displayName, 10)
57+
jspClassName -> node.id
58+
}
59+
}
60+
}
61+
62+
object JasperJspAdapter {
63+
lazy val default = new JasperJspAdapter
64+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Code Pulse: A real-time code coverage testing tool. For more information
3+
* see http://code-pulse.com
4+
*
5+
* Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package com.secdec.codepulse.data.jsp
21+
22+
import com.secdec.codepulse.data.trace.TreeNodeDataAccess
23+
24+
/** Handles mapping of Jasper JSP method signatures down to their JSP node.
25+
*
26+
* @param basePackage the base package for the JSPs (e.g., org.apache.jsp)
27+
* @param nodeData `TreeNodeDataAccess` to map against
28+
*
29+
* @author robertf
30+
*/
31+
class JasperJspMapper(basePackage: String, nodeData: TreeNodeDataAccess) extends JspMapper {
32+
private val basePrefix = s"${basePackage.replace('.', '/')}/"
33+
private lazy val basePrefixLength = basePrefix.length
34+
35+
def map(signature: String): Option[Int] = if (signature startsWith basePrefix) {
36+
val className = signature.drop(basePrefixLength).takeWhile(_ != '.').replace('/', '.')
37+
nodeData.getNodeIdForJsp(className)
38+
} else None
39+
40+
def getInclusion(jspClass: String) = s"^$basePrefix${jspClass.replace('.', '/')}"
41+
}
42+
43+
object JasperJspMapper {
44+
val DefaultBasePackage = "org.apache.jsp"
45+
def apply(nodeData: TreeNodeDataAccess): JasperJspMapper = apply(DefaultBasePackage, nodeData)
46+
def apply(basePackage: String, nodeData: TreeNodeDataAccess): JasperJspMapper = new JasperJspMapper(basePackage, nodeData)
47+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Code Pulse: A real-time code coverage testing tool. For more information
3+
* see http://code-pulse.com
4+
*
5+
* Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package com.secdec.codepulse.data.jsp
21+
22+
/** Utilities for Jasper interaction. Most of the ideas here are taken from Jasper's
23+
* source (most notably, JspUtil and JspCompilationContext). A lot of the code within
24+
* was simply translated directly from Java to Scala.
25+
*/
26+
private[jsp] object JasperUtils {
27+
private val JavaKeywords = Set(
28+
"abstract", "assert", "boolean", "break", "byte", "case", "catch",
29+
"char", "class", "const", "continue", "default", "do", "double",
30+
"else", "enum", "extends", "final", "finally", "float", "for",
31+
"goto", "if", "implements", "import", "instanceof", "int",
32+
"interface", "long", "native", "new", "package", "private",
33+
"protected", "public", "return", "short", "static", "strictfp",
34+
"super", "switch", "synchronized", "this", "throw", "throws",
35+
"transient", "try", "void", "volatile", "while")
36+
37+
def isJavaKeyword(key: String) = JavaKeywords.contains(key)
38+
39+
def mangleChar(ch: Char) = {
40+
val result = new Array[Char](5)
41+
result(0) = '_'
42+
result(1) = Character.forDigit((ch >> 12) & 0xf, 16)
43+
result(2) = Character.forDigit((ch >> 8) & 0xf, 16)
44+
result(3) = Character.forDigit((ch >> 4) & 0xf, 16)
45+
result(4) = Character.forDigit((ch) & 0xf, 16)
46+
new String(result)
47+
}
48+
49+
def makeJavaIdentifier(identifier: String, periodToUnderscore: Boolean = true) = {
50+
val modifiedIdentifier = new StringBuilder(identifier.length);
51+
if (!Character.isJavaIdentifierStart(identifier charAt 0))
52+
modifiedIdentifier.append('_');
53+
54+
for (ch <- identifier) {
55+
if (Character.isJavaIdentifierPart(ch) &&
56+
(ch != '_' || !periodToUnderscore))
57+
modifiedIdentifier append ch
58+
else if (ch == '.' && periodToUnderscore)
59+
modifiedIdentifier append '_'
60+
else
61+
modifiedIdentifier append mangleChar(ch)
62+
}
63+
64+
if (isJavaKeyword(modifiedIdentifier.toString()))
65+
modifiedIdentifier append '_'
66+
67+
modifiedIdentifier.toString
68+
}
69+
70+
def makeJavaClass(path: String) = path.split('/').filter(!_.isEmpty).map(makeJavaIdentifier(_)) mkString "."
71+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Code Pulse: A real-time code coverage testing tool. For more information
3+
* see http://code-pulse.com
4+
*
5+
* Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package com.secdec.codepulse.data.jsp
21+
22+
import com.secdec.codepulse.data.bytecode.CodeForestBuilder
23+
24+
/** Base trait for an implementation of JSP support.
25+
*
26+
* @author robertf
27+
*/
28+
trait JspAdapter {
29+
def addJsp(path: String): Unit
30+
def addWebinf(path: String): Unit
31+
def build(cfb: CodeForestBuilder): List[(String, Int)]
32+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Code Pulse: A real-time code coverage testing tool. For more information
3+
* see http://code-pulse.com
4+
*
5+
* Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package com.secdec.codepulse.data.jsp
21+
22+
/** Base trait for JSP signature mappers.
23+
*
24+
* @author robertf
25+
*/
26+
trait JspMapper {
27+
def map(signature: String): Option[Int]
28+
def getInclusion(jspClass: String): String
29+
}

codepulse/src/main/scala/com/secdec/codepulse/data/trace/TreeNodeData.scala

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,24 @@ trait TreeNodeDataAccess {
4242

4343
def getNode(id: Int): Option[TreeNode]
4444

45-
def getNodeId(signature: String): Option[Int]
46-
def getNode(signature: String): Option[TreeNode]
45+
def getNodeIdForSignature(signature: String): Option[Int]
46+
def getNodeForSignature(signature: String): Option[TreeNode]
4747

48-
def foreachMapping(f: (String, Int) => Unit): Unit
49-
def iterateMappings[T](f: Iterator[(String, Int)] => T): T
48+
def foreachMethodMapping(f: (String, Int) => Unit): Unit
49+
def iterateMethodMappings[T](f: Iterator[(String, Int)] => T): T
5050

5151
def mapMethodSignature(signature: String, nodeId: Int): Unit
5252
def mapMethodSignatures(signatures: Iterable[(String, Int)]): Unit = signatures foreach { case (signature, nodeId) => mapMethodSignature(signature, nodeId) }
5353

54+
def getNodeIdForJsp(jspClass: String): Option[Int]
55+
def getNodeForJsp(jspClass: String): Option[TreeNode]
56+
57+
def foreachJspMapping(f: (String, Int) => Unit): Unit
58+
def iterateJspMappings[T](f: Iterator[(String, Int)] => T): T
59+
60+
def mapJsp(jspClass: String, nodeId: Int): Unit
61+
def mapJsps(jsps: Iterable[(String, Int)]): Unit = jsps foreach { case (jspPath, nodeId) => mapJsp(jspPath, nodeId) }
62+
5463
def storeNode(node: TreeNode): Unit
5564
def storeNodes(nodes: Iterable[TreeNode]) = nodes foreach storeNode
5665
}

codepulse/src/main/scala/com/secdec/codepulse/data/trace/slick/SlickTreeNodeDataAccess.scala

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ private[slick] class SlickTreeNodeDataAccess(dao: TreeNodeDataDao, db: Database)
4141
dao get id
4242
}
4343

44-
def getNode(sig: String): Option[TreeNode] = db withSession { implicit session =>
45-
dao get sig
44+
def getNodeForSignature(sig: String): Option[TreeNode] = db withSession { implicit session =>
45+
dao getForSignature sig
4646
}
4747

48-
def getNodeId(sig: String): Option[Int] = db withSession { implicit session =>
49-
dao getId sig
48+
def getNodeIdForSignature(sig: String): Option[Int] = db withSession { implicit session =>
49+
dao getIdForSignature sig
5050
}
5151

52-
def foreachMapping(f: (String, Int) => Unit) {
53-
iterateMappings { _.foreach { case (method, nodeId) => f(method, nodeId) } }
52+
def foreachMethodMapping(f: (String, Int) => Unit) {
53+
iterateMethodMappings { _.foreach { case (method, nodeId) => f(method, nodeId) } }
5454
}
5555

56-
def iterateMappings[T](f: Iterator[(String, Int)] => T): T = {
56+
def iterateMethodMappings[T](f: Iterator[(String, Int)] => T): T = {
5757
db withSession { implicit session =>
58-
dao.iterateMappingsWith(f)
58+
dao iterateMethodMappingsWith f
5959
}
6060
}
6161

@@ -67,6 +67,32 @@ private[slick] class SlickTreeNodeDataAccess(dao: TreeNodeDataDao, db: Database)
6767
dao storeMethodSignatures signatures
6868
}
6969

70+
def getNodeForJsp(jspPath: String): Option[TreeNode] = db withSession { implicit session =>
71+
dao getForJsp jspPath
72+
}
73+
74+
def getNodeIdForJsp(jspPath: String): Option[Int] = db withSession { implicit session =>
75+
dao getIdForJsp jspPath
76+
}
77+
78+
def foreachJspMapping(f: (String, Int) => Unit) {
79+
iterateJspMappings { _.foreach { case (method, nodeId) => f(method, nodeId) } }
80+
}
81+
82+
def iterateJspMappings[T](f: Iterator[(String, Int)] => T): T = {
83+
db withSession { implicit session =>
84+
dao iterateJspMappingsWith f
85+
}
86+
}
87+
88+
def mapJsp(jspClass: String, nodeId: Int) = db withTransaction { implicit transaction =>
89+
dao.storeJsp(jspClass, nodeId)
90+
}
91+
92+
override def mapJsps(jsps: Iterable[(String, Int)]) = db withTransaction { implicit transaction =>
93+
dao storeJsps jsps
94+
}
95+
7096
def storeNode(node: TreeNode) = db withTransaction { implicit transaction =>
7197
dao storeNode node
7298
}

0 commit comments

Comments
 (0)