As Scala Library
Describes how to use recheck as a Scala library.
Install
To install the Scala library of recheck, you should append the following line into your build.sbt.
libraryDependencies += "codes.quine.labs" %% "recheck-core" % "4.5.0"
Usage
ReDoS.check is the only entry point for checking the vulnerability of regular expression.
import codes.quine.labs.recheck.ReDoS
ReDoS.check("^(a|a)*$", "")
The first argument of ReDoS.check is the source which is corresponding to RegExp.prototype.source.
And, the second argument is the flags which is corresponding to RegExp.prototype.flags too.
The third argument is the params to specify parameters for the checker behavior, and the fourth is the token to cancel the checking in the middle.
See this page for detailed information on Parameters value.
The result of ReDoS.check is called Diagnostics.
Diagnostics is a sealed class, and it has three child classes.
Diagnostics.Safemeans the given regular expression seems safe at least in this checking.Diagnostics.Vulnerablemeans vulnerability in the given regular expression is found.Diagnostics.Unknownmeans something wrong happened in checking (timeout, cancel, or error).
See this page for detailed information on Diagnostics value.
Cancel
The fourth argument of ReDoS.check is used to cancel the checking in the middle.
This type is CancellationToken which is created by CancellationTokenSource.
Noting that ReDoS.check blocks the process, you should use this with concurrent libraries like scala.concurrentor cats-effect.
The following example is usage with cats-effect.
import scala.concurrent.duration._
import cats.effect._
import cats.effect.unsafe.implicits.global
import codes.quine.labs.recheck.ReDoS
import codes.quine.labs.recheck.common.CancellationTokenSource
import codes.quine.labs.recheck.common.Parameters
import codes.quine.labs.recheck.diagnostics.Diagnostics
val cancellation = new CancellationTokenSource
val io = for {
fiber <- IO(ReDoS.check("^a+a+$", "", Parameters(), Some(cancellation.token))).start
_ <- (IO.sleep(100.millisecond) *> IO(cancellation.cancel())).start
diagnostics <- fiber.joinWithNever
_ <- IO.println(diagnostics)
} yield ()
io.unsafeRunSync()
There is the timeout parameter to specify timeout seconds.
Please use this instead of the manual way.
The above example does not work with cats-effect better actually, because fiber has cancel method and it does not work with this method.
To fix this problem, we can use IO.async. The following is the complete example. However, by cats-effect restriction, it will lose a result diagnostics when a fiber is cancelled.
import scala.concurrent.duration._
import cats.effect._
import cats.effect.unsafe.implicits.global
import codes.quine.labs.recheck.ReDoS
import codes.quine.labs.recheck.common.CancellationTokenSource
import codes.quine.labs.recheck.common.Parameters
import codes.quine.labs.recheck.diagnostics.Diagnostics
def cancelableCheck(source: String, flags: String, params: Parameters): IO[Diagnostics] =
IO.executionContext.flatMap { ec =>
IO.async { cb =>
IO {
val cancellation = new CancellationTokenSource
ec.execute(() => {
val diagnostics = ReDoS.check(source, flags, params, Some(cancellation.token))
cb(Right(diagnostics))
})
Some(IO(cancellation.cancel()))
}
}
}
val io = for {
fiber <- cancelableCheck("^a+a+$", "", Parameters()).start
_ <- (IO.sleep(100.millisecond) *> fiber.cancel).start
diagnostics <- fiber.join
_ <- IO.println(diagnostics)
} yield ()
io.unsafeRunSync()