keybase-proofs 2.3.31 → 2.3.32

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,8 @@
1
+ ## 2.3.32 (2026-05-28)
2
+
3
+ - Fix Twitter scraper, allow returned URL to be either in x.com or twitter.com
4
+ domain.
5
+
1
6
  ## 2.3.31 (2025-12-18)
2
7
 
3
8
  - Set response size limit when making a request in GenericWebSiteScraper.
@@ -113,7 +113,7 @@
113
113
  return (function(__iced_k) {
114
114
  __iced_deferrals = new iced.Deferrals(__iced_k, {
115
115
  parent: ___iced_passed_deferral,
116
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
116
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
117
117
  funcname: "TwitterScraper.hunt2"
118
118
  });
119
119
  _this._get_body_api({
@@ -222,7 +222,7 @@
222
222
  (function(__iced_k) {
223
223
  __iced_deferrals = new iced.Deferrals(__iced_k, {
224
224
  parent: ___iced_passed_deferral,
225
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
225
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
226
226
  funcname: "TwitterScraper.users_lookup"
227
227
  });
228
228
  _this._get_body_api({
@@ -265,7 +265,7 @@
265
265
  (function(__iced_k) {
266
266
  __iced_deferrals = new iced.Deferrals(__iced_k, {
267
267
  parent: ___iced_passed_deferral,
268
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
268
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
269
269
  funcname: "TwitterScraper.users_lookup"
270
270
  });
271
271
  setTimeout(__iced_deferrals.defer({
@@ -351,7 +351,7 @@
351
351
  (function(__iced_k) {
352
352
  __iced_deferrals = new iced.Deferrals(__iced_k, {
353
353
  parent: ___iced_passed_deferral,
354
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
354
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
355
355
  funcname: "TwitterScraper.get_follower_ids"
356
356
  });
357
357
  _this._get_body_api({
@@ -397,7 +397,7 @@
397
397
  (function(__iced_k) {
398
398
  __iced_deferrals = new iced.Deferrals(__iced_k, {
399
399
  parent: ___iced_passed_deferral,
400
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
400
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
401
401
  funcname: "TwitterScraper.get_follower_ids"
402
402
  });
403
403
  setTimeout(__iced_deferrals.defer({
@@ -483,6 +483,21 @@
483
483
  return rc;
484
484
  };
485
485
 
486
+ TwitterScraper.prototype._comp_twitter_url = function(url_a_str, url_b_str) {
487
+ var twitter_hosts, url_a, url_b;
488
+ if (!url_a_str || !url_b_str) {
489
+ return false;
490
+ }
491
+ twitter_hosts = ['x.com', 'twitter.com'];
492
+ try {
493
+ url_a = new urlmod.URL(url_a_str.toLowerCase());
494
+ url_b = new urlmod.URL(url_b_str.toLowerCase());
495
+ return twitter_hosts.includes(url_a.host) && twitter_hosts.includes(url_b.host) && url_a.protocol === url_b.protocol && url_a.pathname === url_b.pathname && url_a.search === url_b.search && url_a.hash === url_b.hash;
496
+ } catch (_error) {
497
+ return false;
498
+ }
499
+ };
500
+
486
501
  TwitterScraper.prototype.check_status = function(_arg, cb) {
487
502
  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;
488
503
  __iced_k = __iced_k_noop;
@@ -503,7 +518,7 @@
503
518
  return (function(__iced_k) {
504
519
  __iced_deferrals = new iced.Deferrals(__iced_k, {
505
520
  parent: ___iced_passed_deferral,
506
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
521
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
507
522
  funcname: "TwitterScraper.check_status"
508
523
  });
509
524
  _this._get_url_body({
@@ -517,7 +532,7 @@
517
532
  return body_obj = arguments[2];
518
533
  };
519
534
  })(),
520
- lineno: 288
535
+ lineno: 309
521
536
  }));
522
537
  __iced_deferrals._fulfill();
523
538
  });
@@ -532,16 +547,16 @@
532
547
  if (err = schm.check(body_obj)) {
533
548
  return cb(err, v_codes.CONTENT_FAILURE);
534
549
  }
535
- if (!(sncmp(body_obj.url, api_url))) {
550
+ if (!(_this._comp_twitter_url(body_obj.url, api_url))) {
536
551
  err = new Error("returned url field doesn't match api_url (found: " + body_obj.url + ", expected: " + api_url + ")");
537
552
  return cb(err, v_codes.CONTENT_FAILURE);
538
553
  }
539
- api_url_matches = api_url.match(new RegExp("^https://twitter\\.com/([^/]+)/status/(\\d+)(.*)$"));
554
+ api_url_matches = api_url.match(new RegExp("^https://(x|twitter)\\.com/([^/]+)/status/(\\d+)(.*)$"));
540
555
  if (!api_url_matches) {
541
556
  err = new Error("api_url field doesn't match regexp, got: " + api_url_matches);
542
557
  return cb(err, v_codes.CONTENT_FAILURE);
543
558
  }
544
- _ = api_url_matches[0], username_from_url = api_url_matches[1], tweet_id = api_url_matches[2];
559
+ _ = api_url_matches[0], _ = api_url_matches[1], username_from_url = api_url_matches[2], tweet_id = api_url_matches[3];
545
560
  if (!(sncmp(username, username_from_url))) {
546
561
  err = new Error("username from api_url didn't match, expected: " + username + ", got: " + username_from_url);
547
562
  return cb(err, v_codes.BAD_USERNAME);
@@ -549,12 +564,12 @@
549
564
  if (tweet_id !== remote_id.toString()) {
550
565
  return cb(err, v_codes.BAD_REMOTE_ID);
551
566
  }
552
- author_url_matches = body_obj.author_url.match(new RegExp("^https://twitter\\.com/(.+)$"));
567
+ author_url_matches = body_obj.author_url.match(new RegExp("^https://(x|twitter)\\.com/(.+)$"));
553
568
  if (!author_url_matches) {
554
569
  err = new Error("author_url doesn't match regexp, got: " + body_obj.author_url);
555
570
  return cb(err, v_codes.CONTENT_FAILURE);
556
571
  }
557
- _ = author_url_matches[0], author_username = author_url_matches[1];
572
+ _ = author_url_matches[0], _ = author_url_matches[1], author_username = author_url_matches[2];
558
573
  if (!(sncmp(username, author_username))) {
559
574
  err = new Error("username from author_url didn't match, expected: " + username + ", got: " + author_username);
560
575
  return cb(err, v_codes.BAD_USERNAME);
@@ -590,7 +605,7 @@
590
605
  return (function(__iced_k) {
591
606
  __iced_deferrals = new iced.Deferrals(__iced_k, {
592
607
  parent: ___iced_passed_deferral,
593
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
608
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
594
609
  funcname: "TwitterScraper._get_bearer_token"
595
610
  });
596
611
  bt.get(__iced_deferrals.defer({
@@ -600,7 +615,7 @@
600
615
  return tok = arguments[1];
601
616
  };
602
617
  })(),
603
- lineno: 349
618
+ lineno: 372
604
619
  }));
605
620
  __iced_deferrals._fulfill();
606
621
  });
@@ -622,7 +637,7 @@
622
637
  return (function(__iced_k) {
623
638
  __iced_deferrals = new iced.Deferrals(__iced_k, {
624
639
  parent: ___iced_passed_deferral,
625
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
640
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
626
641
  funcname: "TwitterScraper._get_body_api"
627
642
  });
628
643
  _this._get_bearer_token(__iced_deferrals.defer({
@@ -633,7 +648,7 @@
633
648
  return tok = arguments[2];
634
649
  };
635
650
  })(),
636
- lineno: 358
651
+ lineno: 381
637
652
  }));
638
653
  __iced_deferrals._fulfill();
639
654
  });
@@ -655,7 +670,7 @@
655
670
  (function(__iced_k) {
656
671
  __iced_deferrals = new iced.Deferrals(__iced_k, {
657
672
  parent: ___iced_passed_deferral,
658
- filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/server_test_progs/proofs/src/scrapers/twitter.iced",
673
+ filename: "/Users/michal/SourceCode/keybase/go/src/github.com/keybase/proofs/src/scrapers/twitter.iced",
659
674
  funcname: "TwitterScraper._get_body_api"
660
675
  });
661
676
  _this._get_url_body(args, __iced_deferrals.defer({
@@ -666,7 +681,7 @@
666
681
  return body = arguments[2];
667
682
  };
668
683
  })(),
669
- lineno: 369
684
+ lineno: 392
670
685
  }));
671
686
  __iced_deferrals._fulfill();
672
687
  })(__iced_k);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keybase-proofs",
3
- "version": "2.3.31",
3
+ "version": "2.3.32",
4
4
  "description": "Publicly-verifiable proofs of identity",
5
5
  "main": "lib/main.js",
6
6
  "scripts": {
@@ -262,6 +262,27 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
262
262
 
263
263
  # ---------------------------------------------------------------------------
264
264
 
265
+ _comp_twitter_url : (url_a_str, url_b_str) ->
266
+ # Do a smarter check for twitter URL but allowing hosts to be either
267
+ # x.com or twitter.com. Rest of the URL has to match (protocol, path,
268
+ # hash etc.)
269
+ if not url_a_str or not url_b_str
270
+ return false
271
+ twitter_hosts = ['x.com', 'twitter.com']
272
+ try
273
+ url_a = new urlmod.URL(url_a_str.toLowerCase())
274
+ url_b = new urlmod.URL(url_b_str.toLowerCase())
275
+ return (
276
+ twitter_hosts.includes(url_a.host) and
277
+ twitter_hosts.includes(url_b.host) and
278
+ url_a.protocol is url_b.protocol and
279
+ url_a.pathname is url_b.pathname and
280
+ url_a.search is url_b.search and
281
+ url_a.hash is url_b.hash
282
+ )
283
+ catch
284
+ return false
285
+
265
286
  check_status: ({username, api_url, proof_text_check, remote_id}, cb) ->
266
287
 
267
288
  if not api_url?.length
@@ -297,20 +318,22 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
297
318
  if err = schm.check body_obj
298
319
  return cb err, v_codes.CONTENT_FAILURE
299
320
 
300
- # "url" field returned by the API should match api_url.
301
- if not(sncmp(body_obj.url, api_url))
321
+ # "url" field returned by the API should match api_url, but twitter may
322
+ # return x.com domain in the "url" field. Our `api_url` is always
323
+ # twitter.com right now (for new and existing proofs).
324
+ if not(@_comp_twitter_url(body_obj.url, api_url))
302
325
  err = new Error "returned url field doesn't match api_url (found: #{body_obj.url}, expected: #{api_url})"
303
326
  return cb err, v_codes.CONTENT_FAILURE
304
327
 
305
328
  # Extract username from URL, do not use "author_name" because it's the
306
329
  # full name.
307
- api_url_matches = api_url.match(new RegExp("^https://twitter\\.com/([^/]+)/status/(\\d+)(.*)$"))
330
+ api_url_matches = api_url.match(new RegExp("^https://(x|twitter)\\.com/([^/]+)/status/(\\d+)(.*)$"))
308
331
  if not api_url_matches
309
332
  err = new Error "api_url field doesn't match regexp, got: #{api_url_matches}"
310
333
  return cb err, v_codes.CONTENT_FAILURE
311
334
 
312
335
  # Check username and tweet ID.
313
- [_, username_from_url, tweet_id] = api_url_matches
336
+ [_, _, username_from_url, tweet_id] = api_url_matches
314
337
  if not(sncmp(username, username_from_url))
315
338
  err = new Error("username from api_url didn't match, expected: #{username}, got: #{username_from_url}")
316
339
  return cb err, v_codes.BAD_USERNAME
@@ -319,12 +342,12 @@ exports.TwitterScraper = class TwitterScraper extends BaseScraper
319
342
 
320
343
  # Check "author_url" field, it contains a link to user's twitter profile.
321
344
  # It should match our username.
322
- author_url_matches = body_obj.author_url.match(new RegExp("^https://twitter\\.com/(.+)$"))
345
+ author_url_matches = body_obj.author_url.match(new RegExp("^https://(x|twitter)\\.com/(.+)$"))
323
346
  if not author_url_matches
324
347
  err = new Error("author_url doesn't match regexp, got: #{body_obj.author_url}")
325
348
  return cb err, v_codes.CONTENT_FAILURE
326
349
 
327
- [_, author_username] = author_url_matches
350
+ [_, _, author_username] = author_url_matches
328
351
  if not(sncmp(username, author_username))
329
352
  err = new Error("username from author_url didn't match, expected: #{username}, got: #{author_username}")
330
353
  return cb err, v_codes.BAD_USERNAME