Skip to content

gloriousfutureio/secure-string-context

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Coverage Status
secure-string-context

Secure String Context

A Scala library for pre-processing certain types of values during string interpolation to ensure all values can be shown securely, and checked at compile-time.

Summary

It's as simple as:

import safe.strings._

val name = "Jeff"
println(ss"Hello my name is ${safe(name)}!")
println(safe"Hello my name is ${safe(name)}!")

This will print Hello my name is Jeff! twice

Note that ss (aka safe) is identical to the s string interpolation, however, it will not print values that are not wrapped in the safe method by default.

Some types can be made to be implicitly Safe, so that you don't need to wrap them in the safe method. And all types can be forcibly stringified with the raw method.

Compiles value: Any value: [T: CanBeSafe] value: [T: Safe]
ss"$value" X X
ss"${safe(value)}" X
ss"${raw(value)}"

Usage

To declare whole types to be safe to print without using the safe keyword, just create an implicit instance of [[Safe]] for that type.

For example:

val user = User(name = "Jeff", password = "12345")  // Hey, it's easy to remember
println(ss"User created successfully: $user")
                                          ^ compile error

In order to use this type of value, you must define an implicit instance of Safe[User]. Typically, it is best to put this on the companion object so that you never have to import it.

case class User(name: String, password: String)

object User {
  implicit val safe: Safe[User] = new Safe[User] {
    override def write(value: User): String = {
      value.map(password = encrypt(value.password)).toString
    }
  }
}

And voila! Now when printing an instance of User in a secure string, the Safe[User] will be used to ensure that the password is encrypted before being printed to a log or something.

And now this will print the user with an encrypted password field:

println(ss"User created successfully: $user")

Advanced Usage Patterns

You can chain Safe types together so that you can reuse the serialization logic and be checked at compile-time.

For example:

trait Encrypted[T] {
  def bytes: Array[Byte]
  def decrypt(implicit service: DecryptionService): T
}

object Encrypted {

  implicit def SafeEncrypted[T](value: Encrypted[T]): Safe[Encrypted[T]] = {
    new Safe[T] {
      override def write(value: Encrypted[T]): String = new String(value.bytes, "UTF-8")
    }
  }
}

And now any value wrapped with Encrypted will be stringifyed as UTF-8 encoded / encrypted bytes. Alternatively, you could stringify it with a common log encryption key or whatever suits your fancy.

The main point is that you can reuse the Safe typeclass instances to print these values.

case class User(name: String, password: Encrypted[String])

About

A Scala library for encrypted string interpolation

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages