UNPKG

keybase-proofs

Version:

Publicly-verifiable proofs of identity

688 lines (653 loc) 28 kB
// Generated by IcedCoffeeScript 108.0.11 (function() { var BaseBearerToken, BaseScraper, TwitterBearerToken, TwitterScraper, bearer_token, constants, decode_sig, iced, make_ids, schema, sncmp, urlmod, v_codes, ws_normalize, __iced_k, __iced_k_noop, _bearer_token, _ref, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; iced = require('iced-runtime'); __iced_k = __iced_k_noop = function() {}; _ref = require('./base'), sncmp = _ref.sncmp, BaseScraper = _ref.BaseScraper, BaseBearerToken = _ref.BaseBearerToken; make_ids = require('../base').make_ids; constants = require('../constants').constants; v_codes = constants.v_codes; decode_sig = require('kbpgp').ukm.decode_sig; urlmod = require('url'); schema = require('../schema3'); ws_normalize = function(x) { var v; v = x.split(/[\t\r\n ]+/); if (v.length && v[0].length === 0) { v.shift(); } if (v.length && v.slice(-1)[0].length === 0) { v.pop(); } return v.join(' '); }; TwitterBearerToken = (function(_super) { __extends(TwitterBearerToken, _super); function TwitterBearerToken(_arg) { this.base = _arg.base; TwitterBearerToken.__super__.constructor.call(this, { name: "Twitter", base: this.base, access_token_url: "https://api.twitter.com/oauth2/token", scope: ['history', 'read'] }); } return TwitterBearerToken; })(BaseBearerToken); _bearer_token = null; bearer_token = function(_arg) { var base; base = _arg.base; if (!_bearer_token) { _bearer_token = new TwitterBearerToken({ base: base }); } return _bearer_token; }; exports.TwitterScraper = TwitterScraper = (function(_super) { var username_regexp; __extends(TwitterScraper, _super); username_regexp = /^[a-z0-9_-]{2,15}$/; function TwitterScraper(opts) { this.auth = opts.auth; TwitterScraper.__super__.constructor.call(this, opts); } TwitterScraper.prototype._check_args = function(args) { if (!(args.username != null)) { return new Error("Bad args to Twitter proof: no username given"); } else if (!(args.name != null) || (args.name !== 'twitter')) { return new Error("Bad args to Twitter proof: type is " + args.name); } else if (!args.username.match(username_regexp)) { return new Error("Invalid username passed to Twitter proof"); } else { return null; } }; TwitterScraper.prototype.hunt2 = function(_arg, cb) { var api_url, endpoint_name, err, human_url, i, id, json, name, out, proof_text_check, rc, remote_id, text, u, username, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); username = _arg.username, name = _arg.name, proof_text_check = _arg.proof_text_check; out = {}; rc = v_codes.OK; if ((err = this._check_args({ username: username, name: name })) != null) { return cb(err, out); } endpoint_name = "/2/tweets/search/recent"; u = urlmod.format({ host: "api.twitter.com", protocol: "https:", pathname: endpoint_name, query: { query: "\"Verifying myself\" \"Keybase.io\" from:" + username } }); (function(_this) { return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper.hunt2" }); _this._get_body_api({ url: u, endpoint_name: endpoint_name }, __iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; rc = arguments[1]; return json = arguments[2]; }; })(), lineno: 73 })); __iced_deferrals._fulfill(); }); })(this)((function(_this) { return function() { var _i, _len, _ref1, _ref2, _ref3; _this.log("| search index " + u + " -> " + rc); if (rc !== v_codes.OK) { } else if ((typeof json === "undefined" || json === null) || (json.length === 0)) { rc = v_codes.EMPTY_JSON; } else if (json.data == null) { if (((_ref1 = json.meta) != null ? _ref1.result_count : void 0) === 0) { rc = v_codes.NOT_FOUND; } else { rc = v_codes.INVALID_JSON; } } else { rc = v_codes.NOT_FOUND; _ref2 = json.data; for (i = _i = 0, _len = _ref2.length; _i < _len; i = ++_i) { _ref3 = _ref2[i], text = _ref3.text, id = _ref3.id; if ((_this.find_sig_in_tweet({ inside: text, proof_text_check: proof_text_check })) === v_codes.OK) { _this.log("| found valid tweet in stream @ " + i); rc = v_codes.OK; remote_id = id; api_url = human_url = _this._id_to_url(username, remote_id); out = { remote_id: remote_id, api_url: api_url, human_url: human_url }; break; } } } out.rc = rc; return cb(err, out); }; })(this)); }; TwitterScraper.prototype.users_lookup = function(_arg, cb) { var batch_size, cursor_wait, dict, done, err, i, identifier, ids, include_entities, input_list, j, json, key, query, r, rc, res, responses, screen_names, u, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); ids = _arg.ids, screen_names = _arg.screen_names, cursor_wait = _arg.cursor_wait, include_entities = _arg.include_entities; if (ids && screen_names) { throw new Error('users_lookup cannot take ids and screen_names'); } input_list = ids || screen_names; err = null; responses = []; cursor_wait = cursor_wait != null ? cursor_wait : 100; i = 0; include_entities = include_entities != null ? include_entities : false; batch_size = 100; done = false; (function(_this) { return (function(__iced_k) { var _while; _while = function(__iced_k) { var _break, _continue, _next; _break = __iced_k; _continue = function() { return iced.trampoline(function() { return _while(__iced_k); }); }; _next = _continue; if (!!done) { return _break(); } else { j = Math.min(i + batch_size, input_list.length); query = { include_entities: include_entities }; if (ids != null) { query.user_id = ids.slice(i, j).join(','); } else { query.screen_name = screen_names.slice(i, j).join(','); } u = urlmod.format({ host: "api.twitter.com", protocol: "https:", pathname: "/1.1/users/lookup.json", query: query }); (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper.users_lookup" }); _this._get_body_api({ url: u }, __iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; rc = arguments[1]; return json = arguments[2]; }; })(), lineno: 129 })); __iced_deferrals._fulfill(); })(function() { _this.log("| users_lookup " + i + "..." + j); (function(__iced_k) { if (err != null) { return __iced_k(done = true); } else { (function(__iced_k) { if (rc !== v_codes.OK) { err = new Error("failed to scrape; not ok " + rc); return __iced_k(done = true); } else { (function(__iced_k) { var _i, _len; if (!(typeof json !== "undefined" && json !== null ? json.length : void 0)) { err = new Error("failed to scrape; empty json " + v_codes.EMPTY_JSON); return __iced_k(done = true); } else { for (_i = 0, _len = json.length; _i < _len; _i++) { u = json[_i]; responses.push(u); } (function(__iced_k) { if (j !== input_list.length) { i = j; (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper.users_lookup" }); setTimeout(__iced_deferrals.defer({ lineno: 143 }), cursor_wait); __iced_deferrals._fulfill(); })(__iced_k); } else { return __iced_k(done = true); } })(function() { return __iced_k(_this.log("| got " + json.length + " more; total=" + responses.length)); }); } })(__iced_k); } })(__iced_k); } })(_next); }); } }; _while(__iced_k); }); })(this)((function(_this) { return function() { var _i, _j, _len, _len1; if (responses != null ? responses.length : void 0) { dict = {}; key = ids != null ? "id_str" : "screen_name"; for (_i = 0, _len = responses.length; _i < _len; _i++) { r = responses[_i]; dict[r[key]] = r; } res = []; for (i = _j = 0, _len1 = input_list.length; _j < _len1; i = ++_j) { identifier = input_list[i]; res[i] = dict[identifier] || null; } } return cb(err, res); }; })(this)); }; TwitterScraper.prototype.get_follower_ids = function(_arg, cb) { var cursor, cursor_wait, done, err, friends, json, rc, res, stop_at, u, username, x, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); username = _arg.username, cursor_wait = _arg.cursor_wait, stop_at = _arg.stop_at, friends = _arg.friends; done = false; cursor = -1; err = null; res = []; cursor_wait = cursor_wait != null ? cursor_wait : 1000; stop_at = stop_at || Infinity; (function(_this) { return (function(__iced_k) { var _while; _while = function(__iced_k) { var _break, _continue, _next; _break = __iced_k; _continue = function() { return iced.trampoline(function() { return _while(__iced_k); }); }; _next = _continue; if (!!done) { return _break(); } else { u = urlmod.format({ host: "api.twitter.com", protocol: "https:", pathname: "/1.1/" + (friends ? 'friends' : 'followers') + "/ids.json", query: { stringify_ids: true, cursor: cursor, screen_name: username, count: 5000 } }); (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper.get_follower_ids" }); _this._get_body_api({ url: u }, __iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; rc = arguments[1]; return json = arguments[2]; }; })(), lineno: 181 })); __iced_deferrals._fulfill(); })(function() { _this.log("| get_followers " + username + " (" + cursor + ")"); (function(__iced_k) { if (err != null) { return __iced_k(done = true); } else { (function(__iced_k) { if (rc !== v_codes.OK) { err = new Error("got bad code from get_body_api " + rc); err.code = rc; return __iced_k(done = true); } else { (function(__iced_k) { var _i, _len, _ref1; if ((typeof json !== "undefined" && json !== null ? json.ids : void 0) == null) { err = new Error("got empty_json from get_body_api"); err.code = v_codes.EMPTY_JSON; return __iced_k(done = true); } else { _ref1 = json.ids; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { x = _ref1[_i]; res.push(x); } (function(__iced_k) { if (json.next_cursor && (res.length < stop_at)) { cursor = json.next_cursor_str; (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper.get_follower_ids" }); setTimeout(__iced_deferrals.defer({ lineno: 197 }), cursor_wait); __iced_deferrals._fulfill(); })(__iced_k); } else { return __iced_k(done = true); } })(function() { return __iced_k(_this.log("| got " + json.ids.length + " more; total=" + res.length)); }); } })(__iced_k); } })(__iced_k); } })(_next); }); } }; _while(__iced_k); }); })(this)((function(_this) { return function() { return cb(err, res); }; })(this)); }; TwitterScraper.prototype._id_to_url = function(username, status_id) { return "https://twitter.com/" + username + "/status/" + status_id; }; TwitterScraper.prototype._check_api_url = function(_arg) { var api_url, username; api_url = _arg.api_url, username = _arg.username; return api_url.indexOf("https://twitter.com/" + username + "/") === 0; }; TwitterScraper.prototype._validate_text_check = function(_arg) { var err, msg, proof_text_check, short_id, signature, _ref1; signature = _arg.signature, proof_text_check = _arg.proof_text_check; _ref1 = decode_sig({ armored: signature }), err = _ref1[0], msg = _ref1[1]; if (err == null) { short_id = make_ids(msg.body).short_id; if (proof_text_check.indexOf(" " + short_id + " ") < 0) { err = new Error("Cannot find " + short_id + " in " + proof_text_check); } } return err; }; TwitterScraper.prototype.find_sig_in_tweet = function(_arg) { var html, inside, m, p, proof_text_check, rc, rxx, rxx_text, tweet_p, x; inside = _arg.inside, tweet_p = _arg.tweet_p, proof_text_check = _arg.proof_text_check; if ((tweet_p != null) && (inside == null)) { inside = tweet_p.text(); html = tweet_p.html(); } else { html = null; } inside = ws_normalize(inside); proof_text_check = ws_normalize(proof_text_check); rxx_text = proof_text_check.replace(" on Keybase.io.", " on (Keybase.io|https://t\\.co/\\S*|http://Keybase\\.io\\s)\\."); rxx = new RegExp("^" + rxx_text + ".*"); this.log("+ Checking tweet '" + inside + "' for signature '" + rxx + "'"); this.log("| Incoming check text: " + proof_text_check); if (html != null) { this.log("| html is: " + (html.replace(/\n/g, ' ').trim())); } x = /^(@[a-zA-Z0-9_-]+\s+)/; while ((m = inside.match(x)) != null) { p = m[1]; inside = inside.slice(p.length); this.log("| Stripping off @prefix: " + p); } rc = inside.match(rxx) != null ? v_codes.OK : v_codes.DELETED; this.log("- Result -> " + rc); return rc; }; TwitterScraper.prototype.check_status = function(_arg, cb) { var $, api_url, api_url_matches, author_url_matches, author_username, body_obj, err, new_api_url, proof_text_check, rc, remote_id, schm, tweet_id, tweet_p, u, username, username_from_url, _, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); username = _arg.username, api_url = _arg.api_url, proof_text_check = _arg.proof_text_check, remote_id = _arg.remote_id; if (!(api_url != null ? api_url.length : void 0)) { rc = v_codes.FAILED_PARSE; err = new Error("null api_url API for " + remote_id + "/" + username); this.log("null api_url for " + remote_id + "/" + username); return cb(err, rc); } u = new urlmod.URL('https://api.twitter.com/1/statuses/oembed.json'); u.searchParams.set('id', remote_id.toString()); u.searchParams.set('omit_script', '1'); new_api_url = urlmod.format(u); this.log("| use oembed API " + new_api_url + " for tweet at " + api_url + " (remote_id=" + remote_id + ")"); (function(_this) { return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper.check_status" }); _this._get_url_body({ url: new_api_url, json: true }, __iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; rc = arguments[1]; return body_obj = arguments[2]; }; })(), lineno: 288 })); __iced_deferrals._fulfill(); }); })(this)((function(_this) { return function() { if (!err && rc === v_codes.OK) { schm = schema.dict({ url: schema.string().name('url'), author_url: schema.string().name('author_url'), html: schema.string().name('html') }).allow_extra_keys(); if (err = schm.check(body_obj)) { return cb(err, v_codes.CONTENT_FAILURE); } if (!(sncmp(body_obj.url, api_url))) { err = new Error("returned url field doesn't match api_url (found: " + body_obj.url + ", expected: " + api_url + ")"); return cb(err, v_codes.CONTENT_FAILURE); } api_url_matches = api_url.match(new RegExp("^https://twitter\\.com/([^/]+)/status/(\\d+)(.*)$")); if (!api_url_matches) { err = new Error("api_url field doesn't match regexp, got: " + api_url_matches); return cb(err, v_codes.CONTENT_FAILURE); } _ = api_url_matches[0], username_from_url = api_url_matches[1], tweet_id = api_url_matches[2]; if (!(sncmp(username, username_from_url))) { err = new Error("username from api_url didn't match, expected: " + username + ", got: " + username_from_url); return cb(err, v_codes.BAD_USERNAME); } if (tweet_id !== remote_id.toString()) { return cb(err, v_codes.BAD_REMOTE_ID); } author_url_matches = body_obj.author_url.match(new RegExp("^https://twitter\\.com/(.+)$")); if (!author_url_matches) { err = new Error("author_url doesn't match regexp, got: " + body_obj.author_url); return cb(err, v_codes.CONTENT_FAILURE); } _ = author_url_matches[0], author_username = author_url_matches[1]; if (!(sncmp(username, author_username))) { err = new Error("username from author_url didn't match, expected: " + username + ", got: " + author_username); return cb(err, v_codes.BAD_USERNAME); } if (body_obj.html.length > 1000) { err = new Error("html is " + body_obj.html.length + " characters, not trying to parse it"); return cb(err, v_codes.CONTENT_FAILURE); } $ = _this.libs.cheerio.load(body_obj.html); tweet_p = $('blockquote.twitter-tweet p'); if (tweet_p.length !== 1) { err = new Error("failed to find tweet <p> in returned 'html' field"); return cb(err, v_codes.FAILED_PARSE); } rc = _this.find_sig_in_tweet({ tweet_p: tweet_p, proof_text_check: proof_text_check }); } return cb(err, rc); }; })(this)); }; TwitterScraper.prototype._get_bearer_token = function(cb) { var bt, err, rc, tok, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); bt = bearer_token({ base: this }); (function(_this) { return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper._get_bearer_token" }); bt.get(__iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; return tok = arguments[1]; }; })(), lineno: 349 })); __iced_deferrals._fulfill(); }); })(this)((function(_this) { return function() { rc = typeof err !== "undefined" && err !== null ? v_codes.AUTH_FAILED : v_codes.OK; return cb(err, rc, tok); }; })(this)); }; TwitterScraper.prototype._get_body_api = function(_arg, cb) { var args, body, endpoint_name, err, rc, tok, url, ___iced_passed_deferral, __iced_deferrals, __iced_k; __iced_k = __iced_k_noop; ___iced_passed_deferral = iced.findDeferral(arguments); url = _arg.url, endpoint_name = _arg.endpoint_name; rc = body = err = null; (function(_this) { return (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper._get_body_api" }); _this._get_bearer_token(__iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; rc = arguments[1]; return tok = arguments[2]; }; })(), lineno: 358 })); __iced_deferrals._fulfill(); }); })(this)((function(_this) { return function() { (function(__iced_k) { if (err == null) { _this.log("| HTTP API request for URL '" + url + "'"); args = { url: url, headers: { Authorization: "Bearer " + tok }, method: "get", json: true, log_ratelimit: endpoint_name != null, endpoint_name: endpoint_name }; (function(__iced_k) { __iced_deferrals = new iced.Deferrals(__iced_k, { parent: ___iced_passed_deferral, filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced", funcname: "TwitterScraper._get_body_api" }); _this._get_url_body(args, __iced_deferrals.defer({ assign_fn: (function() { return function() { err = arguments[0]; rc = arguments[1]; return body = arguments[2]; }; })(), lineno: 369 })); __iced_deferrals._fulfill(); })(__iced_k); } else { return __iced_k(); } })(function() { return cb(err, rc, body); }); }; })(this)); }; return TwitterScraper; })(BaseScraper); }).call(this);