profile.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package precis
  5. import (
  6. "errors"
  7. "unicode/utf8"
  8. "golang.org/x/text/runes"
  9. "golang.org/x/text/secure/bidirule"
  10. "golang.org/x/text/transform"
  11. "golang.org/x/text/width"
  12. )
  13. var (
  14. errDisallowedRune = errors.New("precis: disallowed rune encountered")
  15. )
  16. var dpTrie = newDerivedPropertiesTrie(0)
  17. // A Profile represents a set of rules for normalizing and validating strings in
  18. // the PRECIS framework.
  19. type Profile struct {
  20. options
  21. class *class
  22. }
  23. // NewIdentifier creates a new PRECIS profile based on the Identifier string
  24. // class. Profiles created from this class are suitable for use where safety is
  25. // prioritized over expressiveness like network identifiers, user accounts, chat
  26. // rooms, and file names.
  27. func NewIdentifier(opts ...Option) *Profile {
  28. return &Profile{
  29. options: getOpts(opts...),
  30. class: identifier,
  31. }
  32. }
  33. // NewFreeform creates a new PRECIS profile based on the Freeform string class.
  34. // Profiles created from this class are suitable for use where expressiveness is
  35. // prioritized over safety like passwords, and display-elements such as
  36. // nicknames in a chat room.
  37. func NewFreeform(opts ...Option) *Profile {
  38. return &Profile{
  39. options: getOpts(opts...),
  40. class: freeform,
  41. }
  42. }
  43. // NewTransformer creates a new transform.Transformer that performs the PRECIS
  44. // preparation and enforcement steps on the given UTF-8 encoded bytes.
  45. func (p *Profile) NewTransformer() *Transformer {
  46. var ts []transform.Transformer
  47. // These transforms are applied in the order defined in
  48. // https://tools.ietf.org/html/rfc7564#section-7
  49. if p.options.foldWidth {
  50. ts = append(ts, width.Fold)
  51. }
  52. for _, f := range p.options.additional {
  53. ts = append(ts, f())
  54. }
  55. if p.options.cases != nil {
  56. ts = append(ts, p.options.cases)
  57. }
  58. ts = append(ts, p.options.norm)
  59. if p.options.bidiRule {
  60. ts = append(ts, bidirule.New())
  61. }
  62. ts = append(ts, &checker{p: p, allowed: p.Allowed()})
  63. // TODO: Add the disallow empty rule with a dummy transformer?
  64. return &Transformer{transform.Chain(ts...)}
  65. }
  66. var errEmptyString = errors.New("precis: transformation resulted in empty string")
  67. type buffers struct {
  68. src []byte
  69. buf [2][]byte
  70. next int
  71. }
  72. func (b *buffers) init(n int) {
  73. b.buf[0] = make([]byte, 0, n)
  74. b.buf[1] = make([]byte, 0, n)
  75. }
  76. func (b *buffers) apply(t transform.Transformer) (err error) {
  77. // TODO: use Span, once available.
  78. x := b.next & 1
  79. b.src, _, err = transform.Append(t, b.buf[x][:0], b.src)
  80. b.buf[x] = b.src
  81. b.next++
  82. return err
  83. }
  84. func (b *buffers) enforce(p *Profile, src []byte) (str []byte, err error) {
  85. b.src = src
  86. // These transforms are applied in the order defined in
  87. // https://tools.ietf.org/html/rfc7564#section-7
  88. // TODO: allow different width transforms options.
  89. if p.options.foldWidth {
  90. // TODO: use Span, once available.
  91. if err = b.apply(width.Fold); err != nil {
  92. return nil, err
  93. }
  94. }
  95. for _, f := range p.options.additional {
  96. if err = b.apply(f()); err != nil {
  97. return nil, err
  98. }
  99. }
  100. if p.options.cases != nil {
  101. if err = b.apply(p.options.cases); err != nil {
  102. return nil, err
  103. }
  104. }
  105. if n := p.norm.QuickSpan(b.src); n < len(b.src) {
  106. x := b.next & 1
  107. n = copy(b.buf[x], b.src[:n])
  108. b.src, _, err = transform.Append(p.norm, b.buf[x][:n], b.src[n:])
  109. b.buf[x] = b.src
  110. b.next++
  111. if err != nil {
  112. return nil, err
  113. }
  114. }
  115. if p.options.bidiRule {
  116. if err := b.apply(bidirule.New()); err != nil {
  117. return nil, err
  118. }
  119. }
  120. c := checker{p: p}
  121. if _, err := c.span(b.src, true); err != nil {
  122. return nil, err
  123. }
  124. if p.disallow != nil {
  125. for i := 0; i < len(b.src); {
  126. r, size := utf8.DecodeRune(b.src[i:])
  127. if p.disallow.Contains(r) {
  128. return nil, errDisallowedRune
  129. }
  130. i += size
  131. }
  132. }
  133. // TODO: Add the disallow empty rule with a dummy transformer?
  134. if p.options.disallowEmpty && len(b.src) == 0 {
  135. return nil, errEmptyString
  136. }
  137. return b.src, nil
  138. }
  139. // Append appends the result of applying p to src writing the result to dst.
  140. // It returns an error if the input string is invalid.
  141. func (p *Profile) Append(dst, src []byte) ([]byte, error) {
  142. var buf buffers
  143. buf.init(8 + len(src) + len(src)>>2)
  144. b, err := buf.enforce(p, src)
  145. if err != nil {
  146. return nil, err
  147. }
  148. return append(dst, b...), nil
  149. }
  150. // Bytes returns a new byte slice with the result of applying the profile to b.
  151. func (p *Profile) Bytes(b []byte) ([]byte, error) {
  152. var buf buffers
  153. buf.init(8 + len(b) + len(b)>>2)
  154. b, err := buf.enforce(p, b)
  155. if err != nil {
  156. return nil, err
  157. }
  158. if buf.next == 0 {
  159. c := make([]byte, len(b))
  160. copy(c, b)
  161. return c, nil
  162. }
  163. return b, nil
  164. }
  165. // String returns a string with the result of applying the profile to s.
  166. func (p *Profile) String(s string) (string, error) {
  167. var buf buffers
  168. buf.init(8 + len(s) + len(s)>>2)
  169. b, err := buf.enforce(p, []byte(s))
  170. if err != nil {
  171. return "", err
  172. }
  173. return string(b), nil
  174. }
  175. // Compare enforces both strings, and then compares them for bit-string identity
  176. // (byte-for-byte equality). If either string cannot be enforced, the comparison
  177. // is false.
  178. func (p *Profile) Compare(a, b string) bool {
  179. a, err := p.String(a)
  180. if err != nil {
  181. return false
  182. }
  183. b, err = p.String(b)
  184. if err != nil {
  185. return false
  186. }
  187. // TODO: This is out of order. Need to extract the transformation logic and
  188. // put this in where the normal case folding would go (but only for
  189. // comparison).
  190. if p.options.ignorecase {
  191. a = width.Fold.String(a)
  192. b = width.Fold.String(a)
  193. }
  194. return a == b
  195. }
  196. // Allowed returns a runes.Set containing every rune that is a member of the
  197. // underlying profile's string class and not disallowed by any profile specific
  198. // rules.
  199. func (p *Profile) Allowed() runes.Set {
  200. if p.options.disallow != nil {
  201. return runes.Predicate(func(r rune) bool {
  202. return p.class.Contains(r) && !p.options.disallow.Contains(r)
  203. })
  204. }
  205. return p.class
  206. }
  207. type checker struct {
  208. p *Profile
  209. allowed runes.Set
  210. beforeBits catBitmap
  211. termBits catBitmap
  212. acceptBits catBitmap
  213. }
  214. func (c *checker) Reset() {
  215. c.beforeBits = 0
  216. c.termBits = 0
  217. c.acceptBits = 0
  218. }
  219. func (c *checker) span(src []byte, atEOF bool) (n int, err error) {
  220. for n < len(src) {
  221. e, sz := dpTrie.lookup(src[n:])
  222. d := categoryTransitions[category(e&catMask)]
  223. if sz == 0 {
  224. if !atEOF {
  225. return n, transform.ErrShortSrc
  226. }
  227. return n, errDisallowedRune
  228. }
  229. if property(e) < c.p.class.validFrom {
  230. if d.rule == nil {
  231. return n, errDisallowedRune
  232. }
  233. doLookAhead, err := d.rule(c.beforeBits)
  234. if err != nil {
  235. return n, err
  236. }
  237. if doLookAhead {
  238. c.beforeBits &= d.keep
  239. c.beforeBits |= d.set
  240. // We may still have a lookahead rule which we will require to
  241. // complete (by checking termBits == 0) before setting the new
  242. // bits.
  243. if c.termBits != 0 && (!c.checkLookahead() || c.termBits == 0) {
  244. return n, err
  245. }
  246. c.termBits = d.term
  247. c.acceptBits = d.accept
  248. n += sz
  249. continue
  250. }
  251. }
  252. c.beforeBits &= d.keep
  253. c.beforeBits |= d.set
  254. if c.termBits != 0 && !c.checkLookahead() {
  255. return n, errContext
  256. }
  257. n += sz
  258. }
  259. if m := c.beforeBits >> finalShift; c.beforeBits&m != m || c.termBits != 0 {
  260. err = errContext
  261. }
  262. return n, err
  263. }
  264. func (c *checker) checkLookahead() bool {
  265. switch {
  266. case c.beforeBits&c.termBits != 0:
  267. c.termBits = 0
  268. c.acceptBits = 0
  269. case c.beforeBits&c.acceptBits != 0:
  270. default:
  271. return false
  272. }
  273. return true
  274. }
  275. // TODO: we may get rid of this transform if transform.Chain understands
  276. // something like a Spanner interface.
  277. func (c checker) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
  278. short := false
  279. if len(dst) < len(src) {
  280. src = src[:len(dst)]
  281. atEOF = false
  282. short = true
  283. }
  284. nSrc, err = c.span(src, atEOF)
  285. nDst = copy(dst, src[:nSrc])
  286. if short && (err == transform.ErrShortSrc || err == nil) {
  287. err = transform.ErrShortDst
  288. }
  289. return nDst, nSrc, err
  290. }