keybase-proofs
Version:
Publicly-verifiable proofs of identity
688 lines (653 loc) • 28 kB
JavaScript
// 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);