Skip to content

Commit 626f33f

Browse files
Lenny HalsethLenny Halseth
authored andcommitted
Merge branch 'dev/java-parse' into dev/llc
2 parents 89f9b56 + f4c83f3 commit 626f33f

20 files changed

+1583
-36
lines changed

build.sbt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ lazy val CodePulse = Project("CodePulse", file("codepulse"))
189189
Dependencies.akka, Dependencies.reactive,
190190
Dependencies.commons.io, Dependencies.commons.lang,
191191
Dependencies.concLinkedHashMap, Dependencies.juniversalchardet, Dependencies.dependencyCheckCore,
192-
Dependencies.slick, Dependencies.h2
192+
Dependencies.slick, Dependencies.h2,
193+
Dependencies.javaparser
193194
) ++ Dependencies.asm ++ Dependencies.jackson ++ Dependencies.jna ++ Dependencies.logging
194195
)

codepulse/src/main/resources/toserve/pages/projects/projects.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ $(document).ready(function(){
248248
selection.refresh = true
249249

250250
sourceView.setSourceView(mode, source)
251-
sourceView.scrollToLine(selection.metadata.methodStartLine - 1)
251+
sourceView.scrollToLine(selection.metadata.methodStartLine)
252252

253253
coverageUpdateSubscription = Bacon.onValues(Trace.coverageRecords, Trace.activeRecordingsProp, function(coverage, activeRecordings) {
254254
if (selection.refresh) {

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

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ import java.io.InputStream
2525

2626
object AsmVisitors {
2727
// counterCallback(instructionCount)
28-
type CounterCallback = (Int, Int, Int) => Unit
28+
type CounterCallback = (Int, Int) => Unit
2929

3030
// methodCallback(methodSignature, instructionCount)
31-
type MethodCallback = (String, String, Int, Int, Int) => Unit
31+
type MethodCallback = (String, String, Int, Int) => Unit
3232

3333
def parseMethodsFromClass(classBytes: InputStream) = {
3434
val reader = new ClassReader(classBytes)
35-
val builder = List.newBuilder[(String, String, Int, Int, Int)]
36-
val visitor = new ClassStructureVisitor2({ (file, sig, size, lineCount, methodStartLine) => builder += ((file, sig, size, lineCount, methodStartLine)) })
35+
val builder = List.newBuilder[(String, String, Int, Int)]
36+
val visitor = new ClassStructureVisitor2({ (file, sig, size, lineCount) => builder += ((file, sig, size, lineCount)) })
3737
reader.accept(visitor, ClassReader.SKIP_FRAMES)
3838
builder.result()
3939
}
@@ -42,12 +42,10 @@ object AsmVisitors {
4242
class MethodContentVisitor(counterCallback: AsmVisitors.CounterCallback) extends MethodVisitor(Opcodes.ASM5) {
4343
private var instructionCounter = 0
4444
private var lineCounter = 0
45-
private var methodStart = false
46-
private var methodStartLine = -1
4745

4846
def inc() = instructionCounter += 1
49-
override def visitCode = { instructionCounter = 0; lineCounter = 0; methodStart = true; methodStartLine = -1 }
50-
override def visitEnd = { counterCallback(instructionCounter, lineCounter, methodStartLine) }
47+
override def visitCode = { instructionCounter = 0; lineCounter = 0 }
48+
override def visitEnd = { counterCallback(instructionCounter, lineCounter) }
5149

5250
override def visitFieldInsn(opcode: Int, owner: String, name: String, desc: String): Unit = inc()
5351
override def visitIincInsn(v: Int, amt: Int): Unit = inc()
@@ -62,15 +60,7 @@ class MethodContentVisitor(counterCallback: AsmVisitors.CounterCallback) extends
6260
override def visitTableSwitchInsn(min: Int, max: Int, dflt: Label, labels: Label*): Unit = inc()
6361
override def visitTypeInsn(opcode: Int, tp: String): Unit = inc()
6462
override def visitVarInsn(opcode: Int, v: Int): Unit = inc()
65-
override def visitLineNumber(line: Int, start: Label): Unit = {
66-
lineCounter += 1
67-
68-
if(methodStart) {
69-
methodStartLine = line
70-
methodStart = false
71-
}
72-
73-
}
63+
override def visitLineNumber(line: Int, start: Label): Unit = lineCounter += 1
7464
}
7565

7666
class ClassStructureVisitor2(methodCallback: AsmVisitors.MethodCallback) extends ClassVisitor(Opcodes.ASM5) {
@@ -87,8 +77,8 @@ class ClassStructureVisitor2(methodCallback: AsmVisitors.MethodCallback) extends
8777

8878
override def visitMethod(access: Int, name: String, desc: String, sig: String, exceptions: Array[String]): MethodVisitor = {
8979
val methodSignature = classSignature + "." + name + ";" + access + ";" + desc
90-
val counterCallback: AsmVisitors.CounterCallback = (insnCount, lineCount, methodStartLine) => {
91-
methodCallback(classFile, methodSignature, insnCount, lineCount, methodStartLine)
80+
val counterCallback: AsmVisitors.CounterCallback = (insnCount, lineCount) => {
81+
methodCallback(classFile, methodSignature, insnCount, lineCount)
9282
}
9383
new MethodContentVisitor(counterCallback)
9484
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2018 Secure Decisions, a division of Applied Visions, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* This material is based on research sponsored by the Department of Homeland
17+
* Security (DHS) Science and Technology Directorate, Cyber Security Division
18+
* (DHS S&T/CSD) via contract number HHSP233201600058C.
19+
*/
20+
package com.secdec.codepulse.data.bytecode.parse
21+
22+
import org.objectweb.asm.{Opcodes => O}
23+
24+
/** Typesafe wrapper around an integer value that represents
25+
* the combination of any number of `Opcodes.ACC_*` values.
26+
*
27+
* Note that not all of the flags provided by this class are
28+
* necessarily used by Code Dx. They are simply mapped 1:1
29+
* from what is found in the javadocs at
30+
* http://asm.ow2.org/asm50/javadoc/user/org/objectweb/asm/Opcodes.html
31+
*
32+
* The `toStringFor...` methods try to choose a "sane" set of
33+
* flags to present for stringification.
34+
*/
35+
case class AccessFlags(bits: Int) extends AnyVal {
36+
37+
@inline def is(thoseBits: Int) = (bits & thoseBits) != 0
38+
39+
def isAbstract = is(O.ACC_ABSTRACT)
40+
def isAnnotation = is(O.ACC_ANNOTATION)
41+
def isBridge = is(O.ACC_BRIDGE)
42+
def isDeprecated = is(O.ACC_DEPRECATED)
43+
def isEnum = is(O.ACC_ENUM)
44+
def isFinal = is(O.ACC_FINAL)
45+
def isInterface = is(O.ACC_INTERFACE)
46+
def isMandated = is(O.ACC_MANDATED)
47+
def isNative = is(O.ACC_NATIVE)
48+
def isPrivate = is(O.ACC_PRIVATE)
49+
def isProtected = is(O.ACC_PROTECTED)
50+
def isPublic = is(O.ACC_PUBLIC)
51+
def isStatic = is(O.ACC_STATIC)
52+
def isStrict = is(O.ACC_STRICT)
53+
def isSuper = is(O.ACC_SUPER)
54+
def isSynchronized = is(O.ACC_SYNCHRONIZED)
55+
def isSynthetic = is(O.ACC_SYNTHETIC)
56+
def isTransient = is(O.ACC_TRANSIENT)
57+
def isVarargs = is(O.ACC_VARARGS)
58+
def isVolatile = is(O.ACC_VOLATILE)
59+
60+
private def stringifyWith(f: (String => Unit) => Unit) = {
61+
val sb = new StringBuilder
62+
var addedFirst = false
63+
def add(s: String): Unit = {
64+
if(addedFirst) sb += ' '
65+
else addedFirst = true
66+
sb append s
67+
}
68+
f(add _)
69+
sb.result()
70+
}
71+
72+
def toStringForClass = stringifyWith { add =>
73+
if(isPublic) add("public")
74+
if(isPrivate) add("private")
75+
if(isProtected) add("protected")
76+
77+
if(isFinal) add("final")
78+
if(isStatic) add("static")
79+
if(isAbstract) add("abstract")
80+
81+
if(isAnnotation) add("@interface")
82+
else if(isEnum) add("enum")
83+
else if(isInterface) add("interface")
84+
else add("class")
85+
}
86+
87+
def toStringForMethod = stringifyWith { add =>
88+
if(isPublic) add("public")
89+
if(isPrivate) add("private")
90+
if(isProtected) add("protected")
91+
92+
if(isFinal) add("final")
93+
if(isStatic) add("static")
94+
if(isAbstract) add("abstract")
95+
if(isNative) add("native")
96+
if(isSynchronized) add("synchronized")
97+
}
98+
99+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2018 Secure Decisions, a division of Applied Visions, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* This material is based on research sponsored by the Department of Homeland
17+
* Security (DHS) Science and Technology Directorate, Cyber Security Division
18+
* (DHS S&T/CSD) via contract number HHSP233201600058C.
19+
*/
20+
package com.secdec.codepulse.data.bytecode.parse
21+
22+
/** Base trait describing "Binary Classes" in a codebase.
23+
* A class signature should typically include a name, and type signature where applicable,
24+
* but the actual details are up to the implementing classes.
25+
* The main focus of this base trait is converting class signatures to and from their "memoized" forms.
26+
* Classes stored in the database will have their "memo" in a dedicated column.
27+
*/
28+
sealed trait BinaryClassSignature {
29+
def codebaseType: CodebaseType
30+
def toMemo: BinaryClassSignatureMemo
31+
}
32+
33+
object BinaryClassSignature {
34+
def fromMemo(memo: BinaryClassSignatureMemo): BinaryClassSignature = {
35+
CodebaseMemoParts.fromRaw(memo.raw) getOrElse { unrecognized(memo) } match {
36+
case CodebaseMemoParts(CodebaseType.Java, 1, body) => JavaBinaryClassSignature.parseMemoV1(body)
37+
case _ => unrecognized(memo)
38+
}
39+
}
40+
41+
@inline private def unrecognized(memo: BinaryClassSignatureMemo) = {
42+
throw new IllegalArgumentException(s"Unrecognized binary class signature memo: ${memo.raw}")
43+
}
44+
}
45+
46+
/** "MEMO" representation of a `BinaryClassDeclaration`.
47+
*
48+
* Technically this is a value class wrapper for String, and it exists
49+
* simply for the purpose of providing a type-level hint about what the
50+
* String contains. Clients should not attempt to interact directly with
51+
* the `raw` data. Instead they should call `BinaryClassDeclaration.fromMemo` to
52+
* convert the memo to a nicer in-memory representation.
53+
*/
54+
final case class BinaryClassSignatureMemo(val raw: String) extends AnyVal
55+
56+
// -------------------------------------------------------------
57+
// JAVA
58+
// -------------------------------------------------------------
59+
60+
case class JavaBinaryClassSignature(
61+
access: AccessFlags,
62+
name: ClassName,
63+
signature: JVMClassSignature
64+
) extends BinaryClassSignature {
65+
66+
def codebaseType: CodebaseType = CodebaseType.Java
67+
68+
def toMemo = new BinaryClassSignatureMemo(CodebaseMemoParts.buildRaw(codebaseType, 1, sb => {
69+
sb append access.bits
70+
sb append ';'
71+
sb append name.slashedName
72+
sb append ';'
73+
sb append JVMSignatureConverter.toString(signature)
74+
}))
75+
76+
override def toString = {
77+
val sb = new StringBuilder
78+
sb append access.toStringForClass
79+
if(sb.nonEmpty) sb += ' '
80+
sb append name.dottedName
81+
if(signature.typeParams.nonEmpty) sb append signature.typeParams.mkString("<", ", ", ">")
82+
sb append " extends " append signature.superClass
83+
if(signature.interfaces.nonEmpty) sb append signature.interfaces.mkString(" implements ", ", ", "")
84+
sb.result()
85+
}
86+
}
87+
88+
object JavaBinaryClassSignature {
89+
def parseMemoV1(body: String) = {
90+
// format is "<access>;<slashedName>;<signature>"
91+
// note that signature will usually have ";" in it, so we limit the split to 3 parts
92+
val Array(accessStr, slashedName, rawSignature) = body.split(";", 3)
93+
JavaBinaryClassSignature(
94+
AccessFlags(accessStr.toInt),
95+
ClassName.fromSlashed(slashedName),
96+
JVMSignatureConverter.fromString[JVMClassSignature](rawSignature)
97+
)
98+
}
99+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2018 Secure Decisions, a division of Applied Visions, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* This material is based on research sponsored by the Department of Homeland
17+
* Security (DHS) Science and Technology Directorate, Cyber Security Division
18+
* (DHS S&T/CSD) via contract number HHSP233201600058C.
19+
*/
20+
package com.secdec.codepulse.data.bytecode.parse
21+
22+
sealed trait BinaryMethodSignature extends MethodSignature {
23+
def toMemo: BinaryMethodSignatureMemo
24+
}
25+
object BinaryMethodSignature {
26+
def fromMemo(memo: BinaryMethodSignatureMemo): BinaryMethodSignature = {
27+
CodebaseMemoParts.fromRaw(memo.raw) getOrElse { unrecognized(memo) } match {
28+
case CodebaseMemoParts(CodebaseType.Java, 1, body) => JavaBinaryMethodSignature.parseMemoV1(body)
29+
case _ => unrecognized(memo)
30+
}
31+
}
32+
33+
@inline private def unrecognized(memo: BinaryMethodSignatureMemo): Nothing = {
34+
throw new IllegalArgumentException(s"Unrecognized binary method signature memo: ${memo.raw}")
35+
}
36+
}
37+
38+
/** "MEMO" representation of a `BinaryMethodSignature`.
39+
*
40+
* Technically this is a value class wrapper for String, and it exists
41+
* simply for the purpose of providing a type-level hint about what the
42+
* String contains. Clients should not attempt to interact directly with
43+
* the `raw` data. Instead, they should call `MethodSignature.fromMemo`
44+
* to convert the memo to a nicer in-memory representation.
45+
*
46+
* @param raw
47+
*/
48+
final case class BinaryMethodSignatureMemo(raw: String) extends AnyVal
49+
50+
// -------------------------------------------------------------
51+
// JAVA
52+
// -------------------------------------------------------------
53+
54+
/** Represents a java method signature found via bytecode inspection.
55+
* Method signatures in this form can be converted to and from "memos"
56+
* in order to be stored in the database.
57+
*/
58+
case class JavaBinaryMethodSignature(
59+
access: AccessFlags,
60+
name: String,
61+
typeSignature: JVMMethodTypeSignature
62+
) extends BinaryMethodSignature {
63+
64+
def codebaseType: CodebaseType = CodebaseType.Java
65+
66+
def simplifiedReturnType = SimplifiedType of typeSignature.returnType
67+
def simplifiedArgumentTypes = typeSignature.paramTypes.map(SimplifiedType.of)
68+
69+
def toMemo = new BinaryMethodSignatureMemo(CodebaseMemoParts.buildRaw(codebaseType, 1, sb => {
70+
sb append access.bits
71+
sb append ';'
72+
sb append name
73+
sb append ';'
74+
sb append JVMSignatureConverter.toString(typeSignature)
75+
}))
76+
77+
override def toString = {
78+
val sb = new StringBuilder
79+
sb append access.toStringForMethod
80+
sb += ' '
81+
if(typeSignature.typeParams.nonEmpty) sb append typeSignature.typeParams.mkString("<", ", ", "> ")
82+
sb append typeSignature.returnType
83+
sb += ' '
84+
sb append name
85+
sb append typeSignature.paramTypes.mkString("(", ", ", ")")
86+
if(typeSignature.exceptionTypes.nonEmpty) sb append typeSignature.exceptionTypes.mkString(" throws ", ", ", "")
87+
sb.result()
88+
}
89+
90+
}
91+
92+
object JavaBinaryMethodSignature {
93+
94+
def parseMemoV1(body: String) = {
95+
// format is "<access>;<name>;<signature>"
96+
// note that signature will usually have ";" in it, so we limit the split to 3 parts
97+
val Array(accessStr, name, rawSignature) = body.split(";", 3)
98+
JavaBinaryMethodSignature(
99+
AccessFlags(accessStr.toInt),
100+
name,
101+
JVMSignatureConverter.fromString[JVMMethodTypeSignature](rawSignature)
102+
)
103+
}
104+
105+
}

0 commit comments

Comments
 (0)