diff --git a/src/claim.js b/src/claim.js index 645b3dd..5b2c1ef 100644 --- a/src/claim.js +++ b/src/claim.js @@ -43,7 +43,7 @@ class Claim { * const claim = doip.Claim('dns:domain.tld?type=TXT', '123abc123abc'); * const claimAlt = doip.Claim(JSON.stringify(claim)); */ - constructor(uri, fingerprint) { + constructor (uri, fingerprint) { // Import JSON if (typeof uri === 'object' && 'claimVersion' in uri) { const data = uri @@ -58,7 +58,6 @@ class Claim { default: throw new Error('Invalid claim version') - break } return } @@ -73,44 +72,44 @@ class Claim { try { validator.isAlphanumeric(fingerprint) } catch (err) { - throw new Error(`Invalid fingerprint`) + throw new Error('Invalid fingerprint') } } - this._uri = uri ? uri : null - this._fingerprint = fingerprint ? fingerprint : null + this._uri = uri || null + this._fingerprint = fingerprint || null this._status = E.ClaimStatus.INIT this._matches = null this._verification = null } - get uri() { + get uri () { return this._uri } - get fingerprint() { + get fingerprint () { return this._fingerprint } - get status() { + get status () { return this._status } - get matches() { + get matches () { if (this._status === E.ClaimStatus.INIT) { throw new Error('This claim has not yet been matched') } return this._matches } - get verification() { + get verification () { if (this._status !== E.ClaimStatus.VERIFIED) { throw new Error('This claim has not yet been verified') } return this._verification } - set uri(uri) { + set uri (uri) { if (this._status !== E.ClaimStatus.INIT) { throw new Error( 'Cannot change the URI, this claim has already been matched' @@ -126,7 +125,7 @@ class Claim { this._uri = uri } - set fingerprint(fingerprint) { + set fingerprint (fingerprint) { if (this._status === E.ClaimStatus.VERIFIED) { throw new Error( 'Cannot change the fingerprint, this claim has already been verified' @@ -135,15 +134,15 @@ class Claim { this._fingerprint = fingerprint } - set status(anything) { + set status (anything) { throw new Error("Cannot change a claim's status") } - set matches(anything) { + set matches (anything) { throw new Error("Cannot change a claim's matches") } - set verification(anything) { + set verification (anything) { throw new Error("Cannot change a claim's verification result") } @@ -151,7 +150,7 @@ class Claim { * Match the claim's URI to candidate definitions * @function */ - match() { + match () { if (this._status !== E.ClaimStatus.INIT) { throw new Error('This claim was already matched') } @@ -195,7 +194,7 @@ class Claim { * @function * @param {object} [opts] - Options for proxy, fetchers */ - async verify(opts) { + async verify (opts) { if (this._status === E.ClaimStatus.INIT) { throw new Error('This claim has not yet been matched') } @@ -207,7 +206,7 @@ class Claim { } // Handle options - opts = mergeOptions(defaults.opts, opts ? opts : {}) + opts = mergeOptions(defaults.opts, opts || {}) // If there are no matches if (this._matches.length === 0) { @@ -215,7 +214,7 @@ class Claim { result: false, completed: true, proof: {}, - errors: ['No matches for claim'], + errors: ['No matches for claim'] } } @@ -223,9 +222,9 @@ class Claim { for (let index = 0; index < this._matches.length; index++) { const claimData = this._matches[index] - let verificationResult = null, - proofData = null, - proofFetchError + let verificationResult = null + let proofData = null + let proofFetchError try { proofData = await proofs.fetch(claimData, opts) @@ -242,15 +241,15 @@ class Claim { ) verificationResult.proof = { fetcher: proofData.fetcher, - viaProxy: proofData.viaProxy, + viaProxy: proofData.viaProxy } } else { // Consider the proof completed but with a negative result - verificationResult = verificationResult ? verificationResult : { + verificationResult = verificationResult || { result: false, completed: true, proof: {}, - errors: [proofFetchError], + errors: [proofFetchError] } if (this.isAmbiguous()) { @@ -268,12 +267,14 @@ class Claim { } // Fail safe verification result - this._verification = this._verification ? this._verification : { - result: false, - completed: true, - proof: {}, - errors: ['Unknown error'], - } + this._verification = this._verification + ? this._verification + : { + result: false, + completed: true, + proof: {}, + errors: ['Unknown error'] + } this._status = E.ClaimStatus.VERIFIED } @@ -285,16 +286,14 @@ class Claim { * @function * @returns {boolean} */ - isAmbiguous() { + isAmbiguous () { if (this._status === E.ClaimStatus.INIT) { throw new Error('The claim has not been matched yet') } if (this._matches.length === 0) { throw new Error('The claim has no matches') } - return ( - this._matches.length > 1 || this._matches[0].match.isAmbiguous - ) + return this._matches.length > 1 || this._matches[0].match.isAmbiguous } /** @@ -303,14 +302,14 @@ class Claim { * @function * @returns {object} */ - toJSON() { + toJSON () { return { claimVersion: 1, uri: this._uri, fingerprint: this._fingerprint, status: this._status, matches: this._matches, - verification: this._verification, + verification: this._verification } } } diff --git a/src/claimDefinitions/devto.js b/src/claimDefinitions/devto.js index fa77d34..b6ed1b9 100644 --- a/src/claimDefinitions/devto.js +++ b/src/claimDefinitions/devto.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'devto', + name: 'devto' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: `https://dev.to/${match[1]}`, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://dev.to/api/articles/${match[1]}/${match[2]}`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['body_markdown'], - }, + path: ['body_markdown'] + } } } const tests = [ { uri: 'https://dev.to/alice/post', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://dev.to/alice/post/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice/post', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/discourse.js b/src/claimDefinitions/discourse.js index 8945de4..d044aa3 100644 --- a/src/claimDefinitions/discourse.js +++ b/src/claimDefinitions/discourse.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'discourse', + name: 'discourse' }, match: { regularExpression: reURI, - isAmbiguous: true, + isAmbiguous: true }, profile: { display: `${match[2]}@${match[1]}`, uri: uri, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://${match[1]}/u/${match[2]}.json`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['user', 'bio_raw'], - }, + path: ['user', 'bio_raw'] + } } } const tests = [ { uri: 'https://domain.org/u/alice', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/u/alice/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/dns.js b/src/claimDefinitions/dns.js index dcf828f..8487335 100644 --- a/src/claimDefinitions/dns.js +++ b/src/claimDefinitions/dns.js @@ -15,7 +15,7 @@ limitations under the License. */ const E = require('../enums') -const reURI = /^dns:([a-zA-Z0-9\.\-\_]*)(?:\?(.*))?/ +const reURI = /^dns:([a-zA-Z0-9.\-_]*)(?:\?(.*))?/ const processURI = (uri) => { const match = uri.match(reURI) @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'dns', + name: 'dns' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: `https://${match[1]}`, - qr: null, + qr: null }, proof: { uri: null, @@ -41,31 +41,31 @@ const processURI = (uri) => { access: E.ProofAccess.SERVER, format: E.ProofFormat.JSON, data: { - domain: match[1], - }, - }, + domain: match[1] + } + } }, claim: { format: E.ClaimFormat.URI, relation: E.ClaimRelation.CONTAINS, - path: ['records', 'txt'], - }, + path: ['records', 'txt'] + } } } const tests = [ { uri: 'dns:domain.org', - shouldMatch: true, + shouldMatch: true }, { uri: 'dns:domain.org?type=TXT', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/fediverse.js b/src/claimDefinitions/fediverse.js index 434af1a..7ee138c 100644 --- a/src/claimDefinitions/fediverse.js +++ b/src/claimDefinitions/fediverse.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'fediverse', + name: 'fediverse' }, match: { regularExpression: reURI, - isAmbiguous: true, + isAmbiguous: true }, profile: { display: `@${match[2]}@${match[1]}`, uri: uri, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: uri, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.FINGERPRINT, relation: E.ClaimRelation.CONTAINS, - path: ['summary'], - }, + path: ['summary'] + } } } const tests = [ { uri: 'https://domain.org/users/alice', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/users/alice/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/gitea.js b/src/claimDefinitions/gitea.js index 743236d..dab70a8 100644 --- a/src/claimDefinitions/gitea.js +++ b/src/claimDefinitions/gitea.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'gitea', + name: 'gitea' }, match: { regularExpression: reURI, - isAmbiguous: true, + isAmbiguous: true }, profile: { display: `${match[2]}@${match[1]}`, uri: `https://${match[1]}/${match[2]}`, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://${match[1]}/api/v1/repos/${match[2]}/gitea_proof`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.EQUALS, - path: ['description'], - }, + path: ['description'] + } } } const tests = [ { uri: 'https://domain.org/alice/gitea_proof', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice/gitea_proof/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice/other_proof', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/github.js b/src/claimDefinitions/github.js index 57327a2..c90f688 100644 --- a/src/claimDefinitions/github.js +++ b/src/claimDefinitions/github.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'github', + name: 'github' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: `https://github.com/${match[1]}`, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://api.github.com/gists/${match[2]}`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['files', 'openpgp.md', 'content'], - }, + path: ['files', 'openpgp.md', 'content'] + } } } const tests = [ { uri: 'https://gist.github.com/Alice/123456789', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://gist.github.com/Alice/123456789/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/Alice/123456789', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/gitlab.js b/src/claimDefinitions/gitlab.js index 79c5d31..b209021 100644 --- a/src/claimDefinitions/gitlab.js +++ b/src/claimDefinitions/gitlab.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'gitlab', + name: 'gitlab' }, match: { regularExpression: reURI, - isAmbiguous: true, + isAmbiguous: true }, profile: { display: `${match[2]}@${match[1]}`, uri: `https://${match[1]}/${match[2]}`, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { domain: match[1], - username: match[2], - }, - }, + username: match[2] + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.EQUALS, - path: ['description'], - }, + path: ['description'] + } } } const tests = [ { uri: 'https://gitlab.domain.org/alice/gitlab_proof', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://gitlab.domain.org/alice/gitlab_proof/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice/other_proof', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/hackernews.js b/src/claimDefinitions/hackernews.js index efe7a84..c2f7d0a 100644 --- a/src/claimDefinitions/hackernews.js +++ b/src/claimDefinitions/hackernews.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'hackernews', + name: 'hackernews' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: uri, - qr: null, + qr: null }, proof: { uri: `https://hacker-news.firebaseio.com/v0/user/${match[1]}.json`, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://hacker-news.firebaseio.com/v0/user/${match[1]}.json`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.URI, relation: E.ClaimRelation.CONTAINS, - path: ['about'], - }, + path: ['about'] + } } } const tests = [ { uri: 'https://news.ycombinator.com/user?id=Alice', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://news.ycombinator.com/user?id=Alice/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/user?id=Alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/index.js b/src/claimDefinitions/index.js index 42cbc4d..87ac4f8 100644 --- a/src/claimDefinitions/index.js +++ b/src/claimDefinitions/index.js @@ -30,7 +30,7 @@ const list = [ 'mastodon', 'fediverse', 'discourse', - 'owncast', + 'owncast' ] const data = { @@ -50,7 +50,7 @@ const data = { mastodon: require('./mastodon'), fediverse: require('./fediverse'), discourse: require('./discourse'), - owncast: require('./owncast'), + owncast: require('./owncast') } exports.list = list diff --git a/src/claimDefinitions/irc.js b/src/claimDefinitions/irc.js index dd8de0f..289f074 100644 --- a/src/claimDefinitions/irc.js +++ b/src/claimDefinitions/irc.js @@ -15,7 +15,7 @@ limitations under the License. */ const E = require('../enums') -const reURI = /^irc\:\/\/(.*)\/([a-zA-Z0-9\-\[\]\\\`\_\^\{\|\}]*)/ +const reURI = /^irc:\/\/(.*)\/([a-zA-Z0-9\-[\]\\`_^{|}]*)/ const processURI = (uri) => { const match = uri.match(reURI) @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'communication', - name: 'irc', + name: 'irc' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: `irc://${match[1]}/${match[2]}`, uri: uri, - qr: null, + qr: null }, proof: { uri: null, @@ -42,35 +42,35 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { domain: match[1], - nick: match[2], - }, - }, + nick: match[2] + } + } }, claim: { format: E.ClaimFormat.URI, relation: E.ClaimRelation.CONTAINS, - path: [], - }, + path: [] + } } } const tests = [ { uri: 'irc://chat.ircserver.org/Alice1', - shouldMatch: true, + shouldMatch: true }, { uri: 'irc://chat.ircserver.org/alice?param=123', - shouldMatch: true, + shouldMatch: true }, { uri: 'irc://chat.ircserver.org/alice_bob', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://chat.ircserver.org/alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/liberapay.js b/src/claimDefinitions/liberapay.js index 975e0e3..8e0faa8 100644 --- a/src/claimDefinitions/liberapay.js +++ b/src/claimDefinitions/liberapay.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'liberapay', + name: 'liberapay' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: uri, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://liberapay.com/${match[1]}/public.json`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['statements', 'content'], - }, + path: ['statements', 'content'] + } } } const tests = [ { uri: 'https://liberapay.com/alice', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://liberapay.com/alice/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/lobsters.js b/src/claimDefinitions/lobsters.js index cea719e..a33e9e6 100644 --- a/src/claimDefinitions/lobsters.js +++ b/src/claimDefinitions/lobsters.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'lobsters', + name: 'lobsters' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: uri, - qr: null, + qr: null }, proof: { uri: `https://lobste.rs/u/${match[1]}.json`, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://lobste.rs/u/${match[1]}.json`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['about'], - }, + path: ['about'] + } } } const tests = [ { uri: 'https://lobste.rs/u/Alice', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://lobste.rs/u/Alice/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/u/Alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/mastodon.js b/src/claimDefinitions/mastodon.js index 44dcf27..dccc8d4 100644 --- a/src/claimDefinitions/mastodon.js +++ b/src/claimDefinitions/mastodon.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'mastodon', + name: 'mastodon' }, match: { regularExpression: reURI, - isAmbiguous: true, + isAmbiguous: true }, profile: { display: `@${match[2]}@${match[1]}`, uri: uri, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: uri, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.FINGERPRINT, relation: E.ClaimRelation.CONTAINS, - path: ['attachment', 'value'], - }, + path: ['attachment', 'value'] + } } } const tests = [ { uri: 'https://domain.org/@alice', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/@alice/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/matrix.js b/src/claimDefinitions/matrix.js index 0df8e12..d2c8bdb 100644 --- a/src/claimDefinitions/matrix.js +++ b/src/claimDefinitions/matrix.js @@ -16,7 +16,7 @@ limitations under the License. const E = require('../enums') const queryString = require('query-string') -const reURI = /^matrix\:u\/(?:\@)?([^@:]*\:[^?]*)(\?.*)?/ +const reURI = /^matrix:u\/(?:@)?([^@:]*:[^?]*)(\?.*)?/ const processURI = (uri) => { const match = uri.match(reURI) @@ -37,16 +37,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'communication', - name: 'matrix', + name: 'matrix' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: `@${match[1]}`, uri: profileUrl, - qr: null, + qr: null }, proof: { uri: eventUrl, @@ -56,15 +56,15 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { eventId: params['org.keyoxide.e'], - roomId: params['org.keyoxide.r'], - }, - }, + roomId: params['org.keyoxide.r'] + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['content', 'body'], - }, + path: ['content', 'body'] + } } } @@ -72,20 +72,20 @@ const tests = [ { uri: 'matrix:u/alice:matrix.domain.org?org.keyoxide.r=!123:domain.org&org.keyoxide.e=$123', - shouldMatch: true, + shouldMatch: true }, { uri: 'matrix:u/alice:matrix.domain.org', - shouldMatch: true, + shouldMatch: true }, { uri: 'xmpp:alice@domain.org', - shouldMatch: false, + shouldMatch: false }, { uri: 'https://domain.org/@alice', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/owncast.js b/src/claimDefinitions/owncast.js index 4870825..bdedef3 100644 --- a/src/claimDefinitions/owncast.js +++ b/src/claimDefinitions/owncast.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'owncast', + name: 'owncast' }, match: { regularExpression: reURI, - isAmbiguous: true, + isAmbiguous: true }, profile: { display: match[1], uri: uri, - qr: null, + qr: null }, proof: { uri: `${uri}/api/config`, @@ -42,35 +42,35 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `${uri}/api/config`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.FINGERPRINT, relation: E.ClaimRelation.CONTAINS, - path: ['socialHandles', 'url'], - }, + path: ['socialHandles', 'url'] + } } } const tests = [ { uri: 'https://live.domain.org', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://live.domain.org/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/live', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/live/', - shouldMatch: true, - }, + shouldMatch: true + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/reddit.js b/src/claimDefinitions/reddit.js index d9d4091..d9968b3 100644 --- a/src/claimDefinitions/reddit.js +++ b/src/claimDefinitions/reddit.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'reddit', + name: 'reddit' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: match[1], uri: `https://www.reddit.com/user/${match[1]}`, - qr: null, + qr: null }, proof: { uri: uri, @@ -42,39 +42,39 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { url: `https://www.reddit.com/user/${match[1]}/comments/${match[2]}.json`, - format: E.ProofFormat.JSON, - }, - }, + format: E.ProofFormat.JSON + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: ['data', 'children', 'data', 'selftext'], - }, + path: ['data', 'children', 'data', 'selftext'] + } } } const tests = [ { uri: 'https://www.reddit.com/user/Alice/comments/123456/post', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://www.reddit.com/user/Alice/comments/123456/post/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://reddit.com/user/Alice/comments/123456/post', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://reddit.com/user/Alice/comments/123456/post/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/user/Alice/comments/123456/post', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/twitter.js b/src/claimDefinitions/twitter.js index f868faa..026d1a1 100644 --- a/src/claimDefinitions/twitter.js +++ b/src/claimDefinitions/twitter.js @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'web', - name: 'twitter', + name: 'twitter' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: `@${match[1]}`, uri: `https://twitter.com/${match[1]}`, - qr: null, + qr: null }, proof: { uri: uri, @@ -41,31 +41,31 @@ const processURI = (uri) => { access: E.ProofAccess.GRANTED, format: E.ProofFormat.TEXT, data: { - tweetId: match[2], - }, - }, + tweetId: match[2] + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: [], - }, + path: [] + } } } const tests = [ { uri: 'https://twitter.com/alice/status/1234567890123456789', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://twitter.com/alice/status/1234567890123456789/', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org/alice/status/1234567890123456789', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/claimDefinitions/xmpp.js b/src/claimDefinitions/xmpp.js index 1b14a87..c862dce 100644 --- a/src/claimDefinitions/xmpp.js +++ b/src/claimDefinitions/xmpp.js @@ -15,7 +15,7 @@ limitations under the License. */ const E = require('../enums') -const reURI = /^xmpp:([a-zA-Z0-9\.\-\_]*)@([a-zA-Z0-9\.\-\_]*)(?:\?(.*))?/ +const reURI = /^xmpp:([a-zA-Z0-9.\-_]*)@([a-zA-Z0-9.\-_]*)(?:\?(.*))?/ const processURI = (uri) => { const match = uri.match(reURI) @@ -23,16 +23,16 @@ const processURI = (uri) => { return { serviceprovider: { type: 'communication', - name: 'xmpp', + name: 'xmpp' }, match: { regularExpression: reURI, - isAmbiguous: false, + isAmbiguous: false }, profile: { display: `${match[1]}@${match[2]}`, uri: uri, - qr: uri, + qr: uri }, proof: { uri: null, @@ -42,31 +42,31 @@ const processURI = (uri) => { format: E.ProofFormat.TEXT, data: { id: `${match[1]}@${match[2]}`, - field: 'note', - }, - }, + field: 'note' + } + } }, claim: { format: E.ClaimFormat.MESSAGE, relation: E.ClaimRelation.CONTAINS, - path: [], - }, + path: [] + } } } const tests = [ { uri: 'xmpp:alice@domain.org', - shouldMatch: true, + shouldMatch: true }, { uri: 'xmpp:alice@domain.org?omemo-sid-123456789=A1B2C3D4E5F6G7H8I9', - shouldMatch: true, + shouldMatch: true }, { uri: 'https://domain.org', - shouldMatch: false, - }, + shouldMatch: false + } ] exports.reURI = reURI diff --git a/src/defaults.js b/src/defaults.js index 014741a..8c80cb1 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -42,25 +42,25 @@ const E = require('./enums') const opts = { proxy: { hostname: null, - policy: E.ProxyPolicy.NEVER, + policy: E.ProxyPolicy.NEVER }, claims: { irc: { - nick: null, + nick: null }, matrix: { instance: null, - accessToken: null, + accessToken: null }, xmpp: { service: null, username: null, - password: null, + password: null }, twitter: { - bearerToken: null, - }, - }, + bearerToken: null + } + } } exports.opts = opts diff --git a/src/enums.js b/src/enums.js index 95da108..7120605 100644 --- a/src/enums.js +++ b/src/enums.js @@ -29,7 +29,7 @@ const ProxyPolicy = { /** Always use a proxy */ ALWAYS: 'always', /** Never use a proxy, skip a verification if a proxy is inevitable */ - NEVER: 'never', + NEVER: 'never' } Object.freeze(ProxyPolicy) @@ -52,7 +52,7 @@ const Fetcher = { /** HTTP request to Gitlab API */ GITLAB: 'gitlab', /** HTTP request to Twitter API */ - TWITTER: 'twitter', + TWITTER: 'twitter' } Object.freeze(Fetcher) @@ -69,7 +69,7 @@ const ProofAccess = { /** HTTP requests must contain API or access tokens */ GRANTED: 2, /** Not accessible by HTTP request, needs server software */ - SERVER: 3, + SERVER: 3 } Object.freeze(ProofAccess) @@ -82,7 +82,7 @@ const ProofFormat = { /** JSON format */ JSON: 'json', /** Plaintext format */ - TEXT: 'text', + TEXT: 'text' } Object.freeze(ProofFormat) @@ -97,7 +97,7 @@ const ClaimFormat = { /** `123123123` */ FINGERPRINT: 1, /** `[Verifying my OpenPGP key: openpgp4fpr:123123123]` */ - MESSAGE: 2, + MESSAGE: 2 } Object.freeze(ClaimFormat) @@ -112,7 +112,7 @@ const ClaimRelation = { /** Claim is equal to the JSON field's textual content */ EQUALS: 1, /** Claim is equal to an element of the JSON field's array of strings */ - ONEOF: 2, + ONEOF: 2 } Object.freeze(ClaimRelation) @@ -127,7 +127,7 @@ const ClaimStatus = { /** Claim has matched its URI to candidate claim definitions */ MATCHED: 'matched', /** Claim has verified one or multiple candidate claim definitions */ - VERIFIED: 'verified', + VERIFIED: 'verified' } Object.freeze(ClaimStatus) diff --git a/src/fetcher/dns.js b/src/fetcher/dns.js index 02ba90c..27c2093 100644 --- a/src/fetcher/dns.js +++ b/src/fetcher/dns.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require("browser-or-node") +const jsEnv = require('browser-or-node') /** * @module fetcher/dns @@ -55,8 +55,8 @@ if (jsEnv.isNode) { resolve({ domain: data.domain, records: { - txt: records, - }, + txt: records + } }) }) }) @@ -68,4 +68,4 @@ if (jsEnv.isNode) { } } else { module.exports.fn = null -} \ No newline at end of file +} diff --git a/src/fetcher/gitlab.js b/src/fetcher/gitlab.js index d4bbcfb..0c86706 100644 --- a/src/fetcher/gitlab.js +++ b/src/fetcher/gitlab.js @@ -44,28 +44,45 @@ module.exports.fn = async (data, opts) => { ) }) - const fetchPromise = new Promise(async (resolve, reject) => { + const fetchPromise = new Promise((resolve, reject) => { const urlUser = `https://${data.domain}/api/v4/users?username=${data.username}` - const resUser = await req(urlUser, null, { Accept: 'application/json' }) - const jsonUser = await resUser.json() + // const resUser = await req(urlUser, null, { Accept: 'application/json' }) + const res = req(urlUser, null, { Accept: 'application/json' }) + .then(resUser => { + return resUser.json() + }) + .then(jsonUser => { + return jsonUser.find((user) => user.username === data.username) + }) + .then(user => { + if (!user) { + throw new Error(`No user with username ${data.username}`) + } + return user + }) + .then(user => { + const urlProject = `https://${data.domain}/api/v4/users/${user.id}/projects` + return req(urlProject, null, { + Accept: 'application/json' + }) + }) + .then(resProject => { + return resProject.json() + }) + .then(jsonProject => { + return jsonProject.find((proj) => proj.path === 'gitlab_proof') + }) + .then(project => { + if (!project) { + throw new Error('No project found') + } + return project + }) + .catch(error => { + reject(error) + }) - const user = jsonUser.find((user) => user.username === data.username) - if (!user) { - reject(`No user with username ${data.username}`) - } - - const urlProject = `https://${data.domain}/api/v4/users/${user.id}/projects` - const resProject = await req(urlProject, null, { - Accept: 'application/json', - }) - const jsonProject = await resProject.json() - - const project = jsonProject.find((proj) => proj.path === 'gitlab_proof') - if (!project) { - reject(`No project found`) - } - - resolve(project) + resolve(res) }) return Promise.race([fetchPromise, timeoutPromise]).then((result) => { diff --git a/src/fetcher/http.js b/src/fetcher/http.js index ca3d125..278e60d 100644 --- a/src/fetcher/http.js +++ b/src/fetcher/http.js @@ -47,7 +47,7 @@ module.exports.fn = async (data, opts) => { const fetchPromise = new Promise((resolve, reject) => { if (!data.url) { - reject('No valid URI provided') + reject(new Error('No valid URI provided')) return } @@ -55,7 +55,7 @@ module.exports.fn = async (data, opts) => { case E.ProofFormat.JSON: req(data.url, null, { Accept: 'application/json', - 'User-Agent': `doipjs/${require('../../package.json').version}`, + 'User-Agent': `doipjs/${require('../../package.json').version}` }) .then(async (res) => { return await res.json() @@ -80,7 +80,7 @@ module.exports.fn = async (data, opts) => { }) break default: - reject('No specified data format') + reject(new Error('No specified data format')) break } }) diff --git a/src/fetcher/irc.js b/src/fetcher/irc.js index 8f37f17..717b71f 100644 --- a/src/fetcher/irc.js +++ b/src/fetcher/irc.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require("browser-or-node") +const jsEnv = require('browser-or-node') /** * @module fetcher/irc @@ -28,7 +28,7 @@ module.exports.timeout = 20000 if (jsEnv.isNode) { const irc = require('irc-upd') const validator = require('validator') - + /** * Execute a fetch request * @function @@ -48,14 +48,14 @@ if (jsEnv.isNode) { data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout ) }) - + const fetchPromise = new Promise((resolve, reject) => { try { validator.isAscii(opts.claims.irc.nick) } catch (err) { throw new Error(`IRC fetcher was not set up properly (${err.message})`) } - + try { const client = new irc.Client(data.domain, opts.claims.irc.nick, { port: 6697, @@ -64,10 +64,10 @@ if (jsEnv.isNode) { showErrors: false, debug: false }) - const reKey = /[a-zA-Z0-9\-\_]+\s+:\s(openpgp4fpr\:.*)/ + const reKey = /[a-zA-Z0-9\-_]+\s+:\s(openpgp4fpr:.*)/ const reEnd = /End\sof\s.*\staxonomy./ - let keys = [] - + const keys = [] + client.addListener('registered', (message) => { client.send(`PRIVMSG NickServ TAXONOMY ${data.nick}`) }) @@ -85,7 +85,7 @@ if (jsEnv.isNode) { reject(error) } }) - + return Promise.race([fetchPromise, timeoutPromise]).then((result) => { clearTimeout(timeoutHandle) return result diff --git a/src/fetcher/matrix.js b/src/fetcher/matrix.js index 0295ee8..c2f958c 100644 --- a/src/fetcher/matrix.js +++ b/src/fetcher/matrix.js @@ -58,7 +58,7 @@ module.exports.fn = async (data, opts) => { const url = `https://${opts.claims.matrix.instance}/_matrix/client/r0/rooms/${data.roomId}/event/${data.eventId}?access_token=${opts.claims.matrix.accessToken}` bentReq(url, null, { - Accept: 'application/json', + Accept: 'application/json' }) .then(async (res) => { return await res.json() diff --git a/src/fetcher/twitter.js b/src/fetcher/twitter.js index dc19172..9a7375c 100644 --- a/src/fetcher/twitter.js +++ b/src/fetcher/twitter.js @@ -60,7 +60,7 @@ module.exports.fn = async (data, opts) => { null, { Accept: 'application/json', - Authorization: `Bearer ${opts.claims.twitter.bearerToken}`, + Authorization: `Bearer ${opts.claims.twitter.bearerToken}` } ) .then(async (data) => { diff --git a/src/fetcher/xmpp.js b/src/fetcher/xmpp.js index c35e981..f4efe13 100644 --- a/src/fetcher/xmpp.js +++ b/src/fetcher/xmpp.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require("browser-or-node") +const jsEnv = require('browser-or-node') /** * @module fetcher/xmpp @@ -30,16 +30,16 @@ if (jsEnv.isNode) { const { client, xml } = require('@xmpp/client') const debug = require('@xmpp/debug') const validator = require('validator') - - let xmpp = null, - iqCaller = null - + + let xmpp = null + let iqCaller = null + const xmppStart = async (service, username, password) => { return new Promise((resolve, reject) => { const xmpp = client({ service: service, username: username, - password: password, + password: password }) if (process.env.NODE_ENV !== 'production') { debug(xmpp, true) @@ -54,7 +54,7 @@ if (jsEnv.isNode) { }) }) } - + /** * Execute a fetch request * @function @@ -69,6 +69,32 @@ if (jsEnv.isNode) { * @returns {object} */ module.exports.fn = async (data, opts) => { + try { + validator.isFQDN(opts.claims.xmpp.service) + validator.isAscii(opts.claims.xmpp.username) + validator.isAscii(opts.claims.xmpp.password) + } catch (err) { + throw new Error(`XMPP fetcher was not set up properly (${err.message})`) + } + + if (!xmpp || xmpp.status !== 'online') { + const xmppStartRes = await xmppStart( + opts.claims.xmpp.service, + opts.claims.xmpp.username, + opts.claims.xmpp.password + ) + xmpp = xmppStartRes.xmpp + iqCaller = xmppStartRes.iqCaller + } + + const response = await iqCaller.request( + xml('iq', { type: 'get', to: data.id }, xml('vCard', 'vcard-temp')), + 30 * 1000 + ) + + const vcardRow = response.getChild('vCard', 'vcard-temp').toString() + const dom = new jsdom.JSDOM(vcardRow) + let timeoutHandle const timeoutPromise = new Promise((resolve, reject) => { timeoutHandle = setTimeout( @@ -76,37 +102,11 @@ if (jsEnv.isNode) { data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout ) }) - - const fetchPromise = new Promise(async (resolve, reject) => { - try { - validator.isFQDN(opts.claims.xmpp.service) - validator.isAscii(opts.claims.xmpp.username) - validator.isAscii(opts.claims.xmpp.password) - } catch (err) { - throw new Error(`XMPP fetcher was not set up properly (${err.message})`) - } - - if (!xmpp || xmpp.status !== 'online') { - const xmppStartRes = await xmppStart( - opts.claims.xmpp.service, - opts.claims.xmpp.username, - opts.claims.xmpp.password - ) - xmpp = xmppStartRes.xmpp - iqCaller = xmppStartRes.iqCaller - } - - const response = await iqCaller.request( - xml('iq', { type: 'get', to: data.id }, xml('vCard', 'vcard-temp')), - 30 * 1000 - ) - - const vcardRow = response.getChild('vCard', 'vcard-temp').toString() - const dom = new jsdom.JSDOM(vcardRow) - + + const fetchPromise = new Promise((resolve, reject) => { try { let vcard - + switch (data.field.toLowerCase()) { case 'desc': case 'note': @@ -120,7 +120,7 @@ if (jsEnv.isNode) { throw new Error('No DESC or NOTE field found in vCard') } break - + default: vcard = dom.window.document.querySelector(data).textContent break @@ -131,7 +131,7 @@ if (jsEnv.isNode) { reject(error) } }) - + return Promise.race([fetchPromise, timeoutPromise]).then((result) => { clearTimeout(timeoutHandle) return result diff --git a/src/keys.js b/src/keys.js index a850dff..ea28a9d 100644 --- a/src/keys.js +++ b/src/keys.js @@ -34,36 +34,32 @@ const Claim = require('./claim') * const key1 = doip.keys.fetchHKP('alice@domain.tld'); * const key2 = doip.keys.fetchHKP('123abc123abc'); */ -exports.fetchHKP = (identifier, keyserverDomain) => { - return new Promise(async (resolve, reject) => { - const keyserverBaseUrl = keyserverDomain - ? `https://${keyserverDomain}` - : 'https://keys.openpgp.org' +exports.fetchHKP = async (identifier, keyserverDomain) => { + const keyserverBaseUrl = keyserverDomain + ? `https://${keyserverDomain}` + : 'https://keys.openpgp.org' - const hkp = new openpgp.HKP(keyserverBaseUrl) - const lookupOpts = { - query: identifier, - } + const hkp = new openpgp.HKP(keyserverBaseUrl) + const lookupOpts = { + query: identifier + } - let publicKey = await hkp.lookup(lookupOpts).catch((error) => { - reject('Key does not exist or could not be fetched') - }) - - publicKey = await openpgp.key - .readArmored(publicKey) - .then((result) => { - return result.keys[0] - }) - .catch((error) => { - return null - }) - - if (publicKey) { - resolve(publicKey) - } else { - reject('Key does not exist or could not be fetched') - } + const publicKey = await hkp.lookup(lookupOpts).catch((error) => { + throw new Error(`Key does not exist or could not be fetched (${error})`) }) + + if (!publicKey) { + throw new Error('Key does not exist or could not be fetched') + } + + return await openpgp.key + .readArmored(publicKey) + .then((result) => { + return result.keys[0] + }) + .catch((error) => { + throw new Error(`Key does not exist or could not be fetched (${error})`) + }) } /** @@ -74,28 +70,20 @@ exports.fetchHKP = (identifier, keyserverDomain) => { * @example * const key = doip.keys.fetchWKD('alice@domain.tld'); */ -exports.fetchWKD = (identifier) => { - return new Promise(async (resolve, reject) => { - const wkd = new openpgp.WKD() - const lookupOpts = { - email: identifier, - } +exports.fetchWKD = async (identifier) => { + const wkd = new openpgp.WKD() + const lookupOpts = { + email: identifier + } - const publicKey = await wkd - .lookup(lookupOpts) - .then((result) => { - return result.keys[0] - }) - .catch((error) => { - return null - }) - - if (publicKey) { - resolve(publicKey) - } else { - reject('Key does not exist or could not be fetched') - } - }) + return await wkd + .lookup(lookupOpts) + .then((result) => { + return result.keys[0] + }) + .catch((error) => { + throw new Error(`Key does not exist or could not be fetched (${error})`) + }) } /** @@ -107,37 +95,29 @@ exports.fetchWKD = (identifier) => { * @example * const key = doip.keys.fetchKeybase('alice', '123abc123abc'); */ -exports.fetchKeybase = (username, fingerprint) => { - return new Promise(async (resolve, reject) => { - const keyLink = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}` - let rawKeyContent - try { - rawKeyContent = await req(keyLink) - .then((response) => { - if (response.status === 200) { - return response - } - }) - .then((response) => response.text()) - } catch (e) { - reject(`Error fetching Keybase key: ${e.message}`) - } - - const publicKey = await openpgp.key - .readArmored(rawKeyContent) - .then((result) => { - return result.keys[0] - }) - .catch((error) => { - return null +exports.fetchKeybase = async (username, fingerprint) => { + const keyLink = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}` + let rawKeyContent + try { + rawKeyContent = await req(keyLink) + .then((response) => { + if (response.status === 200) { + return response + } }) + .then((response) => response.text()) + } catch (e) { + throw new Error(`Error fetching Keybase key: ${e.message}`) + } - if (publicKey) { - resolve(publicKey) - } else { - reject('Key does not exist or could not be fetched') - } - }) + return await openpgp.key + .readArmored(rawKeyContent) + .then((result) => { + return result.keys[0] + }) + .catch((error) => { + throw new Error(`Key does not exist or could not be fetched (${error})`) + }) } /** @@ -154,12 +134,9 @@ exports.fetchKeybase = (username, fingerprint) => { * -----END PGP PUBLIC KEY BLOCK-----` * const key = doip.keys.fetchPlaintext(plainkey); */ -exports.fetchPlaintext = (rawKeyContent) => { - return new Promise(async (resolve, reject) => { - const publicKey = (await openpgp.key.readArmored(rawKeyContent)).keys[0] - - resolve(publicKey) - }) +exports.fetchPlaintext = async (rawKeyContent) => { + const publicKey = (await openpgp.key.readArmored(rawKeyContent)).keys[0] + return publicKey } /** @@ -172,41 +149,34 @@ exports.fetchPlaintext = (rawKeyContent) => { * const key2 = doip.keys.fetchURI('hkp:123abc123abc'); * const key3 = doip.keys.fetchURI('wkd:alice@domain.tld'); */ -exports.fetchURI = (uri) => { - return new Promise(async (resolve, reject) => { - if (!validUrl.isUri(uri)) { - reject('Invalid URI') - } +exports.fetchURI = async (uri) => { + if (!validUrl.isUri(uri)) { + throw new Error('Invalid URI') + } - const re = /([a-zA-Z0-9]*):([a-zA-Z0-9@._=+\-]*)(?:\:([a-zA-Z0-9@._=+\-]*))?/ - const match = uri.match(re) + const re = /([a-zA-Z0-9]*):([a-zA-Z0-9@._=+-]*)(?::([a-zA-Z0-9@._=+-]*))?/ + const match = uri.match(re) - if (!match[1]) { - reject('Invalid URI') - } + if (!match[1]) { + throw new Error('Invalid URI') + } - switch (match[1]) { - case 'hkp': - resolve( - exports.fetchHKP( - match[3] ? match[3] : match[2], - match[3] ? match[2] : null - ) - ) - break - case 'wkd': - resolve(exports.fetchWKD(match[2])) - break - case 'kb': - resolve( - exports.fetchKeybase(match[2], match.length >= 4 ? match[3] : null) - ) - break - default: - reject('Invalid URI protocol') - break - } - }) + switch (match[1]) { + case 'hkp': + return exports.fetchHKP( + match[3] ? match[3] : match[2], + match[3] ? match[2] : null + ) + + case 'wkd': + return exports.fetchWKD(match[2]) + + case 'kb': + return exports.fetchKeybase(match[2], match.length >= 4 ? match[3] : null) + + default: + throw new Error('Invalid URI protocol') + } } /** @@ -221,51 +191,55 @@ exports.fetchURI = (uri) => { * console.log(claim.uri); * }); */ -exports.process = (publicKey) => { - return new Promise(async (resolve, reject) => { - if (!publicKey || !(publicKey instanceof openpgp.key.Key)) { - reject('Invalid public key') +exports.process = async (publicKey) => { + if (!publicKey || !(publicKey instanceof openpgp.key.Key)) { + throw new Error('Invalid public key') + } + + const fingerprint = await publicKey.primaryKey.getFingerprint() + const primaryUser = await publicKey.getPrimaryUser() + const users = publicKey.users + const usersOutput = [] + + users.forEach((user, i) => { + usersOutput[i] = { + userData: { + id: user.userId ? user.userId.userid : null, + name: user.userId ? user.userId.name : null, + email: user.userId ? user.userId.email : null, + comment: user.userId ? user.userId.comment : null, + isPrimary: primaryUser.index === i, + isRevoked: false + }, + claims: [] } - const fingerprint = await publicKey.primaryKey.getFingerprint() - const primaryUser = await publicKey.getPrimaryUser() - const users = publicKey.users - let usersOutput = [] + if ('selfCertifications' in user && user.selfCertifications.length > 0) { + const selfCertification = user.selfCertifications[0] - users.forEach((user, i) => { - usersOutput[i] = { - userData: { - id: user.userId ? user.userId.userid : null, - name: user.userId ? user.userId.name : null, - email: user.userId ? user.userId.email : null, - comment: user.userId ? user.userId.comment : null, - isPrimary: primaryUser.index === i, - isRevoked: false, - }, - claims: [], - } + const notations = selfCertification.rawNotations + usersOutput[i].claims = notations + .filter( + ({ name, humanReadable }) => + humanReadable && name === 'proof@metacode.biz' + ) + .map( + ({ value }) => + new Claim(openpgp.util.decode_utf8(value), fingerprint) + ) - if ('selfCertifications' in user && user.selfCertifications.length > 0) { - const selfCertification = user.selfCertifications[0] - - const notations = selfCertification.rawNotations - usersOutput[i].claims = notations - .filter(({ name, humanReadable }) => humanReadable && name === 'proof@metacode.biz') - .map(({ value }) => new Claim(openpgp.util.decode_utf8(value), fingerprint)) - - usersOutput[i].userData.isRevoked = selfCertification.revoked - } - }) - - resolve({ - fingerprint: fingerprint, - users: usersOutput, - primaryUserIndex: primaryUser.index, - key: { - data: publicKey, - fetchMethod: null, - uri: null, - }, - }) + usersOutput[i].userData.isRevoked = selfCertification.revoked + } }) + + return { + fingerprint: fingerprint, + users: usersOutput, + primaryUserIndex: primaryUser.index, + key: { + data: publicKey, + fetchMethod: null, + uri: null + } + } } diff --git a/src/proofs.js b/src/proofs.js index 759db9c..9f824e9 100644 --- a/src/proofs.js +++ b/src/proofs.js @@ -54,49 +54,37 @@ const handleBrowserRequests = (data, opts) => { switch (opts.proxy.policy) { case E.ProxyPolicy.ALWAYS: return createProxyRequestPromise(data, opts) - break case E.ProxyPolicy.NEVER: switch (data.proof.request.access) { case E.ProofAccess.GENERIC: case E.ProofAccess.GRANTED: return createDefaultRequestPromise(data, opts) - break case E.ProofAccess.NOCORS: case E.ProofAccess.SERVER: throw new Error( 'Impossible to fetch proof (bad combination of service access and proxy policy)' ) - break default: throw new Error('Invalid proof access value') - break } - break case E.ProxyPolicy.ADAPTIVE: switch (data.proof.request.access) { case E.ProofAccess.GENERIC: return createFallbackRequestPromise(data, opts) - break case E.ProofAccess.NOCORS: return createProxyRequestPromise(data, opts) - break case E.ProofAccess.GRANTED: return createFallbackRequestPromise(data, opts) - break case E.ProofAccess.SERVER: return createProxyRequestPromise(data, opts) - break default: throw new Error('Invalid proof access value') - break } - break default: throw new Error('Invalid proxy policy') - break } } @@ -104,19 +92,15 @@ const handleNodeRequests = (data, opts) => { switch (opts.proxy.policy) { case E.ProxyPolicy.ALWAYS: return createProxyRequestPromise(data, opts) - break case E.ProxyPolicy.NEVER: return createDefaultRequestPromise(data, opts) - break case E.ProxyPolicy.ADAPTIVE: return createFallbackRequestPromise(data, opts) - break default: throw new Error('Invalid proxy policy') - break } } @@ -129,7 +113,7 @@ const createDefaultRequestPromise = (data, opts) => { fetcher: data.proof.request.fetcher, data: data, viaProxy: false, - result: res, + result: res }) }) .catch((err) => { @@ -154,7 +138,7 @@ const createProxyRequestPromise = (data, opts) => { const requestData = { url: proxyUrl, format: data.proof.request.format, - fetcherTimeout: fetcher[data.proof.request.fetcher].timeout, + fetcherTimeout: fetcher[data.proof.request.fetcher].timeout } fetcher.http .fn(requestData, opts) @@ -163,7 +147,7 @@ const createProxyRequestPromise = (data, opts) => { fetcher: 'http', data: data, viaProxy: true, - result: res, + result: res }) }) .catch((err) => { @@ -184,7 +168,7 @@ const createFallbackRequestPromise = (data, opts) => { return resolve(res) }) .catch((err2) => { - return reject([err1, err2]) + return reject(err2) }) }) }) diff --git a/src/proxy/api/v1/index.js b/src/proxy/api/v1/index.js index cada03e..3d82d02 100644 --- a/src/proxy/api/v1/index.js +++ b/src/proxy/api/v1/index.js @@ -24,40 +24,40 @@ const debug = require('@xmpp/debug') const irc = require('irc-upd') require('dotenv').config() -const xmpp_service = process.env.XMPP_SERVICE || null -const xmpp_username = process.env.XMPP_USERNAME || null -const xmpp_password = process.env.XMPP_PASSWORD || null -const twitter_bearer_token = process.env.TWITTER_BEARER_TOKEN || null -const matrix_instance = process.env.MATRIX_INSTANCE || null -const matrix_access_token = process.env.MATRIX_ACCESS_TOKEN || null -const irc_nick = process.env.IRC_NICK || null +const xmppService = process.env.XMPP_SERVICE || null +const xmppUsername = process.env.XMPP_USERNAME || null +const xmppPassword = process.env.XMPP_PASSWORD || null +const twitterBearerToken = process.env.TWITTER_BEARER_TOKEN || null +const matrixInstance = process.env.MATRIX_INSTANCE || null +const matrixAccessToken = process.env.MATRIX_ACCESS_TOKEN || null +const ircNick = process.env.IRC_NICK || null -let xmpp = null, - iqCaller = null, - xmpp_enabled = true, - twitter_enabled = false, - matrix_enabled = false, - irc_enabled = false +let xmpp = null +let iqCaller = null +let xmppEnabled = true +let twitterEnabled = false +let matrixEnabled = false +let ircEnabled = false -if (!xmpp_service || !xmpp_username || !xmpp_password) { - xmpp_enabled = false +if (!xmppService || !xmppUsername || !xmppPassword) { + xmppEnabled = false } -if (twitter_bearer_token) { - twitter_enabled = true +if (twitterBearerToken) { + twitterEnabled = true } -if (matrix_instance && matrix_access_token) { - matrix_enabled = true +if (matrixInstance && matrixAccessToken) { + matrixEnabled = true } -if (irc_nick) { - irc_enabled = true +if (ircNick) { + ircEnabled = true } -const xmppStart = async (xmpp_service, xmpp_username, xmpp_password) => { +const xmppStart = async (xmppService, xmppUsername, xmppPassword) => { return new Promise((resolve, reject) => { const xmpp = client({ - service: xmpp_service, - username: xmpp_username, - password: xmpp_password, + service: xmppService, + username: xmppUsername, + password: xmppPassword }) if (process.env.NODE_ENV !== 'production') { debug(xmpp, true) @@ -77,7 +77,7 @@ const xmppStart = async (xmpp_service, xmpp_username, xmpp_password) => { router.get('/', async (req, res) => { res.status(200).json({ message: - 'Available endpoints: /json/:url, /text/:url, /dns/:hostname, /xmpp/:xmppid, /twitter/:tweetid, /matrix/:roomid/:eventid, /irc/:ircserver/:ircnick', + 'Available endpoints: /json/:url, /text/:url, /dns/:hostname, /xmpp/:xmppid, /twitter/:tweetid, /matrix/:roomid/:eventid, /irc/:ircserver/:ircnick' }) }) @@ -94,7 +94,7 @@ router.param('url', async (req, res, next, url) => { router.param('xmppid', async (req, res, next, xmppid) => { req.params.xmppid = xmppid - if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/.test(req.params.xmppid)) { + if (/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,})+$/.test(req.params.xmppid)) { next() } else { return res.status(400).json({ message: 'XMPP_ID was not valid' }) @@ -112,13 +112,13 @@ router.param('xmppdata', async (req, res, next, xmppdata) => { 'BDAY', 'NICKNAME', 'NOTE', - 'DESC', + 'DESC' ] if (!allowedData.includes(req.params.xmppdata)) { return res.status(400).json({ message: - 'Allowed data are: FN, NUMBER, USERID, URL, BDAY, NICKNAME, NOTE, DESC', + 'Allowed data are: FN, NUMBER, USERID, URL, BDAY, NICKNAME, NOTE, DESC' }) } @@ -127,7 +127,7 @@ router.param('xmppdata', async (req, res, next, xmppdata) => { router.get('/get/json/:url', (req, res) => { bentReq(req.params.url, 'json', { - Accept: 'application/json', + Accept: 'application/json' }) .then(async (result) => { return await result.json() @@ -155,11 +155,14 @@ router.get('/get/text/:url', (req, res) => { router.get('/get/dns/:hostname', async (req, res) => { dns.resolveTxt(req.params.hostname, (err, records) => { + if (err) { + throw new Error(err) + } const out = { hostname: req.params.hostname, records: { - txt: records, - }, + txt: records + } } return res.status(200).json(out) }) @@ -174,14 +177,14 @@ router.get('/get/xmpp/:xmppid', async (req, res) => { }) router.get('/get/xmpp/:xmppid/:xmppdata', async (req, res) => { - if (!xmpp_enabled) { + if (!xmppEnabled) { return res.status(500).json('XMPP not enabled on server') } if (!xmpp) { const xmppStartRes = await xmppStart( - xmpp_service, - xmpp_username, - xmpp_password + xmppService, + xmppUsername, + xmppPassword ) xmpp = xmppStartRes.xmpp iqCaller = xmppStartRes.iqCaller @@ -231,7 +234,7 @@ router.get('/get/xmpp/:xmppid/:xmppdata', async (req, res) => { }) router.get('/get/twitter/:tweetid', async (req, res) => { - if (!twitter_enabled) { + if (!twitterEnabled) { return res.status(500).json('Twitter not enabled on server') } @@ -240,7 +243,7 @@ router.get('/get/twitter/:tweetid', async (req, res) => { null, { Accept: 'application/json', - Authorization: `Bearer ${twitter_bearer_token}`, + Authorization: `Bearer ${twitterBearerToken}` } ) .then(async (data) => { @@ -253,20 +256,20 @@ router.get('/get/twitter/:tweetid', async (req, res) => { return res.status(error.statusCode || 400).json({ data: [], message: 'Request could not be fulfilled', - error: error, + error: error }) }) }) router.get('/get/matrix/:matrixroomid/:matrixeventid', async (req, res) => { - if (!matrix_enabled) { + if (!matrixEnabled) { return res.status(500).json('Matrix not enabled on server') } - const url = `https://${matrix_instance}/_matrix/client/r0/rooms/${req.params.matrixroomid}/event/${req.params.matrixeventid}?access_token=${matrix_access_token}` + const url = `https://${matrixInstance}/_matrix/client/r0/rooms/${req.params.matrixroomid}/event/${req.params.matrixeventid}?access_token=${matrixAccessToken}` bentReq(url, null, { - Accept: 'application/json', + Accept: 'application/json' }) .then(async (data) => { return await data.json() @@ -278,25 +281,25 @@ router.get('/get/matrix/:matrixroomid/:matrixeventid', async (req, res) => { return res.status(error.statusCode || 400).json({ data: [], message: 'Request could not be fulfilled', - error: error, + error: error }) }) }) router.get('/get/irc/:ircserver/:ircnick', async (req, res) => { - if (!irc_enabled) { + if (!ircEnabled) { return res.status(500).json('IRC not enabled on server') } try { - const client = new irc.Client(req.params.ircserver, irc_nick, { + const client = new irc.Client(req.params.ircserver, ircNick, { port: 6697, secure: true, - channels: [], + channels: [] }) - const reKey = /[a-zA-Z0-9\-\_]+\s+:\s(openpgp4fpr\:.*)/ + const reKey = /[a-zA-Z0-9\-_]+\s+:\s(openpgp4fpr:.*)/ const reEnd = /End\sof\s.*\staxonomy./ - let keys = [] + const keys = [] client.addListener('registered', (message) => { client.send(`PRIVMSG NickServ :TAXONOMY ${req.params.ircnick}`) @@ -317,7 +320,7 @@ router.get('/get/irc/:ircserver/:ircnick', async (req, res) => { return res.status(400).json({ data: [], message: 'Request could not be fulfilled', - error: error, + error: error }) } }) diff --git a/src/proxy/api/v2/index.js b/src/proxy/api/v2/index.js index 77f4a0b..8ece786 100644 --- a/src/proxy/api/v2/index.js +++ b/src/proxy/api/v2/index.js @@ -22,21 +22,21 @@ require('dotenv').config() const opts = { claims: { irc: { - nick: process.env.IRC_NICK || null, + nick: process.env.IRC_NICK || null }, matrix: { instance: process.env.MATRIX_INSTANCE || null, - accessToken: process.env.MATRIX_ACCESS_TOKEN || null, + accessToken: process.env.MATRIX_ACCESS_TOKEN || null }, xmpp: { service: process.env.XMPP_SERVICE || null, username: process.env.XMPP_USERNAME || null, - password: process.env.XMPP_PASSWORD || null, + password: process.env.XMPP_PASSWORD || null }, twitter: { - bearerToken: process.env.TWITTER_BEARER_TOKEN || null, - }, - }, + bearerToken: process.env.TWITTER_BEARER_TOKEN || null + } + } } // Root route @@ -61,11 +61,9 @@ router.get( switch (req.query.format) { case E.ProofFormat.JSON: return res.status(200).json(result) - break case E.ProofFormat.TEXT: return res.status(200).send(result) - break } }) .catch((err) => { @@ -103,7 +101,7 @@ router.get( 'bday', 'nickname', 'note', - 'desc', + 'desc' ]), async (req, res) => { if ( diff --git a/src/signatures.js b/src/signatures.js index 9dda8a3..9cefe96 100644 --- a/src/signatures.js +++ b/src/signatures.js @@ -27,122 +27,116 @@ const keys = require('./keys') * @param {string} signature - The plaintext signature to process * @returns {Promise} */ -const process = (signature) => { - return new Promise(async (resolve, reject) => { - let sigData, - result = { - fingerprint: null, - users: [ - { - userData: {}, - claims: [], - }, - ], - primaryUserIndex: null, - key: { - data: null, - fetchMethod: null, - uri: null, - }, +const process = async (signature) => { + let sigData + const result = { + fingerprint: null, + users: [ + { + userData: {}, + claims: [] } + ], + primaryUserIndex: null, + key: { + data: null, + fetchMethod: null, + uri: null + } + } - try { - sigData = await openpgp.cleartext.readArmored(signature) - } catch (error) { - reject(new Error('invalid_signature')) + try { + sigData = await openpgp.cleartext.readArmored(signature) + } catch (error) { + throw new Error('invalid_signature') + } + + const issuerKeyId = sigData.signature.packets[0].issuerKeyId.toHex() + const signersUserId = sigData.signature.packets[0].signersUserId + const preferredKeyServer = + sigData.signature.packets[0].preferredKeyServer || + 'https://keys.openpgp.org/' + const text = sigData.getText() + const sigKeys = [] + + text.split('\n').forEach((line, i) => { + const match = line.match(/^([a-zA-Z0-9]*)=(.*)$/i) + if (!match) { return } + switch (match[1].toLowerCase()) { + case 'key': + sigKeys.push(match[2]) + break - const issuerKeyId = sigData.signature.packets[0].issuerKeyId.toHex() - const signersUserId = sigData.signature.packets[0].signersUserId - const preferredKeyServer = - sigData.signature.packets[0].preferredKeyServer || - 'https://keys.openpgp.org/' - const text = sigData.getText() - let sigKeys = [] + case 'proof': + result.users[0].claims.push(new Claim(match[2])) + break - text.split('\n').forEach((line, i) => { - const match = line.match(/^([a-zA-Z0-9]*)\=(.*)$/i) - if (!match) { - return - } - switch (match[1].toLowerCase()) { - case 'key': - sigKeys.push(match[2]) - break - - case 'proof': - result.users[0].claims.push(new Claim(match[2])) - break - - default: - break - } - }) - - // Try overruling key - if (sigKeys.length > 0) { - try { - result.key.uri = sigKeys[0] - result.key.data = await keys.fetchURI(result.key.uri) - result.key.fetchMethod = result.key.uri.split(':')[0] - } catch (e) {} + default: + break } - // Try WKD - if (!result.key.data && signersUserId) { - try { - result.key.uri = `wkd:${signersUserId}` - result.key.data = await keys.fetchURI(result.key.uri) - result.key.fetchMethod = 'wkd' - } catch (e) {} - } - // Try HKP - if (!result.key.data) { - try { - const match = preferredKeyServer.match(/^(.*\:\/\/)?([^/]*)(?:\/)?$/i) - result.key.uri = `hkp:${match[2]}:${ - issuerKeyId ? issuerKeyId : signersUserId - }` - result.key.data = await keys.fetchURI(result.key.uri) - result.key.fetchMethod = 'hkp' - } catch (e) { - reject(new Error('key_not_found')) - return - } - } - - result.fingerprint = result.key.data.keyPacket.getFingerprint() - - result.users[0].claims.forEach((claim) => { - claim.fingerprint = result.fingerprint - }) - - const primaryUserData = await result.key.data.getPrimaryUser() - let userData - - if (signersUserId) { - result.key.data.users.forEach((user) => { - if (user.userId.email == signersUserId) { - userData = user - } - }) - } - if (!userData) { - userData = primaryUserData.user - } - - result.users[0].userData = { - id: userData.userId ? userData.userId.userid : null, - name: userData.userId ? userData.userId.name : null, - email: userData.userId ? userData.userId.email : null, - comment: userData.userId ? userData.userId.comment : null, - isPrimary: primaryUserData.user.userId.userid === userData.userId.userid, - } - - result.primaryUserIndex = result.users[0].userData.isPrimary ? 0 : null - - resolve(result) }) + + // Try overruling key + if (sigKeys.length > 0) { + try { + result.key.uri = sigKeys[0] + result.key.data = await keys.fetchURI(result.key.uri) + result.key.fetchMethod = result.key.uri.split(':')[0] + } catch (e) {} + } + // Try WKD + if (!result.key.data && signersUserId) { + try { + result.key.uri = `wkd:${signersUserId}` + result.key.data = await keys.fetchURI(result.key.uri) + result.key.fetchMethod = 'wkd' + } catch (e) {} + } + // Try HKP + if (!result.key.data) { + try { + const match = preferredKeyServer.match(/^(.*:\/\/)?([^/]*)(?:\/)?$/i) + result.key.uri = `hkp:${match[2]}:${issuerKeyId || signersUserId}` + result.key.data = await keys.fetchURI(result.key.uri) + result.key.fetchMethod = 'hkp' + } catch (e) { + throw new Error('key_not_found') + } + } + + result.fingerprint = result.key.data.keyPacket.getFingerprint() + + result.users[0].claims.forEach((claim) => { + claim.fingerprint = result.fingerprint + }) + + const primaryUserData = await result.key.data.getPrimaryUser() + let userData + + if (signersUserId) { + result.key.data.users.forEach((user) => { + if (user.userId.email === signersUserId) { + userData = user + } + }) + } + if (!userData) { + userData = primaryUserData.user + } + + result.users[0].userData = { + id: userData.userId ? userData.userId.userid : null, + name: userData.userId ? userData.userId.name : null, + email: userData.userId ? userData.userId.email : null, + comment: userData.userId ? userData.userId.comment : null, + isPrimary: primaryUserData.user.userId.userid === userData.userId.userid + } + + result.primaryUserIndex = result.users[0].userData.isPrimary ? 0 : null + + return result } exports.process = process diff --git a/src/utils.js b/src/utils.js index 0b6399b..fd5430c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -32,10 +32,10 @@ const generateProxyURL = (type, data, opts) => { try { validator.isFQDN(opts.proxy.hostname) } catch (err) { - throw new Error(`Invalid proxy hostname`) + throw new Error('Invalid proxy hostname') } - let queryStrings = [] + const queryStrings = [] Object.keys(data).forEach((key) => { queryStrings.push(`${key}=${encodeURIComponent(data[key])}`) @@ -56,13 +56,10 @@ const generateClaim = (fingerprint, format) => { switch (format) { case E.ClaimFormat.URI: return `openpgp4fpr:${fingerprint}` - break case E.ClaimFormat.MESSAGE: return `[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]` - break case E.ClaimFormat.FINGERPRINT: return fingerprint - break default: throw new Error('No valid claim format') } diff --git a/src/verifications.js b/src/verifications.js index 812db1a..c0e3789 100644 --- a/src/verifications.js +++ b/src/verifications.js @@ -39,31 +39,26 @@ const runJSON = (proofData, checkPath, checkClaim, checkRelation) => { return result } - if (checkPath.length == 0) { + if (checkPath.length === 0) { switch (checkRelation) { - default: - case E.ClaimRelation.CONTAINS: - re = new RegExp(checkClaim, 'gi') - return re.test(proofData.replace(/\r?\n|\r|\\/g, '')) - break - case E.ClaimRelation.EQUALS: return ( - proofData.replace(/\r?\n|\r|\\/g, '').toLowerCase() == + proofData.replace(/\r?\n|\r|\\/g, '').toLowerCase() === checkClaim.toLowerCase() ) - break case E.ClaimRelation.ONEOF: re = new RegExp(checkClaim, 'gi') return re.test(proofData.join('|')) - break + + case E.ClaimRelation.CONTAINS: + default: + re = new RegExp(checkClaim, 'gi') + return re.test(proofData.replace(/\r?\n|\r|\\/g, '')) } } - try { - checkPath[0] in proofData - } catch (e) { + if (!(checkPath[0] in proofData)) { throw new Error('err_json_structure_incorrect') } @@ -83,10 +78,10 @@ const runJSON = (proofData, checkPath, checkClaim, checkRelation) => { * @returns {object} */ const run = (proofData, claimData, fingerprint) => { - let res = { + const res = { result: false, completed: false, - errors: [], + errors: [] } switch (claimData.proof.request.format) {