Skip to content

Commit

Permalink
lexicon-level identifier validation helpers and test cases (bluesky-s…
Browse files Browse the repository at this point in the history
…ocial#576)

* hardening: permissive tests for handles

* identifers: permissive tests for NSIDs

* identifiers: add DID validation

* identifiers: wrong about domain syntax (duh, 4chan.com)

In short, labels can start with digits except for the final TLD part.
I think that is all for "DNS domains as hostnames which are actually
used in the real world", though there might be other modern things.
Underscores are obviously allowed in DNS for things like SRV records,
but I don't think as "regular hostnames".

Not sure we want this for NSIDs, so not updating that code or tests yet.

* identifiers: ATURI test corner cases

* identifiers: remove TODOs (moved to formalism doc)

* identifiers: small comment typos and corrections

* move around & integrate indentifier hardneing

* fixed up some tests

---------

Co-authored-by: dholms <[email protected]>
  • Loading branch information
bnewbold and dholms authored Mar 17, 2023
1 parent 9af530e commit 228431e
Show file tree
Hide file tree
Showing 36 changed files with 992 additions and 320 deletions.
1 change: 1 addition & 0 deletions packages/dev-env/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@atproto/uri": "*",
"@did-plc/lib": "^0.0.1",
"@did-plc/server": "^0.0.1",
"better-sqlite3": "^7.6.2",
"chalk": "^5.0.1",
"dotenv": "^16.0.1",
"get-port": "^6.1.2",
Expand Down
101 changes: 0 additions & 101 deletions packages/handle/src/index.ts

This file was deleted.

99 changes: 0 additions & 99 deletions packages/handle/tests/handle.test.ts

This file was deleted.

6 changes: 3 additions & 3 deletions packages/handle/README.md → packages/identifier/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Handle
# Identifier

Validation logic for AT handles
Validation logic for AT identifiers - DIDs & Handles

## Usage

```typescript
import * as handle from '@atproto/handle'
import * as identifier from '@atproto/identifier'

isValid('alice.test', ['.test']) // returns true
ensureValid('alice.test', ['.test']) // returns void
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ const base = require('../../jest.config.base.js')

module.exports = {
...base,
displayName: 'Handle',
displayName: 'Identifier',
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@atproto/handle",
"name": "@atproto/identifier",
"version": "0.0.1",
"main": "src/index.ts",
"scripts": {
Expand All @@ -19,7 +19,6 @@
},
"license": "MIT",
"dependencies": {
"@atproto/common": "*",
"@sideway/address": "^5.0.0"
"@atproto/common": "*"
}
}
54 changes: 54 additions & 0 deletions packages/identifier/src/did.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Human-readable constraints:
// - valid W3C DID (https://www.w3.org/TR/did-core/#did-syntax)
// - entire URI is ASCII: [a-zA-Z0-9._:%-]
// - always starts "did:" (lower-case)
// - method name is one or more lower-case letters, followed by ":"
// - remaining identifier can have any of the above chars, but can not end in ":"
// - it seems that a bunch of ":" can be included, and don't need spaces between
// - "%" is used only for "percent encoding" and must be followed by two hex characters (and thus can't end in "%")
// - query ("?") and fragment ("#") stuff is defined for "DID URIs", but not as part of identifier itself
// - "The current specification does not take a position on the maximum length of a DID"
// - in current atproto, only allowing did:plc and did:web. But not *forcing* this at lexico layer
// - hard length limit of 8KBytes
// - not going to validate "percent encoding" here
export const ensureValidDid = (did: string): void => {
// check that all chars are boring ASCII
if (!/^[a-zA-Z0-9._:%-]*$/.test(did)) {
throw new Error(
'Disallowed characters in DID (ASCII letters, digits, and a couple other characters only)',
)
}

const parts = did.split(':')
if (parts.length < 3) {
throw new Error('DID requires prefix, method, and method-specific content')
}

if (parts[0] != 'did') {
throw new Error('DID requires "did:" prefix')
}

if (!/^[a-z]+$/.test(parts[1])) {
throw new Error('DID method must be lower-case letters')
}

if (did.endsWith(':') || did.endsWith('%')) {
throw new Error('DID can not end with ":" or "%"')
}

if (did.length > 8 * 1024) {
throw new Error('DID is far too long')
}
}

export const ensureValidDidRegex = (did: string): void => {
// simple regex to enforce most constraints via just regex and length.
// hand wrote this regex based on above constraints
if (!/^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$/.test(did)) {
throw new Error("DID didn't validate via regex")
}

if (did.length > 8 * 1024) {
throw new Error('DID is far too long')
}
}
Loading

0 comments on commit 228431e

Please sign in to comment.