• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / atala-prism-building-blocks / 7756228356

02 Feb 2024 12:51PM UTC coverage: 31.346% (+0.05%) from 31.294%
7756228356

Pull #854

bvoiturier
ci: pin semantic-pull-request version to 5.2.0 to use Node 16

Signed-off-by: Benjamin Voiturier <benjamin.voiturier@iohk.io>
Pull Request #854: docs(prism-agent): improve Connect OAS documentation

0 of 10 new or added lines in 2 files covered. (0.0%)

334 existing lines in 72 files now uncovered.

4016 of 12812 relevant lines covered (31.35%)

0.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.49
/castor/lib/core/src/main/scala/io/iohk/atala/castor/core/util/UriUtils.scala
1
package io.iohk.atala.castor.core.util
2

3
import io.lemonlabs.uri.{Uri, Url, Urn, QueryString}
4
import io.lemonlabs.uri.config.UriConfig
5
import io.lemonlabs.uri.encoding.PercentEncoder
6
import io.lemonlabs.uri.decoding.UriDecodeException
7

8
// TODO: unify with the logic used in Node
9
// https://github.com/input-output-hk/atala-prism/blob/ba7b3e3ef307f6bd06734af2bf8fed9b119ee98e/prism-backend/common/src/main/scala/io/iohk/atala/prism/utils/UriUtils.scala
10
object UriUtils {
11

12
  /** Normalized URI according to <a
13
    * href="https://www.rfc-editor.org/rfc/rfc3986#section-6">RFC&nbsp;3986,&nbsp;section-6</a>
14
    *
15
    * @param uri
16
    * @return
17
    *   [[Some]](uri) - normalized uri, if it is a valid uri string, or [[None]]
18
    */
1✔
19
  def normalizeUri(uriStr: String): Option[String] = {
20

21
    /*
22
     * List of normalizations performed:
23
     *   percent encoding normalization
24
     *     decode unreserved characters
25
     *   case normalization
26
     *     scheme and host to lowercase
27
     *     all percent encoded triplets use uppercase hexadecimal chars
28
     *   path segment normalization
29
     *     remove "." and ".." segments from path
30
     *     remove duplicate forward slashes (//) from path
31
     *   scheme specific normalization (http, https) since it is likely to be often used type of URL
32
     *     remove default port
33
     *     sort query parameters by key alphabetically
34
     *     remove duplicates (by name/key)
35
     *     encode special characters that are disallowed in path and query
36
     *     decode the ones that are allowed if encoded
37
     *
38
     * for URN:
39
     *   convert to lowercase
40
     *   decode all percent encoded triplets (including unreserved)
41
     *   encode any that need to be encodedparsingutils
42
     *
43
     * URL without a scheme is treated as invalid
44
     */
1✔
45
    implicit val config: UriConfig = UriConfig.default.copy(queryEncoder = PercentEncoder())
46

1✔
47
    try {
48
      // parsing decodes the percent encoded triplets, including unreserved chars
1✔
49
      val parsed = Uri.parse(uriStr)
50
      parsed match {
1✔
51
        case url: Url =>
×
52
          if (url.schemeOption.isEmpty) throw new UriDecodeException("Scheme is empty")
53
          // lowercase schema
×
54
          val schemeNormalized = url.schemeOption.map(_.toLowerCase())
55

56
          // lowercase host if not IP
1✔
57
          val hostNormalized = url.hostOption.map(_.normalize)
58

59
          // removes dot segments and extra //
1✔
60
          val pathNormalized = url.path.normalize(removeEmptyParts = true)
61

62
          // remove unneeded ports
1✔
63
          val portNormalized = url.port.flatMap { (port: Int) =>
64
            schemeNormalized match {
1✔
65
              case Some(scheme) =>
66
                scheme match {
1✔
67
                  case "http"       => if (port == 80) None else Some(port)
1✔
68
                  case "https"      => if (port == 443) None else Some(port)
1✔
UNCOV
69
                  case "ftp"        => if (port == 21) None else Some(port)
×
70
                  case "ws" | "wss" => if (port == 80) None else Some(port)
1✔
71
                  case _            => Some(port)
72
                }
×
73
              case None => Some(port)
74
            }
75
          }
76

77
          val queryNormalized = {
78
            // filter duplicate keys (last one stays) and sort alphabetically (by key)
1✔
79
            val filtered = url.query.params.toMap.toVector.sortBy(_._1)
80
            QueryString(filtered)
81
          }
82

83
          // construction of the instance encodes all special characters that are disallowed in path and query
1✔
84
          val urlNormalized = Url(
1✔
85
            scheme = schemeNormalized.orNull,
1✔
86
            user = url.user.orNull,
×
87
            password = url.password.orNull,
1✔
88
            host = hostNormalized.map(_.toString).orNull,
1✔
89
            port = portNormalized.getOrElse(-1),
1✔
90
            path = pathNormalized.toString,
91
            query = queryNormalized,
1✔
92
            fragment = url.fragment.orNull
93
          )
94

1✔
95
          Some(urlNormalized.toString)
96

1✔
97
        case urn: Urn =>
1✔
98
          Some(urn.toString.toLowerCase)
99
      }
100
    } catch {
×
101
      case _: Exception => None
102
    }
103
  }
104

1✔
105
  def isValidUriString(str: String): Boolean = {
1✔
106
    try {
1✔
107
      Uri.parse(str) match {
1✔
108
        case url: Url => url.schemeOption.nonEmpty
×
109
        case Urn(_)   => true
110
      }
111
    } catch {
×
112
      case _: Exception => false
113
    }
114
  }
115

116
  /** Checks if a string is a valid URI fragment according to <a
117
    * href="https://www.rfc-editor.org/rfc/rfc3986#section-3.5">RFC&nbsp;3986&nbsp;section-3.5</a>
118
    *
119
    * @param str
120
    * @return
121
    *   true if str is a valid URI fragment, otherwise false
122
    */
1✔
123
  def isValidUriFragment(str: String): Boolean = {
124

125
    /*
126
     * Alphanumeric characters (A-Z, a-z, 0-9)
127
     * Some special characters: -._~!$&'()*+,;=:@
128
     * Percent-encoded characters, which are represented by the pattern %[0-9A-Fa-f]{2}
129
     */
1✔
130
    val uriFragmentRegex = "^([A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*$".r
131

132
    // In general, empty URI fragment is a valid fragment, but for our use-case it would be pointless
1✔
133
    str.nonEmpty && uriFragmentRegex.matches(str)
134
  }
135
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc