keybase-proofs 2.3.26 → 2.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 2.3.28 (2024-07-19)
2
+
3
+ - Use version 2 API for X (formerly known as Twitter) hunting.
4
+
5
+ ## 2.3.27 (2024-07-18)
6
+
7
+ - Fix error handling when an invalid unicode domain is passed to DnsScraper.
8
+
1
9
  ## 2.3.26 (2024-06-17)
2
10
 
3
11
  - Use oauth API for reddit scraping.
package/lib/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by IcedCoffeeScript 108.0.12
1
+ // Generated by IcedCoffeeScript 108.0.11
2
2
  (function() {
3
3
  var constants, d, k, v, _ref;
4
4
 
@@ -172,6 +172,7 @@
172
172
  HTTP_400: 240,
173
173
  HTTP_OTHER: 260,
174
174
  EMPTY_JSON: 270,
175
+ INVALID_JSON: 271,
175
176
  DELETED: 301,
176
177
  SERVICE_DEAD: 302,
177
178
  BAD_SIGNATURE: 303
@@ -143,6 +143,15 @@
143
143
  return b64find(raw, ptc_buf);
144
144
  };
145
145
 
146
+ BaseScraper.prototype._get_rate_limit_header = function(headers, name) {
147
+ var v;
148
+ if ((v = parse_int_or_undefined(headers.get("X-RateLimit-" + name))) || (v = parse_int_or_undefined(headers.get("X-Rate-Limit-" + name)))) {
149
+ return v;
150
+ } else {
151
+ return void 0;
152
+ }
153
+ };
154
+
146
155
  BaseScraper.prototype._get_url_body = function(opts, cb) {
147
156
  var body, err, f, rc, response, rl_limit, rl_remaining, rl_reset, ___iced_passed_deferral, __iced_deferrals, __iced_k, _base;
148
157
  __iced_k = __iced_k_noop;
@@ -172,7 +181,7 @@
172
181
  return response = arguments[1];
173
182
  };
174
183
  })(),
175
- lineno: 88
184
+ lineno: 96
176
185
  }));
177
186
  __iced_deferrals._fulfill();
178
187
  });
@@ -180,9 +189,9 @@
180
189
  return function() {
181
190
  var _base1, _ref;
182
191
  if (opts.log_ratelimit && (typeof response !== "undefined" && response !== null)) {
183
- rl_limit = parse_int_or_undefined(response.headers.get('X-RateLimit-Limit'));
184
- rl_remaining = parse_int_or_undefined(response.headers.get('X-RateLimit-Remaining'));
185
- rl_reset = parse_int_or_undefined(response.headers.get('X-RateLimit-Reset'));
192
+ rl_limit = _this._get_rate_limit_header(response.headers, 'Limit');
193
+ rl_remaining = _this._get_rate_limit_header(response.headers, 'Remaining');
194
+ rl_reset = _this._get_rate_limit_header(response.headers, 'Reset');
186
195
  if (rl_limit != null) {
187
196
  _this.log("| ratelimit info limit=" + rl_limit + " remaining=" + rl_remaining + " reset=" + rl_reset);
188
197
  }
@@ -190,7 +199,8 @@
190
199
  _base1.ratelimit_inform({
191
200
  limit: rl_limit,
192
201
  remaining: rl_remaining,
193
- reset: rl_reset
202
+ reset: rl_reset,
203
+ endpoint_name: opts.endpoint_name
194
204
  });
195
205
  }
196
206
  }
@@ -213,7 +223,7 @@
213
223
  return body = arguments[1];
214
224
  };
215
225
  })(),
216
- lineno: 110
226
+ lineno: 122
217
227
  }));
218
228
  __iced_deferrals._fulfill();
219
229
  })(function() {
@@ -273,7 +283,7 @@
273
283
  funcname: "BaseBearerToken.get"
274
284
  });
275
285
  _this._lock.acquire(__iced_deferrals.defer({
276
- lineno: 148
286
+ lineno: 160
277
287
  }));
278
288
  __iced_deferrals._fulfill();
279
289
  });
@@ -316,7 +326,7 @@
316
326
  return body = arguments[2];
317
327
  };
318
328
  })(),
319
- lineno: 173
329
+ lineno: 185
320
330
  }));
321
331
  __iced_deferrals._fulfill();
322
332
  })(function() {
@@ -151,7 +151,7 @@
151
151
  (function(__iced_k) {
152
152
  __iced_deferrals = new iced.Deferrals(__iced_k, {
153
153
  parent: ___iced_passed_deferral,
154
- filename: "/Users/max/src/keybase/proofs/src/scrapers/dns.iced",
154
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/dns.iced",
155
155
  funcname: "DnsScraper.check_status"
156
156
  });
157
157
  _this._check_status({
@@ -191,28 +191,40 @@ _break()
191
191
  })(this));
192
192
  };
193
193
 
194
+ DnsScraper.prototype._resolve_txt = function(domain, cb) {
195
+ var dnslib, err;
196
+ dnslib = this.libs.dns || dns;
197
+ try {
198
+ return dnslib.resolveTxt(domain, function(err, records) {
199
+ return cb(err, records);
200
+ });
201
+ } catch (_error) {
202
+ err = _error;
203
+ return cb(err);
204
+ }
205
+ };
206
+
194
207
  DnsScraper.prototype._check_status = function(_arg, cb) {
195
- var dnslib, domain, err, proof_text_check, rc, records, ___iced_passed_deferral, __iced_deferrals, __iced_k;
208
+ var domain, err, proof_text_check, rc, records, ___iced_passed_deferral, __iced_deferrals, __iced_k;
196
209
  __iced_k = __iced_k_noop;
197
210
  ___iced_passed_deferral = iced.findDeferral(arguments);
198
211
  domain = _arg.domain, proof_text_check = _arg.proof_text_check;
199
212
  this.log("+ DNS check for " + domain);
200
- dnslib = this.libs.dns || dns;
201
213
  (function(_this) {
202
214
  return (function(__iced_k) {
203
215
  __iced_deferrals = new iced.Deferrals(__iced_k, {
204
216
  parent: ___iced_passed_deferral,
205
- filename: "/Users/max/src/keybase/proofs/src/scrapers/dns.iced",
217
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/dns.iced",
206
218
  funcname: "DnsScraper._check_status"
207
219
  });
208
- dnslib.resolveTxt(domain, __iced_deferrals.defer({
220
+ _this._resolve_txt(domain, __iced_deferrals.defer({
209
221
  assign_fn: (function() {
210
222
  return function() {
211
223
  err = arguments[0];
212
224
  return records = arguments[1];
213
225
  };
214
226
  })(),
215
- lineno: 105
227
+ lineno: 115
216
228
  }));
217
229
  __iced_deferrals._fulfill();
218
230
  });
@@ -1,20 +1,18 @@
1
- // Generated by IcedCoffeeScript 108.0.12
1
+ // Generated by IcedCoffeeScript 108.0.11
2
2
  (function() {
3
- var BaseScraper, BearerToken, Lock, TwitterScraper, bearer_token, constants, decode_sig, iced, make_ids, schema, sncmp, urlmod, v_codes, ws_normalize, __iced_k, __iced_k_noop, _bearer_token, _ref,
3
+ 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,
4
4
  __hasProp = {}.hasOwnProperty,
5
5
  __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; };
6
6
 
7
7
  iced = require('iced-runtime');
8
8
  __iced_k = __iced_k_noop = function() {};
9
9
 
10
- _ref = require('./base'), sncmp = _ref.sncmp, BaseScraper = _ref.BaseScraper;
10
+ _ref = require('./base'), sncmp = _ref.sncmp, BaseScraper = _ref.BaseScraper, BaseBearerToken = _ref.BaseBearerToken;
11
11
 
12
12
  make_ids = require('../base').make_ids;
13
13
 
14
14
  constants = require('../constants').constants;
15
15
 
16
- Lock = require('../util').Lock;
17
-
18
16
  v_codes = constants.v_codes;
19
17
 
20
18
  decode_sig = require('kbpgp').ukm.decode_sig;
@@ -35,108 +33,22 @@
35
33
  return v.join(' ');
36
34
  };
37
35
 
38
- BearerToken = (function() {
39
- function BearerToken(_arg) {
36
+ TwitterBearerToken = (function(_super) {
37
+ __extends(TwitterBearerToken, _super);
38
+
39
+ function TwitterBearerToken(_arg) {
40
40
  this.base = _arg.base;
41
- this._tok = null;
42
- this._created = 0;
43
- this._lock = new Lock();
44
- this.auth = this.base.auth;
41
+ TwitterBearerToken.__super__.constructor.call(this, {
42
+ name: "Twitter",
43
+ base: this.base,
44
+ access_token_url: "https://api.twitter.com/oauth2/token",
45
+ scope: ['history', 'read']
46
+ });
45
47
  }
46
48
 
47
- BearerToken.prototype.get = function(cb) {
48
- var body, cred, e, err, now, opts, rc, req, res, tok, ___iced_passed_deferral, __iced_deferrals, __iced_k;
49
- __iced_k = __iced_k_noop;
50
- ___iced_passed_deferral = iced.findDeferral(arguments);
51
- (function(_this) {
52
- return (function(__iced_k) {
53
- __iced_deferrals = new iced.Deferrals(__iced_k, {
54
- parent: ___iced_passed_deferral,
55
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
56
- funcname: "BearerToken.get"
57
- });
58
- _this._lock.acquire(__iced_deferrals.defer({
59
- lineno: 32
60
- }));
61
- __iced_deferrals._fulfill();
62
- });
63
- })(this)((function(_this) {
64
- return function() {
65
- err = null;
66
- now = Math.floor(Date.now() / 1000);
67
- (function(__iced_k) {
68
- if (((res = _this._tok) == null) || (now - _this._created > _this.auth.lifespan)) {
69
- _this.base.log("+ Request for bearer token");
70
- cred = (Buffer.from([_this.auth.key, _this.auth.secret].join(":"))).toString('base64');
71
- req = 'grant_type=client_credentials';
72
- opts = {
73
- url: "https://api.twitter.com/oauth2/token",
74
- headers: {
75
- Authorization: "Basic " + cred,
76
- 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
77
- 'Content-Length': req.length
78
- },
79
- method: "post",
80
- body: req
81
- };
82
- (function(__iced_k) {
83
- __iced_deferrals = new iced.Deferrals(__iced_k, {
84
- parent: ___iced_passed_deferral,
85
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
86
- funcname: "BearerToken.get"
87
- });
88
- _this.base._get_url_body(opts, __iced_deferrals.defer({
89
- assign_fn: (function() {
90
- return function() {
91
- err = arguments[0];
92
- rc = arguments[1];
93
- return body = arguments[2];
94
- };
95
- })(),
96
- lineno: 52
97
- }));
98
- __iced_deferrals._fulfill();
99
- })(function() {
100
- if (err != null) {
101
- _this.base.logl('error', "In getting bearer_token: " + err.message);
102
- } else if (rc !== v_codes.OK) {
103
- _this.base.logl('error', "HTTP error in getting bearer token: " + rc);
104
- err = new Error("HTTP error: " + rc);
105
- } else {
106
- try {
107
- body = JSON.parse(body);
108
- } catch (_error) {
109
- e = _error;
110
- _this.base.logl('warn', "Could not parse JSON reply: " + e);
111
- err = e;
112
- }
113
- }
114
- if (err != null) {
115
-
116
- } else if ((tok = body.access_token) == null) {
117
- _this.base.logl('warn', "No access token found in reply");
118
- err = new Error("Twitter error: no access token");
119
- } else {
120
- _this._tok = tok;
121
- _this._created = Math.floor(Date.now() / 1000);
122
- res = _this._tok;
123
- }
124
- return __iced_k(_this.base.log("- Request for bearer token -> " + err));
125
- });
126
- } else {
127
- return __iced_k();
128
- }
129
- })(function() {
130
- _this._lock.release();
131
- return cb(err, res);
132
- });
133
- };
134
- })(this));
135
- };
136
-
137
- return BearerToken;
49
+ return TwitterBearerToken;
138
50
 
139
- })();
51
+ })(BaseBearerToken);
140
52
 
141
53
  _bearer_token = null;
142
54
 
@@ -144,7 +56,7 @@
144
56
  var base;
145
57
  base = _arg.base;
146
58
  if (!_bearer_token) {
147
- _bearer_token = new BearerToken({
59
+ _bearer_token = new TwitterBearerToken({
148
60
  base: base
149
61
  });
150
62
  }
@@ -152,8 +64,12 @@
152
64
  };
153
65
 
154
66
  exports.TwitterScraper = TwitterScraper = (function(_super) {
67
+ var username_regexp;
68
+
155
69
  __extends(TwitterScraper, _super);
156
70
 
71
+ username_regexp = /^[a-z0-9_-]{2,15}$/;
72
+
157
73
  function TwitterScraper(opts) {
158
74
  this.auth = opts.auth;
159
75
  TwitterScraper.__super__.constructor.call(this, opts);
@@ -164,13 +80,15 @@
164
80
  return new Error("Bad args to Twitter proof: no username given");
165
81
  } else if (!(args.name != null) || (args.name !== 'twitter')) {
166
82
  return new Error("Bad args to Twitter proof: type is " + args.name);
83
+ } else if (!args.username.match(username_regexp)) {
84
+ return new Error("Invalid username passed to Twitter proof");
167
85
  } else {
168
86
  return null;
169
87
  }
170
88
  };
171
89
 
172
90
  TwitterScraper.prototype.hunt2 = function(_arg, cb) {
173
- var api_url, err, human_url, i, id_str, json, name, out, proof_text_check, rc, remote_id, text, u, username, ___iced_passed_deferral, __iced_deferrals, __iced_k;
91
+ 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;
174
92
  __iced_k = __iced_k_noop;
175
93
  ___iced_passed_deferral = iced.findDeferral(arguments);
176
94
  username = _arg.username, name = _arg.name, proof_text_check = _arg.proof_text_check;
@@ -182,24 +100,25 @@
182
100
  })) != null) {
183
101
  return cb(err, out);
184
102
  }
103
+ endpoint_name = "/2/tweets/search/recent";
185
104
  u = urlmod.format({
186
105
  host: "api.twitter.com",
187
106
  protocol: "https:",
188
- pathname: "/1.1/statuses/user_timeline.json",
107
+ pathname: endpoint_name,
189
108
  query: {
190
- count: 100,
191
- screen_name: username
109
+ query: "\"Verifying myself\" \"Keybase.io\" from:" + username
192
110
  }
193
111
  });
194
112
  (function(_this) {
195
113
  return (function(__iced_k) {
196
114
  __iced_deferrals = new iced.Deferrals(__iced_k, {
197
115
  parent: ___iced_passed_deferral,
198
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
116
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
199
117
  funcname: "TwitterScraper.hunt2"
200
118
  });
201
119
  _this._get_body_api({
202
- url: u
120
+ url: u,
121
+ endpoint_name: endpoint_name
203
122
  }, __iced_deferrals.defer({
204
123
  assign_fn: (function() {
205
124
  return function() {
@@ -208,28 +127,32 @@
208
127
  return json = arguments[2];
209
128
  };
210
129
  })(),
211
- lineno: 124
130
+ lineno: 73
212
131
  }));
213
132
  __iced_deferrals._fulfill();
214
133
  });
215
134
  })(this)((function(_this) {
216
135
  return function() {
217
- var _i, _len, _ref1;
136
+ var _i, _len, _ref1, _ref2;
218
137
  _this.log("| search index " + u + " -> " + rc);
219
138
  if (rc !== v_codes.OK) {
220
139
 
221
140
  } else if ((typeof json === "undefined" || json === null) || (json.length === 0)) {
222
141
  rc = v_codes.EMPTY_JSON;
142
+ } else if (json.data == null) {
143
+ rc = v_codes.INVALID_JSON;
223
144
  } else {
224
- for (i = _i = 0, _len = json.length; _i < _len; i = ++_i) {
225
- _ref1 = json[i], text = _ref1.text, id_str = _ref1.id_str;
145
+ rc = v_codes.NOT_FOUND;
146
+ _ref1 = json.data;
147
+ for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
148
+ _ref2 = _ref1[i], text = _ref2.text, id = _ref2.id;
226
149
  if ((_this.find_sig_in_tweet({
227
150
  inside: text,
228
151
  proof_text_check: proof_text_check
229
152
  })) === v_codes.OK) {
230
153
  _this.log("| found valid tweet in stream @ " + i);
231
154
  rc = v_codes.OK;
232
- remote_id = id_str;
155
+ remote_id = id;
233
156
  api_url = human_url = _this._id_to_url(username, remote_id);
234
157
  out = {
235
158
  remote_id: remote_id,
@@ -295,7 +218,7 @@
295
218
  (function(__iced_k) {
296
219
  __iced_deferrals = new iced.Deferrals(__iced_k, {
297
220
  parent: ___iced_passed_deferral,
298
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
221
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
299
222
  funcname: "TwitterScraper.users_lookup"
300
223
  });
301
224
  _this._get_body_api({
@@ -308,7 +231,7 @@
308
231
  return json = arguments[2];
309
232
  };
310
233
  })(),
311
- lineno: 172
234
+ lineno: 123
312
235
  }));
313
236
  __iced_deferrals._fulfill();
314
237
  })(function() {
@@ -338,11 +261,11 @@
338
261
  (function(__iced_k) {
339
262
  __iced_deferrals = new iced.Deferrals(__iced_k, {
340
263
  parent: ___iced_passed_deferral,
341
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
264
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
342
265
  funcname: "TwitterScraper.users_lookup"
343
266
  });
344
267
  setTimeout(__iced_deferrals.defer({
345
- lineno: 186
268
+ lineno: 137
346
269
  }), cursor_wait);
347
270
  __iced_deferrals._fulfill();
348
271
  })(__iced_k);
@@ -424,7 +347,7 @@
424
347
  (function(__iced_k) {
425
348
  __iced_deferrals = new iced.Deferrals(__iced_k, {
426
349
  parent: ___iced_passed_deferral,
427
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
350
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
428
351
  funcname: "TwitterScraper.get_follower_ids"
429
352
  });
430
353
  _this._get_body_api({
@@ -437,7 +360,7 @@
437
360
  return json = arguments[2];
438
361
  };
439
362
  })(),
440
- lineno: 224
363
+ lineno: 175
441
364
  }));
442
365
  __iced_deferrals._fulfill();
443
366
  })(function() {
@@ -470,11 +393,11 @@
470
393
  (function(__iced_k) {
471
394
  __iced_deferrals = new iced.Deferrals(__iced_k, {
472
395
  parent: ___iced_passed_deferral,
473
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
396
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
474
397
  funcname: "TwitterScraper.get_follower_ids"
475
398
  });
476
399
  setTimeout(__iced_deferrals.defer({
477
- lineno: 240
400
+ lineno: 191
478
401
  }), cursor_wait);
479
402
  __iced_deferrals._fulfill();
480
403
  })(__iced_k);
@@ -576,7 +499,7 @@
576
499
  return (function(__iced_k) {
577
500
  __iced_deferrals = new iced.Deferrals(__iced_k, {
578
501
  parent: ___iced_passed_deferral,
579
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
502
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
580
503
  funcname: "TwitterScraper.check_status"
581
504
  });
582
505
  _this._get_url_body({
@@ -590,7 +513,7 @@
590
513
  return body_obj = arguments[2];
591
514
  };
592
515
  })(),
593
- lineno: 331
516
+ lineno: 282
594
517
  }));
595
518
  __iced_deferrals._fulfill();
596
519
  });
@@ -663,7 +586,7 @@
663
586
  return (function(__iced_k) {
664
587
  __iced_deferrals = new iced.Deferrals(__iced_k, {
665
588
  parent: ___iced_passed_deferral,
666
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
589
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
667
590
  funcname: "TwitterScraper._get_bearer_token"
668
591
  });
669
592
  bt.get(__iced_deferrals.defer({
@@ -673,7 +596,7 @@
673
596
  return tok = arguments[1];
674
597
  };
675
598
  })(),
676
- lineno: 392
599
+ lineno: 343
677
600
  }));
678
601
  __iced_deferrals._fulfill();
679
602
  });
@@ -686,16 +609,16 @@
686
609
  };
687
610
 
688
611
  TwitterScraper.prototype._get_body_api = function(_arg, cb) {
689
- var args, body, err, rc, tok, url, ___iced_passed_deferral, __iced_deferrals, __iced_k;
612
+ var args, body, endpoint_name, err, rc, tok, url, ___iced_passed_deferral, __iced_deferrals, __iced_k;
690
613
  __iced_k = __iced_k_noop;
691
614
  ___iced_passed_deferral = iced.findDeferral(arguments);
692
- url = _arg.url;
615
+ url = _arg.url, endpoint_name = _arg.endpoint_name;
693
616
  rc = body = err = null;
694
617
  (function(_this) {
695
618
  return (function(__iced_k) {
696
619
  __iced_deferrals = new iced.Deferrals(__iced_k, {
697
620
  parent: ___iced_passed_deferral,
698
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
621
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
699
622
  funcname: "TwitterScraper._get_body_api"
700
623
  });
701
624
  _this._get_bearer_token(__iced_deferrals.defer({
@@ -706,7 +629,7 @@
706
629
  return tok = arguments[2];
707
630
  };
708
631
  })(),
709
- lineno: 401
632
+ lineno: 352
710
633
  }));
711
634
  __iced_deferrals._fulfill();
712
635
  });
@@ -721,12 +644,14 @@
721
644
  Authorization: "Bearer " + tok
722
645
  },
723
646
  method: "get",
724
- json: true
647
+ json: true,
648
+ log_ratelimit: endpoint_name != null,
649
+ endpoint_name: endpoint_name
725
650
  };
726
651
  (function(__iced_k) {
727
652
  __iced_deferrals = new iced.Deferrals(__iced_k, {
728
653
  parent: ___iced_passed_deferral,
729
- filename: "/home/zapu/Projects/KB/proofs/src/scrapers/twitter.iced",
654
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
730
655
  funcname: "TwitterScraper._get_body_api"
731
656
  });
732
657
  _this._get_url_body(args, __iced_deferrals.defer({
@@ -737,7 +662,7 @@
737
662
  return body = arguments[2];
738
663
  };
739
664
  })(),
740
- lineno: 410
665
+ lineno: 363
741
666
  }));
742
667
  __iced_deferrals._fulfill();
743
668
  })(__iced_k);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keybase-proofs",
3
- "version": "2.3.26",
3
+ "version": "2.3.28",
4
4
  "description": "Publicly-verifiable proofs of identity",
5
5
  "main": "lib/main.js",
6
6
  "scripts": {
@@ -163,6 +163,7 @@ exports.constants = constants =
163
163
  HTTP_400: 240
164
164
  HTTP_OTHER: 260
165
165
  EMPTY_JSON: 270
166
+ INVALID_JSON: 271
166
167
 
167
168
  # Hard final errors
168
169
  DELETED: 301
@@ -79,6 +79,14 @@ class BaseScraper
79
79
 
80
80
  #-------------------------------------------------------------
81
81
 
82
+ _get_rate_limit_header : (headers, name) ->
83
+ # Twitter and Github can't agree on rate limit header names.
84
+ if ((v = parse_int_or_undefined headers.get("X-RateLimit-#{name}")) or
85
+ (v = parse_int_or_undefined headers.get("X-Rate-Limit-#{name}")))
86
+ return v
87
+ else
88
+ return undefined
89
+
82
90
  _get_url_body: (opts, cb) ->
83
91
  ###
84
92
  cb(err, status, body) only replies with body if status is 200
@@ -88,13 +96,17 @@ class BaseScraper
88
96
  opts.headers["User-Agent"] ?= (opts.user_agent or user_agent)
89
97
  await @libs.fetch opts.url, opts, defer(err, response)
90
98
  if opts.log_ratelimit and response?
91
- rl_limit = parse_int_or_undefined response.headers.get 'X-RateLimit-Limit' # 5000 for github # https://developer.github.com/v3/#rate-limiting
92
- rl_remaining = parse_int_or_undefined response.headers.get 'X-RateLimit-Remaining'
93
- rl_reset = parse_int_or_undefined response.headers.get 'X-RateLimit-Reset' # utc timestamp in seconds when limit will replenish
99
+ rl_limit = @_get_rate_limit_header response.headers, 'Limit' # 5000 for github # https://developer.github.com/v3/#rate-limiting
100
+ rl_remaining = @_get_rate_limit_header response.headers, 'Remaining'
101
+ rl_reset = @_get_rate_limit_header response.headers, 'Reset' # utc timestamp in seconds when limit will replenish
94
102
  if rl_limit?
95
103
  @log "| ratelimit info limit=#{rl_limit} remaining=#{rl_remaining} reset=#{rl_reset}"
96
- @libs.ratelimit_inform? {limit: rl_limit, remaining: rl_remaining, reset: rl_reset}
97
-
104
+ @libs.ratelimit_inform? {
105
+ limit: rl_limit
106
+ remaining: rl_remaining
107
+ reset: rl_reset
108
+ endpoint_name : opts.endpoint_name
109
+ }
98
110
  rc = if err?
99
111
  if err.message.includes('network timeout') then v_codes.TIMEOUT
100
112
  else v_codes.HOST_UNREACHABLE
@@ -97,13 +97,23 @@ exports.DnsScraper = class DnsScraper extends BaseScraper
97
97
 
98
98
  # ---------------------------------------------------------------------------
99
99
 
100
+ _resolve_txt : (domain, cb) ->
101
+ # We can use a DNS library passed in (in the case of native-dns running on
102
+ # the server)
103
+ dnslib = @libs.dns or dns
104
+ try
105
+ dnslib.resolveTxt domain, (err, records) ->
106
+ cb err, records
107
+ catch err
108
+ cb err
109
+
110
+ # ---------------------------------------------------------------------------
111
+
100
112
  # calls back with a v_code or null if it was ok
101
113
  _check_status: ({domain, proof_text_check}, cb) ->
102
114
  @log "+ DNS check for #{domain}"
103
115
 
104
- # We can use a DNS library passed in (in the case of native-dns running on the server)
105
- dnslib = @libs.dns or dns
106
- await dnslib.resolveTxt domain, defer err, records
116
+ await @_resolve_txt domain, defer err, records
107
117
  rc = if err?
108
118
  @log "| DNS error: #{err}"
109
119
  v_codes.DNS_ERROR
@@ -1,7 +1,6 @@
1
- {sncmp,BaseScraper} = require './base'
1
+ {sncmp,BaseScraper,BaseBearerToken} = require './base'
2
2
  {make_ids} = require '../base'
3
3
  {constants} = require '../constants'
4
- {Lock} = require '../util'
5
4
  {v_codes} = constants
6
5
  {decode_sig} = require('kbpgp').ukm
7
6
  urlmod = require 'url'
@@ -17,78 +16,27 @@ ws_normalize = (x) ->
17
16
 
18
17
  #================================================================================
19
18
 
20
- class BearerToken
21
-
22
- #----------------
23
-
19
+ class TwitterBearerToken extends BaseBearerToken
24
20
  constructor : ({@base}) ->
25
- @_tok = null
26
- @_created = 0
27
- @_lock = new Lock()
28
- @auth = @base.auth
29
-
30
- #----------------
31
-
32
- get : (cb) ->
33
- await @_lock.acquire defer()
34
- err = null
35
- now = Math.floor(Date.now() / 1000)
36
-
37
- if not (res = @_tok)? or (now - @_created > @auth.lifespan)
38
-
39
- @base.log "+ Request for bearer token"
40
-
41
- # Very crypto! Not sure why this is done, but it's done
42
- cred = (Buffer.from [ @auth.key, @auth.secret].join(":")).toString('base64')
43
-
44
- req = 'grant_type=client_credentials'
45
- opts =
46
- url : "https://api.twitter.com/oauth2/token"
47
- headers :
48
- Authorization : "Basic #{cred}"
49
- 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
50
- 'Content-Length': req.length
51
- method : "post"
52
- body : req
53
- await @base._get_url_body opts, defer err, rc, body
54
-
55
- if err?
56
- @base.logl 'error', "In getting bearer_token: #{err.message}"
57
- else if (rc isnt v_codes.OK)
58
- @base.logl 'error', "HTTP error in getting bearer token: #{rc}"
59
- err = new Error "HTTP error: #{rc}"
60
- else
61
- try
62
- body = JSON.parse body
63
- catch e
64
- @base.logl 'warn', "Could not parse JSON reply: #{e}"
65
- err = e
66
-
67
- if err? then # noop
68
- else if not (tok = body.access_token)?
69
- @base.logl 'warn', "No access token found in reply"
70
- err = new Error "Twitter error: no access token"
71
- else
72
- @_tok = tok
73
- @_created = Math.floor(Date.now() / 1000)
74
- res = @_tok
75
-
76
- @base.log "- Request for bearer token -> #{err}"
77
-
78
- @_lock.release()
79
- cb err, res
21
+ super {
22
+ name: "Twitter"
23
+ @base
24
+ access_token_url : "https://api.twitter.com/oauth2/token"
25
+ scope : [ 'history', 'read' ]
26
+ }
80
27
 
81
28
  #================================================================================
82
29
 
83
30
  _bearer_token = null
84
31
  bearer_token = ({base}) ->
85
32
  unless _bearer_token
86
- _bearer_token = new BearerToken { base }
33
+ _bearer_token = new TwitterBearerToken { base }
87
34
  return _bearer_token
88
35
 
89
36
  #================================================================================
90
37
 
91
38
  exports.TwitterScraper = class TwitterScraper extends BaseScraper
39
+ username_regexp = /^[a-z0-9_-]{2,15}$/
92
40
 
93
41
  constructor: (opts) ->
94
42
  @auth = opts.auth
@@ -101,6 +49,8 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
101
49
  new Error "Bad args to Twitter proof: no username given"
102
50
  else if not (args.name?) or (args.name isnt 'twitter')
103
51
  new Error "Bad args to Twitter proof: type is #{args.name}"
52
+ else if not args.username.match(username_regexp)
53
+ new Error "Invalid username passed to Twitter proof"
104
54
  else
105
55
  null
106
56
 
@@ -113,25 +63,26 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
113
63
 
114
64
  return cb(err,out) if (err = @_check_args { username, name })?
115
65
 
66
+ endpoint_name = "/2/tweets/search/recent"
116
67
  u = urlmod.format {
117
68
  host : "api.twitter.com"
118
69
  protocol : "https:"
119
- pathname : "/1.1/statuses/user_timeline.json"
70
+ pathname : endpoint_name
120
71
  query :
121
- count : 100
122
- screen_name : username
72
+ query : "\"Verifying myself\" \"Keybase.io\" from:#{username}"
123
73
  }
124
-
125
- await @_get_body_api { url : u }, defer err, rc, json
74
+ await @_get_body_api { url : u, endpoint_name }, defer err, rc, json
126
75
  @log "| search index #{u} -> #{rc}"
127
76
  if rc isnt v_codes.OK then #noop
128
77
  else if not json? or (json.length is 0) then rc = v_codes.EMPTY_JSON
78
+ else if not json.data? then rc = v_codes.INVALID_JSON
129
79
  else
130
- for {text, id_str},i in json
80
+ rc = v_codes.NOT_FOUND
81
+ for {text, id},i in json.data
131
82
  if (@find_sig_in_tweet { inside : text, proof_text_check }) is v_codes.OK
132
83
  @log "| found valid tweet in stream @ #{i}"
133
84
  rc = v_codes.OK
134
- remote_id = id_str
85
+ remote_id = id
135
86
  api_url = human_url = @_id_to_url username, remote_id
136
87
  out = { remote_id, api_url, human_url }
137
88
  break
@@ -397,7 +348,7 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
397
348
  # ---------------------------------------------------------------------------
398
349
 
399
350
  # Only the hunter needs this
400
- _get_body_api : ({url}, cb) ->
351
+ _get_body_api : ({url, endpoint_name}, cb) ->
401
352
  rc = body = err = null
402
353
  await @_get_bearer_token defer err, rc, tok
403
354
  unless err?
@@ -408,6 +359,8 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
408
359
  Authorization : "Bearer #{tok}"
409
360
  method : "get"
410
361
  json : true
362
+ log_ratelimit : endpoint_name?
363
+ endpoint_name : endpoint_name
411
364
  await @_get_url_body args, defer err, rc, body
412
365
  cb err, rc, body
413
366
 
package/SIGNED.md DELETED
@@ -1,131 +0,0 @@
1
- ##### Signed by https://keybase.io/max
2
- ```
3
- -----BEGIN PGP SIGNATURE-----
4
- Comment: GPGTools - https://gpgtools.org
5
-
6
- iQEcBAABCgAGBQJWBZ3PAAoJEJgKPw0B/gTfPrkIALMDLUb+CS2oPrFZlILoC31o
7
- FUKtCiN+hzC9+FgMYp18kWsWDh3DxzqPIRs28mwt9QMQTHdywv6K+nfQw2dE4wns
8
- 1Wi7ZVPbvR5P0/ANIQ0QwetYxbkNow20dU67CRj0aEvvbopQDBgBgyH9b6LjSb+n
9
- 8OTUFLhtiXN0PeZJ5/OyMtq8jyrjT7MEpxAb+8uFt95fPax17TNjCBEF4mlNqvtl
10
- YIOd/1v6YTlaqbYSzEZoMZsSI12UGK/tLaPG6qYf+5gGi96YjEMuQyDsL7ANQpkG
11
- jplkuKpykiY1Ndc1B3TOnTK8n+lrRaWesLmHnhldhR047ZNYgETunz+NYks7MHI=
12
- =96ur
13
- -----END PGP SIGNATURE-----
14
-
15
- ```
16
-
17
- <!-- END SIGNATURES -->
18
-
19
- ### Begin signed statement
20
-
21
- #### Expect
22
-
23
- ```
24
- size exec file contents
25
- ./
26
- 109 .gitignore ec278daeb8f83cac2579d262b92ee6d7d872c4d1544e881ba515d8bcc05361ab
27
- 10709 CHANGELOG.md 8ce81ae853a72be10648f712497199b316dfe57c8519853687cee1b508965af1
28
- 66 CONTIBUTORS.md e5a536ada9dc7f25b352f28cc8936db4ded1a0860f6f23ef61d0b14aeae351da
29
- 1483 LICENSE 333be7050513d91d9e77ca9acb4a91261721f0050209636076ed58676bfc643d
30
- 1383 Makefile b90c2b9165497660a610d5ec6177b854aafaa318aa1252af23484c755f1d959a
31
- 43 README.md b2db580fc880756ae739bb148570f6afb1c250e5c0d2c5fa56ce41491753ca6c
32
- lib/
33
- 2498 alloc.js af1347e6f17197fee04fba783839279b07b07f79bf407ddf44e09d360289aa48
34
- 1189 announcement.js ff01f39dca21c1387faa8b79633dec40414f5645886015a46bd795772987c79e
35
- 1383 auth.js 86403419686d1cac766c8d3d6c8f6e10176694e33de84faafca652fd01e54e0e
36
- 766 b64extract.js 1466f81bb930ddc439f877ef0cba4d20048f5477aafda294290f0fd0da527073
37
- 33694 base.js b57791baad7fd3b648749320875ada1adc6e8d0dc3265c6f2fe4faeb960c05bb
38
- 2043 constants.js e88df2ce555fe3fa8f70bbac3ebdca7c169571cbd5055948ecd8215e231d75fb
39
- 1382 cryptocurrency.js 40536713081183e8b698a345df9a288a0967e1c95bd0699fe8ea18b618a6de6a
40
- 1087 device.js f77d694ccf19c81b8c1977bb5c0280979a8b15983ced95675d6f5d44cedf67a7
41
- 1294 eldest.js 878cc36219a2d25c1e1897a672f03b5d97ece05581b6ece7707e78cbb0a8e02e
42
- 909 main.js 1cc4c7fd9cfee3ce317d8993507a953c8a22803eaea69eb16d4c7be9c0d7731b
43
- 1549 pgp_update.js c084bee65b6e7cee2d9fd7ddeea107751a319c03831205005ed997fa4840f939
44
- 2578 revoke.js 57106f23d108ce01a6d781a795417c86a09633af73ff1c9630d3893a8bc47e5f
45
- scrapers/
46
- 6392 base.js db98e2349217287c98feba718b0bdd9570c5e1108370942af9ca2a2b59be5ed9
47
- 15450 bitbucket.js 7530f245a6836d29177cae7e2a1e0efa9860e1adc9965731cf1e79c5f82f3f4c
48
- 4137 coinbase.js 5d28f2c525f69e64881d0ae5dd38c535a324f894d98b67d1a5b8217a4c564ce3
49
- 8239 dns.js 74ced258122595b80997594c4f8fb82c210b5f316848b035b2b66582bb6052e8
50
- 9369 generic_web_site.js aa3fff8c4d36f86182666642117ec71c4c1574d44eeeba675f698d08fb9a1edd
51
- 9901 github.js 221586b7fd13ec28a51479319902fdb143a1b37d25dcdb7926826b100917ffbd
52
- 5995 hackernews.js 0e73885c9dba4dbba56fc042afe6002aeae1699094f77555c831d2a15348033c
53
- 8535 reddit.js 508d1b349ce6a992afe5c882d7c541c1ff85990645343dbfd748c09888d8ce3f
54
- 27418 twitter.js fda7930b6de79013601c22d0585833e6c2bf09594ec7890a4852ef3e21d24df5
55
- 1774 sibkey.js b434bb5e226214d5113eae44201ead14e7430bc92e56e2446e80d2cf9076ab1d
56
- 10983 subkey.js 76aca817ffb548ffe3dd668d9c2b189c3c3040300f748c6acf105c5f5a5dae82
57
- 1669 track.js b85423eb1309ac58f5cb2b68697f612c091ef2d44fa8cc9e4b85ab6039ae8b9a
58
- 1506 update_passphrase_hash.js f2cfce382fd677e31a88f3646eb2d90258583a1ef68fe74dcd178a2e178229f6
59
- 1544 util.js 1760f3955e71bed8a332c02a1c9a96733f9af0e0f297c89ec354f83ed5bf3908
60
- 19530 web_service.js 769a2c81aeb9b76ec627126776c346c20209ed588536dca528a617b5fbcdc553
61
- 744 package.json b670f8f7efdd7d69fa92f089729bdfe4486ded548e03b677bab888e3e21f2ab0
62
- src/
63
- 2376 alloc.iced bce30dc075d16e0551aeb8ce89f2a0515e8b061f7cbd1c9842a93756c028254b
64
- 537 announcement.iced 98ddc17f5c6b66874ed2a273032d5c5e118d1ef3a7f5a08f4c7fb9cf06e811cf
65
- 671 auth.iced 3f0f211734a2f8408a5f5e1f5c353cad3373f952b01209f0f2d644b681612b0e
66
- 704 b64extract.iced 47b06460fd61d8eb788878566c62086b1fd1f792a1712b3e8736766401bc8fee
67
- 15746 base.iced cbd3229cab90232ce33ca7ced8c2d492c2eaa5091a48019486f401f3df689f4a
68
- 2264 constants.iced ed833b20ade7e8e95d1e4c65577cfb8e7de20b554afaf7aaa7963e86d5f2b370
69
- 609 cryptocurrency.iced 9c839bdd1c12591ca73c6fbc86d44c51f1803718d37286e9eade945bee0761d1
70
- 489 device.iced b0c028eb2598c48d9d24be615e066e0a03e8b7b1930a4ca58f29d23517daf0bc
71
- 566 eldest.iced 8dd63bec35fca4ab933de049888777e957827a862ecf2dec7e91e91e4265917c
72
- 741 main.iced 0c4af12c6c069e3ece0f446c922ba8f306df4f386e3bee3c4ba852bc92a4df0b
73
- 589 pgp_update.iced cdc5fed1d47c0bfe144062cf7985d466ced99fb8ec7bd17901d504127359fc64
74
- 788 revoke.iced 2b94671a923dcdd274d0f769f186f0757a27db64828eaad5fa5eb1a24f9e479f
75
- scrapers/
76
- 4029 base.iced e110786284dda6cd78bc0be507ff9ac0add3a128c959c073562416b513d8b2d8
77
- 3967 bitbucket.iced f22e9c29d781afbecccc0c720513fa63710e3c199cc9780ef34b87a4db0e1fd9
78
- 2370 coinbase.iced 5b1132561f0c33d4c35ba2d7d9c32200e77d83c68a3bce3d770b27085d649bcd
79
- 4081 dns.iced 773ea8e65f46b2db994e41ed4849c4637200ee2e978f280524d63b06a42ff454
80
- 3249 generic_web_site.iced 7a70be6c922e5adb73459973b81143ec9020b109cdbcc5acf349e47c5ca1f68d
81
- 3305 github.iced f10410ccca3bb3fc5045024dada97a4974984ee433476bea30c1ab47f870da90
82
- 2909 hackernews.iced cb14793b3ea889bcbc2121430bc3728655e125806001211864d3c90c1104505a
83
- 4082 reddit.iced 8a4f6902eee7e1609df18bf9a00b5934d2e9a16eaf20bd6231b3d11b2554effe
84
- 11418 twitter.iced 9bbb6cfdc464a5ed3261cc8f1a14a12e18325d6b0edc753c7a9e133b58d9328e
85
- 761 sibkey.iced 308c4cfef33dbc79809600a46c4f1704d4240be18478d2e5e8f71e523fd6962d
86
- 3503 subkey.iced 6f41a5aa38813486cbcb5a3c918c2e7aa464665b9dac852f92bb62ffa1e0a3b1
87
- 838 track.iced d72d0eb5cda7f0f9800f17225fedf552f45111b802f66c463c4408794bd11aca
88
- 658 update_passphrase_hash.iced ea87dd15d3a36ee581e48a5436f4c64f38a102906efcdc20bc925b821210bfd9
89
- 1052 util.iced 88092afaf23bf8dfe42a72252ac156be45e94588911584ecf535d228b108f2b9
90
- 10177 web_service.iced bb436e165233be85342d0abe01237fcfb5773b0cc050abef49f1f19094abd8f0
91
- 1673 test_scraper.iced 099823ea87768b45dd205bd6dc36773999730dadb26fe3844e0a05f8668bb9d1
92
- ```
93
-
94
- #### Ignore
95
-
96
- ```
97
- /SIGNED.md
98
- ```
99
-
100
- #### Presets
101
-
102
- ```
103
- git # ignore .git and anything as described by .gitignore files
104
- dropbox # ignore .dropbox-cache and other Dropbox-related files
105
- kb # ignore anything as described by .kbignore files
106
- ```
107
-
108
- <!-- summarize version = 0.0.9 -->
109
-
110
- ### End signed statement
111
-
112
- <hr>
113
-
114
- #### Notes
115
-
116
- With keybase you can sign any directory's contents, whether it's a git repo,
117
- source code distribution, or a personal documents folder. It aims to replace the drudgery of:
118
-
119
- 1. comparing a zipped file to a detached statement
120
- 2. downloading a public key
121
- 3. confirming it is in fact the author's by reviewing public statements they've made, using it
122
-
123
- All in one simple command:
124
-
125
- ```bash
126
- keybase dir verify
127
- ```
128
-
129
- There are lots of options, including assertions for automating your checks.
130
-
131
- For more info, check out https://keybase.io/docs/command_line/code_signing