Skip to content

Commit

Permalink
in the encrypt scalafix, make the selection of an unnamed key paramet…
Browse files Browse the repository at this point in the history
…er less specific so it works in more cases; refactor
  • Loading branch information
bpholt committed Feb 2, 2024
1 parent 1892deb commit b12f869
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object SeveralDifferentUses {
// all arguments set
alg.encrypt(key, chunkSize, Option("filename"), Encryption.Aes256, Compression.Bzip2, PgpLiteralDataPacketFormat.Utf8)
alg.encrypt(key, tagChunkSize(42), Option("filename"), Encryption.Aes256, Compression.Bzip2, PgpLiteralDataPacketFormat.Utf8)
alg.encrypt(new PGPPublicKey(null, null), tagChunkSize(42), Option("filename"), Encryption.Aes256, Compression.Bzip2, PgpLiteralDataPacketFormat.Utf8)

// all arguments set with named parameters
alg.encrypt(key = key, chunkSize = chunkSize, fileName = Option("filename"), encryption = Encryption.Aes256, compression = Compression.Bzip2, packetFormat = PgpLiteralDataPacketFormat.Utf8)
Expand Down
20 changes: 18 additions & 2 deletions scalafix/input/src/main/scala/example/ShouldBeIgnored.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,25 @@ package example
rule = com.dwolla.security.crypto.V04to05
*/
trait Foo {
def encrypt(request: Int): Unit
def encrypt(key: String, request: Int): Unit
def armor(): Unit
def tagChunkSize(i: Int): Unit
}

class ShouldBeIgnored(foo: Foo) {
def go(): Unit = foo.encrypt(42)
import foo.*
import Nested.*

foo.armor()
foo.encrypt("key", 42)
foo.encrypt(key = "key", request = 42)
tagChunkSize(42)

CryptoAlg[Option]
}

object Nested {
object CryptoAlg {
def apply[F[_]]: Unit = ???
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ object SeveralDifferentUses {
// all arguments set
alg.encrypt(EncryptionConfig().withChunkSize(chunkSize).withFileName(Option("filename")).withEncryption(Encryption.Aes256).withCompression(Compression.Bzip2).withPacketFormat(PgpLiteralDataPacketFormat.Utf8), key)
alg.encrypt(EncryptionConfig().withChunkSize(ChunkSize(42)).withFileName(Option("filename")).withEncryption(Encryption.Aes256).withCompression(Compression.Bzip2).withPacketFormat(PgpLiteralDataPacketFormat.Utf8), key)
alg.encrypt(EncryptionConfig().withChunkSize(ChunkSize(42)).withFileName(Option("filename")).withEncryption(Encryption.Aes256).withCompression(Compression.Bzip2).withPacketFormat(PgpLiteralDataPacketFormat.Utf8), new PGPPublicKey(null, null))

// all arguments set with named parameters
alg.encrypt(EncryptionConfig().withChunkSize(chunkSize).withFileName(Option("filename")).withEncryption(Encryption.Aes256).withCompression(Compression.Bzip2).withPacketFormat(PgpLiteralDataPacketFormat.Utf8), key)
Expand Down
20 changes: 18 additions & 2 deletions scalafix/output/src/main/scala/example/ShouldBeIgnored.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
package example

trait Foo {
def encrypt(request: Int): Unit
def encrypt(key: String, request: Int): Unit
def armor(): Unit
def tagChunkSize(i: Int): Unit
}

class ShouldBeIgnored(foo: Foo) {
def go(): Unit = foo.encrypt(42)
import foo.*
import Nested.*

foo.armor()
foo.encrypt("key", 42)
foo.encrypt(key = "key", request = 42)
tagChunkSize(42)

CryptoAlg[Option]
}

object Nested {
object CryptoAlg {
def apply[F[_]]: Unit = ???
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,20 @@ class V04to05 extends SemanticRule("com.dwolla.security.crypto.V04to05") {
Patch.replaceTree(t, s"CryptoAlg.resource[$name]").atomic
case t@Term.Apply.After_4_6_0(Term.Select(Term.Name(name), Term.Name("armor")), Term.ArgClause(List(), None)) if t.symbol.normalized.value == "com.dwolla.security.crypto.CryptoAlg.armor." =>
Patch.replaceTree(t, s"$name.armor").atomic
case t@Term.Apply.After_4_6_0(Term.Select(Term.Name(_), fun@Term.Name("encrypt")), argClause@Term.ArgClause((keyName@Term.Name(_)) :: additionalArguments, None)) if t.symbol.normalized.value == "com.dwolla.security.crypto.CryptoAlg.encrypt." =>
migrateEncrypt(argClause, fun, additionalArguments, Some(keyName), offset = 1)
case t@Term.Apply.After_4_6_0(Term.Select(Term.Name(_), fun@Term.Name("encrypt")), argClause@Term.ArgClause(arguments, None)) if t.symbol.normalized.value == "com.dwolla.security.crypto.CryptoAlg.encrypt." =>
migrateEncrypt(argClause, fun, arguments, None, offset = 0)
case t@Term.Apply.After_4_6_0(Term.Select(Term.Name(_), fun@Term.Name("encrypt")), argClause@Term.ArgClause(_, _)) if t.symbol.normalized.value == "com.dwolla.security.crypto.CryptoAlg.encrypt." =>
try {
argClause match {
case Term.ArgClause(arguments@Term.Assign(_, _) :: _, None) => // encrypt(…) where all arguments are named parameters
migrateEncrypt(argClause, fun, arguments, None, offset = 0)
case Term.ArgClause(key :: additionalArguments, None) => // encrypt(key, …), i.e. where the unnamed first argument is the key
migrateEncrypt(argClause, fun, additionalArguments, Some(key), offset = 1)
case other =>
throw pathedException(other)(s => new RuntimeException(s"Unexpected argument clause for encrypt at $s"))
}
} catch {
case ex: NoSuchElementException if ex.getMessage == "key not found: key" =>
throw pathedException(fun)(s => new RuntimeException(s"Key not found at $s"))
}
case t@Term.Apply.After_4_6_0(Term.Name("tagChunkSize"), _) if !isEncryptAParent(t) && t.symbol.normalized.value == "com.dwolla.security.crypto.package.tagChunkSize." =>
Patch.replaceToken(t.tokens.head, "ChunkSize")
}.asPatch
Expand All @@ -33,11 +43,11 @@ class V04to05 extends SemanticRule("com.dwolla.security.crypto.V04to05") {
private def migrateEncrypt(t: Term.ArgClause,
fun: Term.Name,
arguments: List[Term],
keyName: Option[Term],
keyValue: Option[Term],
offset: Int,
)
(implicit doc: SemanticDocument): Patch = {
val map = arguments.zipWithIndex.foldLeft(keyName.map("key" -> _).toList) {
val map = arguments.zipWithIndex.foldLeft(keyValue.map("key" -> _).toList) {
case (s, (Term.Assign(Term.Name("key"), term), _)) => s :+ ("key" -> term)
case (s, (Term.Assign(Term.Name("chunkSize"), Term.Apply.After_4_6_0(Term.Name("tagChunkSize"), Term.ArgClause(List(term), _))), _)) =>
s :+ ("ChunkSize" -> term)
Expand All @@ -51,16 +61,7 @@ class V04to05 extends SemanticRule("com.dwolla.security.crypto.V04to05") {
val parameterName = parameter.displayName

s :+ (parameterName.capitalize -> value)
case method: MethodSignature =>
println(method.parameterLists)
fun.pos.input match {
case File(path, _) =>
throw new RuntimeException(s"Unexpected method signature ${info.signature} at $path:${fun.pos.startLine + 1}:${fun.pos.startColumn + 1}${fun.pos.endLine + 1}:${fun.pos.endColumn + 1}")
case VirtualFile(path, _) =>
throw new RuntimeException(s"Unexpected method signature ${info.signature} at $path:${fun.pos.startLine + 1}:${fun.pos.startColumn + 1}${fun.pos.endLine + 1}:${fun.pos.endColumn + 1}")
case _ =>
throw new RuntimeException(s"Unexpected method signature ${info.signature} at ${fun.pos}")
}
case _ => throw pathedException(fun)(s => new RuntimeException(s"Unexpected signature ${info.signature} at $s"))
}
case _ => s
}
Expand Down Expand Up @@ -112,4 +113,15 @@ class V04to05 extends SemanticRule("com.dwolla.security.crypto.V04to05") {

remainder :+ ("key" -> key)
}

private def pathedException(tree: Tree)
(f: String => Exception): Exception =
tree.pos.input match {
case File(path, _) =>
f(s"$path:${tree.pos.startLine + 1}:${tree.pos.startColumn + 1}${tree.pos.endLine + 1}:${tree.pos.endColumn + 1}")
case VirtualFile(path, _) =>
f(s"$path:${tree.pos.startLine + 1}:${tree.pos.startColumn + 1}${tree.pos.endLine + 1}:${tree.pos.endColumn + 1}")
case _ =>
f(tree.pos.toString)
}
}

0 comments on commit b12f869

Please sign in to comment.