(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.doip = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { if (!buffer) buffer = this.arrayBuffer() return buffer } Object.defineProperty(this, 'responseBody', { get }) // match Node.js headers object this.headers = {} for (const [key, value] of res.headers.entries()) { this.headers[key.toLowerCase()] = value } } } const mkrequest = (statusCodes, method, encoding, headers, baseurl) => async (_url, body, _headers = {}) => { _url = baseurl + (_url || '') let parsed = new URL(_url) if (!headers) headers = {} if (parsed.username) { headers.Authorization = 'Basic ' + btoa(parsed.username + ':' + parsed.password) parsed = new URL(parsed.protocol + '//' + parsed.host + parsed.pathname + parsed.search) } if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { throw new Error(`Unknown protocol, ${parsed.protocol}`) } if (body) { if (body instanceof ArrayBuffer || ArrayBuffer.isView(body) || typeof body === 'string' ) { // noop } else if (typeof body === 'object') { body = JSON.stringify(body) headers['Content-Type'] = 'application/json' } else { throw new Error('Unknown body type.') } } _headers = new Headers({ ...(headers || {}), ..._headers }) const resp = await fetch(parsed, { method, headers: _headers, body }) resp.statusCode = resp.status if (!statusCodes.has(resp.status)) { throw new StatusError(resp) } if (encoding === 'json') return resp.json() else if (encoding === 'buffer') return resp.arrayBuffer() else if (encoding === 'string') return resp.text() else return resp } module.exports = core(mkrequest) },{"./core":2}],2:[function(require,module,exports){ 'use strict' const encodings = new Set(['json', 'buffer', 'string']) module.exports = mkrequest => (...args) => { const statusCodes = new Set() let method let encoding let headers let baseurl = '' args.forEach(arg => { if (typeof arg === 'string') { if (arg.toUpperCase() === arg) { if (method) { const msg = `Can't set method to ${arg}, already set to ${method}.` throw new Error(msg) } else { method = arg } } else if (arg.startsWith('http:') || arg.startsWith('https:')) { baseurl = arg } else { if (encodings.has(arg)) { encoding = arg } else { throw new Error(`Unknown encoding, ${arg}`) } } } else if (typeof arg === 'number') { statusCodes.add(arg) } else if (typeof arg === 'object') { if (Array.isArray(arg) || arg instanceof Set) { arg.forEach(code => statusCodes.add(code)) } else { if (headers) { throw new Error('Cannot set headers twice.') } headers = arg } } else { throw new Error(`Unknown type: ${typeof arg}`) } }) if (!method) method = 'GET' if (statusCodes.size === 0) { statusCodes.add(200) } return mkrequest(statusCodes, method, encoding, headers, baseurl) } },{}],3:[function(require,module,exports){ },{}],4:[function(require,module,exports){ 'use strict'; module.exports = value => { if (Object.prototype.toString.call(value) !== '[object Object]') { return false; } const prototype = Object.getPrototypeOf(value); return prototype === null || prototype === Object.prototype; }; },{}],5:[function(require,module,exports){ 'use strict'; const isOptionObject = require('is-plain-obj'); const {hasOwnProperty} = Object.prototype; const {propertyIsEnumerable} = Object; const defineProperty = (object, name, value) => Object.defineProperty(object, name, { value, writable: true, enumerable: true, configurable: true }); const globalThis = this; const defaultMergeOptions = { concatArrays: false, ignoreUndefined: false }; const getEnumerableOwnPropertyKeys = value => { const keys = []; for (const key in value) { if (hasOwnProperty.call(value, key)) { keys.push(key); } } /* istanbul ignore else */ if (Object.getOwnPropertySymbols) { const symbols = Object.getOwnPropertySymbols(value); for (const symbol of symbols) { if (propertyIsEnumerable.call(value, symbol)) { keys.push(symbol); } } } return keys; }; function clone(value) { if (Array.isArray(value)) { return cloneArray(value); } if (isOptionObject(value)) { return cloneOptionObject(value); } return value; } function cloneArray(array) { const result = array.slice(0, 0); getEnumerableOwnPropertyKeys(array).forEach(key => { defineProperty(result, key, clone(array[key])); }); return result; } function cloneOptionObject(object) { const result = Object.getPrototypeOf(object) === null ? Object.create(null) : {}; getEnumerableOwnPropertyKeys(object).forEach(key => { defineProperty(result, key, clone(object[key])); }); return result; } /** * @param {*} merged already cloned * @param {*} source something to merge * @param {string[]} keys keys to merge * @param {Object} config Config Object * @returns {*} cloned Object */ const mergeKeys = (merged, source, keys, config) => { keys.forEach(key => { if (typeof source[key] === 'undefined' && config.ignoreUndefined) { return; } // Do not recurse into prototype chain of merged if (key in merged && merged[key] !== Object.getPrototypeOf(merged)) { defineProperty(merged, key, merge(merged[key], source[key], config)); } else { defineProperty(merged, key, clone(source[key])); } }); return merged; }; /** * @param {*} merged already cloned * @param {*} source something to merge * @param {Object} config Config Object * @returns {*} cloned Object * * see [Array.prototype.concat ( ...arguments )](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat) */ const concatArrays = (merged, source, config) => { let result = merged.slice(0, 0); let resultIndex = 0; [merged, source].forEach(array => { const indices = []; // `result.concat(array)` with cloning for (let k = 0; k < array.length; k++) { if (!hasOwnProperty.call(array, k)) { continue; } indices.push(String(k)); if (array === merged) { // Already cloned defineProperty(result, resultIndex++, array[k]); } else { defineProperty(result, resultIndex++, clone(array[k])); } } // Merge non-index keys result = mergeKeys(result, array, getEnumerableOwnPropertyKeys(array).filter(key => !indices.includes(key)), config); }); return result; }; /** * @param {*} merged already cloned * @param {*} source something to merge * @param {Object} config Config Object * @returns {*} cloned Object */ function merge(merged, source, config) { if (config.concatArrays && Array.isArray(merged) && Array.isArray(source)) { return concatArrays(merged, source, config); } if (!isOptionObject(source) || !isOptionObject(merged)) { return clone(source); } return mergeKeys(merged, source, getEnumerableOwnPropertyKeys(source), config); } module.exports = function (...options) { const config = merge(clone(defaultMergeOptions), (this !== globalThis && this) || {}, defaultMergeOptions); let merged = {_: {}}; for (const option of options) { if (option === undefined) { continue; } if (!isOptionObject(option)) { throw new TypeError('`' + option + '` is not an Option Object'); } merged = merge(merged, {_: option}, config); } return merged._; }; },{"is-plain-obj":4}],6:[function(require,module,exports){ (function(module) { 'use strict'; module.exports.is_uri = is_iri; module.exports.is_http_uri = is_http_iri; module.exports.is_https_uri = is_https_iri; module.exports.is_web_uri = is_web_iri; // Create aliases module.exports.isUri = is_iri; module.exports.isHttpUri = is_http_iri; module.exports.isHttpsUri = is_https_iri; module.exports.isWebUri = is_web_iri; // private function // internal URI spitter method - direct from RFC 3986 var splitUri = function(uri) { var splitted = uri.match(/(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/); return splitted; }; function is_iri(value) { if (!value) { return; } // check for illegal characters if (/[^a-z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\.\-\_\~\%]/i.test(value)) return; // check for hex escapes that aren't complete if (/%[^0-9a-f]/i.test(value)) return; if (/%[0-9a-f](:?[^0-9a-f]|$)/i.test(value)) return; var splitted = []; var scheme = ''; var authority = ''; var path = ''; var query = ''; var fragment = ''; var out = ''; // from RFC 3986 splitted = splitUri(value); scheme = splitted[1]; authority = splitted[2]; path = splitted[3]; query = splitted[4]; fragment = splitted[5]; // scheme and path are required, though the path can be empty if (!(scheme && scheme.length && path.length >= 0)) return; // if authority is present, the path must be empty or begin with a / if (authority && authority.length) { if (!(path.length === 0 || /^\//.test(path))) return; } else { // if authority is not present, the path must not start with // if (/^\/\//.test(path)) return; } // scheme must begin with a letter, then consist of letters, digits, +, ., or - if (!/^[a-z][a-z0-9\+\-\.]*$/.test(scheme.toLowerCase())) return; // re-assemble the URL per section 5.3 in RFC 3986 out += scheme + ':'; if (authority && authority.length) { out += '//' + authority; } out += path; if (query && query.length) { out += '?' + query; } if (fragment && fragment.length) { out += '#' + fragment; } return out; } function is_http_iri(value, allowHttps) { if (!is_iri(value)) { return; } var splitted = []; var scheme = ''; var authority = ''; var path = ''; var port = ''; var query = ''; var fragment = ''; var out = ''; // from RFC 3986 splitted = splitUri(value); scheme = splitted[1]; authority = splitted[2]; path = splitted[3]; query = splitted[4]; fragment = splitted[5]; if (!scheme) return; if(allowHttps) { if (scheme.toLowerCase() != 'https') return; } else { if (scheme.toLowerCase() != 'http') return; } // fully-qualified URIs must have an authority section that is // a valid host if (!authority) { return; } // enable port component if (/:(\d+)$/.test(authority)) { port = authority.match(/:(\d+)$/)[0]; authority = authority.replace(/:\d+$/, ''); } out += scheme + ':'; out += '//' + authority; if (port) { out += port; } out += path; if(query && query.length){ out += '?' + query; } if(fragment && fragment.length){ out += '#' + fragment; } return out; } function is_https_iri(value) { return is_http_iri(value, true); } function is_web_iri(value) { return (is_http_iri(value) || is_https_iri(value)); } })(module); },{}],7:[function(require,module,exports){ const utils = require('./utils') const runOnJson = (res, proofData, checkPath, checkClaim, checkRelation) => { let re if (res.isVerified || !proofData) { return res } if (Array.isArray(proofData)) { proofData.forEach((item, i) => { res = runOnJson(res, item, checkPath, checkClaim, checkRelation) }) return res } if (checkPath.length == 0) { switch (checkRelation) { default: case 'contains': re = new RegExp(checkClaim.replace('[', '\\[').replace(']', '\\]'), "gi") res.isVerified = re.test(proofData.replace(/\r?\n|\r/, '')) break case 'equals': res.isVerified = proofData.replace(/\r?\n|\r/, '').toLowerCase() == checkClaim.toLowerCase() break case 'oneOf': re = new RegExp(checkClaim, "gi") res.isVerified = re.test(proofData.join("|")) break } return res } try { checkPath[0] in proofData } catch(e) { res.errors.push('err_data_structure_incorrect') return res } res = runOnJson(res, proofData[checkPath[0]], checkPath.slice(1), checkClaim, checkRelation) return res } const run = (proofData, spData) => { let res = { isVerified: false, errors: [] } switch (spData.proof.format) { case 'json': res = runOnJson(res, proofData, spData.claim.path, utils.generateClaim(spData.claim.fingerprint, spData.claim.format), spData.claim.relation) break case 'text': re = new RegExp(utils.generateClaim(spData.claim.fingerprint, spData.claim.format), "gi") res = re.test(proofData.replace(/\r?\n|\r/, '')) break } return res } exports.run = run },{"./utils":24}],8:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 mergeOptions = require('merge-options') const validUrl = require('valid-url') const serviceproviders = require('./serviceproviders') const claimVerification = require('./claimVerification') const utils = require('./utils') const verify = async (uri, fingerprint, opts) => { if (!fingerprint) { fingerprint = null } if (!opts) { opts = {} } const defaultOpts = { returnMatchesOnly: false, proxyPolicy: 'adaptive', doipProxyHostname: 'proxy.keyoxide.org' } opts = mergeOptions(defaultOpts, opts) if (!validUrl.isUri(uri)) { throw new Error('Not a valid URI') } const spMatches = serviceproviders.match(uri, opts) if ('returnMatchesOnly' in opts && opts.returnMatchesOnly) { return spMatches } let claimVerificationDone = false, claimVerificationResult, sp, iSp = 0, res, proofData, spData while (!claimVerificationDone && iSp < spMatches.length) { spData = spMatches[iSp] spData.claim.fingerprint = fingerprint res = null if (spData.customRequestHandler instanceof Function) { proofData = await spData.customRequestHandler(spData, opts) } else if (!spData.proof.useProxy || 'proxyPolicy' in opts && !opts.useProxyWhenNeeded) { proofData = await serviceproviders.directRequestHandler(spData, opts) } else { proofData = await serviceproviders.proxyRequestHandler(spData, opts) } if (proofData) { claimVerificationResult = claimVerification.run(proofData, spData) if (claimVerificationResult.errors.length == 0) { claimVerificationDone = true } } iSp++ } if (!claimVerificationResult) { claimVerificationResult = { isVerified: false } } return { isVerified: claimVerificationResult.isVerified, serviceproviderData: spData } } exports.verify = verify exports.serviceproviders = serviceproviders exports.claimVerification = claimVerification exports.utils = utils },{"./claimVerification":7,"./serviceproviders":9,"./utils":24,"merge-options":5,"valid-url":6}],9:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 bent = require('bent') const req = bent('GET') const utils = require('./utils') const list = [ 'dns', 'xmpp', 'twitter', 'reddit', 'liberapay', 'hackernews', 'lobsters', 'devto', 'gitea', 'gitlab', 'github', 'mastodon', 'fediverse', 'discourse', ] const data = { dns: require('./serviceproviders/dns'), xmpp: require('./serviceproviders/xmpp'), twitter: require('./serviceproviders/twitter'), reddit: require('./serviceproviders/reddit'), liberapay: require('./serviceproviders/liberapay'), hackernews: require('./serviceproviders/hackernews'), lobsters: require('./serviceproviders/lobsters'), devto: require('./serviceproviders/devto'), gitea: require('./serviceproviders/gitea'), gitlab: require('./serviceproviders/gitlab'), github: require('./serviceproviders/github'), mastodon: require('./serviceproviders/mastodon'), fediverse: require('./serviceproviders/fediverse'), discourse: require('./serviceproviders/discourse'), } const match = (uri, opts) => { let matches = [], sp list.forEach((spName, i) => { sp = data[spName] if (sp.reURI.test(uri)) { matches.push(sp.processURI(uri, opts)) } }) return matches } const directRequestHandler = async (spData, opts) => { const res = await req(spData.proof.fetch ? spData.proof.fetch : spData.proof.uri, null, { Accept: 'application/json' }) switch (spData.proof.format) { case 'json': return await res.json() break case 'text': return await res.text() break default: throw new Error('No specified proof data format') break } } const proxyRequestHandler = async (spData, opts) => { const url = spData.proof.fetch ? spData.proof.fetch : spData.proof.uri const res = await req(utils.generateProxyURL(spData.proof.format, url, opts), null, { Accept: 'application/json' }) const json = await res.json() return json.content } exports.list = list exports.data = data exports.match = match exports.directRequestHandler = directRequestHandler exports.proxyRequestHandler = proxyRequestHandler },{"./serviceproviders/devto":10,"./serviceproviders/discourse":11,"./serviceproviders/dns":12,"./serviceproviders/fediverse":13,"./serviceproviders/gitea":14,"./serviceproviders/github":15,"./serviceproviders/gitlab":16,"./serviceproviders/hackernews":17,"./serviceproviders/liberapay":18,"./serviceproviders/lobsters":19,"./serviceproviders/mastodon":20,"./serviceproviders/reddit":21,"./serviceproviders/twitter":22,"./serviceproviders/xmpp":23,"./utils":24,"bent":1}],10:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/dev\.to\/(.*)\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'devto' }, profile: { display: match[1], uri: `https://dev.to/${match[1]}`, qr: null }, proof: { uri: uri, fetch: `https://dev.to/api/articles/${match[1]}/${match[2]}`, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['body_markdown'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://dev.to/alice/post', shouldMatch: true }, { uri: 'https://dev.to/alice/post/', shouldMatch: true }, { uri: 'https://domain.org/alice/post', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],11:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/(.*)\/u\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'discourse' }, profile: { display: `${match[2]}@${match[1]}`, uri: uri, qr: null }, proof: { uri: uri, fetch: `https://${match[1]}/u/${match[2]}.json`, useProxy: true, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['user', 'bio_raw'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://domain.org/u/alice', shouldMatch: true }, { uri: 'https://domain.org/u/alice/', shouldMatch: true }, { uri: 'https://domain.org/alice', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],12:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 dns = require('dns') const bent = require('bent') const req = bent('GET') const utils = require('../utils') const reURI = /^dns:([a-zA-Z0-9\.\-\_]*)(?:\?(.*))?/ const customRequestHandler = async (spData, opts) => { if (('resolveTxt' in dns)) { const prom = async () => { return new Promise((resolve, reject) => { dns.resolveTxt(spData.profile.display, (err, records) => { if (err) reject(err) resolve(records) }) }) } return { hostname: spData.profile.display, records: { txt: await prom() } } } else { const res = await req(spData.proof.uri, null, { Accept: 'application/json' }) const json = await res.json() return json } } const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'dns' }, profile: { display: match[1], uri: `https://${match[1]}`, qr: null }, proof: { uri: utils.generateProxyURL('dns', match[1]), fetch: null, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'uri', path: ['records', 'txt'], relation: 'contains' }, customRequestHandler: customRequestHandler } } const tests = [ { uri: 'dns:domain.org', shouldMatch: true }, { uri: 'dns:domain.org?type=TXT', shouldMatch: true }, { uri: 'https://domain.org', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{"../utils":24,"bent":1,"dns":3}],13:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/(.*)\/users\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'fediverse' }, profile: { display: `@${match[2]}@${match[1]}`, uri: uri, qr: null }, proof: { uri: uri, fetch: null, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'fingerprint', path: ['summary'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://domain.org/users/alice', shouldMatch: true }, { uri: 'https://domain.org/users/alice/', shouldMatch: true }, { uri: 'https://domain.org/alice', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],14:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/(.*)\/(.*)\/gitea_proof\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'gitea' }, profile: { display: `${match[2]}@${match[1]}`, uri: `https://${match[1]}/${match[2]}`, qr: null }, proof: { uri: uri, fetch: `https://${match[1]}/api/v1/repos/${match[2]}/gitea_proof`, useProxy: true, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['description'], relation: 'equals' }, customRequestHandler: null } } const tests = [ { uri: 'https://domain.org/alice/gitea_proof', shouldMatch: true }, { uri: 'https://domain.org/alice/gitea_proof/', shouldMatch: true }, { uri: 'https://domain.org/alice/other_proof', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],15:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/gist\.github\.com\/(.*)\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'github' }, profile: { display: match[1], uri: `https://github.com/${match[1]}`, qr: null }, proof: { uri: uri, fetch: `https://api.github.com/gists/${match[2]}`, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['files', 'openpgp.md', 'content'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://gist.github.com/Alice/123456789', shouldMatch: true }, { uri: 'https://gist.github.com/Alice/123456789/', shouldMatch: true }, { uri: 'https://domain.org/Alice/123456789', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],16:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 bent = require('bent') const req = bent('GET') const reURI = /^https:\/\/(.*)\/(.*)\/gitlab_proof\/?/ const customRequestHandler = async (spData, opts) => { const match = spData.proof.uri.match(reURI) const urlUser = `https://${match[1]}/api/v4/users?username=${match[2]}` const resUser = await req(urlUser, 'json', { Accept: 'application/json' }) const jsonUser = await resUser.json() const user = jsonUser.find(user => user.username === match[2]) if (!user) { throw new Error(`No user with username ${match[2]}`); } const urlProject = `https://${match[1]}/api/v4/users/${user.id}/projects` const resProject = await req(urlProject, {}, { Accept: 'application/json' }) const jsonProject = await resProject.json() const project = jsonProject.find(proj => proj.path === 'gitlab_proof') if (!project) { throw new Error(`No project at ${spData.proof.uri}`); } return project } const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'gitlab' }, profile: { display: `${match[2]}@${match[1]}`, uri: `https://${match[1]}/${match[2]}`, qr: null }, proof: { uri: uri, fetch: null, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['description'], relation: 'equals' }, customRequestHandler: customRequestHandler } } const tests = [ { uri: 'https://gitlab.domain.org/alice/gitlab_proof', shouldMatch: true }, { uri: 'https://gitlab.domain.org/alice/gitlab_proof/', shouldMatch: true }, { uri: 'https://domain.org/alice/other_proof', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{"bent":1}],17:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/news\.ycombinator\.com\/user\?id=(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'hackernews' }, profile: { display: match[1], uri: uri, qr: null }, proof: { uri: `https://hacker-news.firebaseio.com/v0/user/${match[1]}.json`, fetch: null, useProxy: true, format: 'json' }, claim: { fingerprint: null, format: 'uri', path: ['about'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://news.ycombinator.com/user?id=Alice', shouldMatch: true }, { uri: 'https://news.ycombinator.com/user?id=Alice/', shouldMatch: true }, { uri: 'https://domain.org/user?id=Alice', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],18:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/liberapay\.com\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'liberapay' }, profile: { display: match[1], uri: uri, qr: null }, proof: { uri: uri, fetch: `https://liberapay.com/${match[1]}/public.json`, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['statements', 'content'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://liberapay.com/alice', shouldMatch: true }, { uri: 'https://liberapay.com/alice/', shouldMatch: true }, { uri: 'https://domain.org/alice', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],19:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/lobste\.rs\/u\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'lobsters' }, profile: { display: match[1], uri: uri, qr: null }, proof: { uri: `https://lobste.rs/u/${match[1]}.json`, fetch: null, useProxy: true, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['about'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://lobste.rs/u/Alice', shouldMatch: true }, { uri: 'https://lobste.rs/u/Alice/', shouldMatch: true }, { uri: 'https://domain.org/u/Alice', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],20:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/(.*)\/@(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'mastodon' }, profile: { display: `@${match[2]}@${match[1]}`, uri: uri, qr: null }, proof: { uri: uri, fetch: null, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'fingerprint', path: ['attachment', 'value'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://domain.org/@alice', shouldMatch: true }, { uri: 'https://domain.org/@alice/', shouldMatch: true }, { uri: 'https://domain.org/alice', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],21:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/(?:www\.)?reddit\.com\/user\/(.*)\/comments\/(.*)\/(.*)\/?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'reddit' }, profile: { display: match[1], uri: `https://www.reddit.com/user/${match[1]}`, qr: null }, proof: { uri: uri, fetch: `https://www.reddit.com/user/${match[1]}/comments/${match[2]}.json`, useProxy: true, format: 'json' }, claim: { fingerprint: null, format: 'message', path: ['data', 'children', 'data', 'selftext'], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://www.reddit.com/user/Alice/comments/123456/post', shouldMatch: true }, { uri: 'https://www.reddit.com/user/Alice/comments/123456/post/', shouldMatch: true }, { uri: 'https://reddit.com/user/Alice/comments/123456/post', shouldMatch: true }, { uri: 'https://reddit.com/user/Alice/comments/123456/post/', shouldMatch: true }, { uri: 'https://domain.org/user/Alice/comments/123456/post', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],22:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^https:\/\/twitter\.com\/(.*)\/status\/([0-9]*)(?:\?.*)?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'web', name: 'twitter' }, profile: { display: `@${match[1]}`, uri: `https://twitter.com/${match[1]}`, qr: null }, proof: { uri: uri, fetch: `https://mobile.twitter.com/${match[1]}/status/${match[2]}`, useProxy: false, format: 'text' }, claim: { fingerprint: null, format: 'message', path: [], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'https://twitter.com/alice/status/1234567890123456789', shouldMatch: true }, { uri: 'https://twitter.com/alice/status/1234567890123456789/', shouldMatch: true }, { uri: 'https://domain.org/alice/status/1234567890123456789', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],23:[function(require,module,exports){ /* Copyright 2020 Yarmo Mackenbach Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, 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 reURI = /^xmpp:([a-zA-Z0-9\.\-\_]*)@([a-zA-Z0-9\.\-\_]*)(?:\?(.*))?/ const processURI = (uri, opts) => { if (!opts) { opts = {} } const match = uri.match(reURI) return { serviceprovider: { type: 'communication', name: 'xmpp' }, profile: { display: `${match[1]}@${match[2]}`, uri: uri, qr: uri }, proof: { uri: 'xmppVcardServerDomain' in opts ? `https://${opts.xmppVcardServerDomain}/api/vcard/${match[1]}@${match[2]}/DESC` : null, fetch: null, useProxy: false, format: 'json' }, claim: { fingerprint: null, format: 'message', path: [], relation: 'contains' }, customRequestHandler: null } } const tests = [ { uri: 'xmpp:alice@domain.org', shouldMatch: true }, { uri: 'xmpp:alice@domain.org?omemo-sid-123456789=A1B2C3D4E5F6G7H8I9', shouldMatch: true }, { uri: 'https://domain.org', shouldMatch: false } ] exports.reURI = reURI exports.processURI = processURI exports.tests = tests },{}],24:[function(require,module,exports){ const generateProxyURL = (type, url, opts) => { if (!opts || !opts.doipProxyHostname) { return null } return `https://${opts.doipProxyHostname}/api/1/get/${type}/${encodeURIComponent(url)}` } const generateClaim = (fingerprint, format) => { switch (format) { case 'uri': return `openpgp4fpr:${fingerprint}` break; case 'message': return `[Verifying my OpenPGP key: openpgp4fpr:${fingerprint}]` break; case 'fingerprint': return fingerprint break; default: throw new Error('No valid claim format') } } exports.generateProxyURL = generateProxyURL exports.generateClaim = generateClaim },{}]},{},[8])(8) });