whalibmob 5.5.68 → 5.5.70

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/cli.js CHANGED
@@ -174,6 +174,47 @@ function enableWireTrace() {
174
174
  }
175
175
  }
176
176
 
177
+ // The Noise handshake never goes through sendNode — sendNode refuses to run
178
+ // until the channel is secured, and the ClientHello/ClientFinish frames are
179
+ // written straight to the TCP socket. Wrap that socket as soon as connect()
180
+ // creates it so the handshake is visible too, then step aside once the
181
+ // channel is up and the stanza-level hooks take over.
182
+ const HS_OUT = ['ClientHello', 'ClientFinish'];
183
+ const HS_IN = ['ServerHello'];
184
+ const origConnect = NoiseSocket.prototype.connect;
185
+ NoiseSocket.prototype.connect = function (...args) {
186
+ const result = origConnect.apply(this, args);
187
+ const sock = this.socket;
188
+ if (sock && !sock.__waTraced) {
189
+ sock.__waTraced = true;
190
+ let outN = 0, inN = 0;
191
+ trace(`\n${C.out}${stamp()} ──▶ TCP CONNECT ${sock.remoteAddress || ''}${C.off}`);
192
+
193
+ const origWrite = sock.write.bind(sock);
194
+ sock.write = (chunk, ...rest) => {
195
+ if (!this.secured && Buffer.isBuffer(chunk)) {
196
+ const name = HS_OUT[outN++] || 'handshake frame';
197
+ trace(`\n${C.out}${stamp()} ──▶ NOISE ${name} (${chunk.length} bytes)${C.off}`);
198
+ trace(` ${C.dim}${chunk.toString('hex')}${C.off}`);
199
+ }
200
+ return origWrite(chunk, ...rest);
201
+ };
202
+
203
+ sock.on('data', (d) => {
204
+ if (this.secured) return;
205
+ // Label positionally, but say so when the reply is plainly not a Noise
206
+ // frame — a proxy or captive portal answering in ASCII is worth reading
207
+ // as text rather than staring at its hex.
208
+ const ascii = isPrintable(d);
209
+ const name = ascii ? 'unexpected plaintext reply' : (HS_IN[inN++] || 'handshake frame');
210
+ trace(`\n${C.in}${stamp()} ◀── NOISE ${name} (${d.length} bytes)${C.off}`);
211
+ trace(` ${C.dim}${d.toString('hex').slice(0, 1024)}${C.off}`);
212
+ if (ascii) trace(` ${C.dim}as text: ${JSON.stringify(d.toString('utf8').slice(0, 400))}${C.off}`);
213
+ });
214
+ }
215
+ return result;
216
+ };
217
+
177
218
  const origSendNode = NoiseSocket.prototype.sendNode;
178
219
  NoiseSocket.prototype.sendNode = function (node) {
179
220
  try { logStanza('OUT', node); } catch (_) {}
@@ -158,19 +158,27 @@ async function iosAppAttest(noisePubB64) {
158
158
  // ENC envelope, so the Play Integrity nonce is derived from the store (a stable
159
159
  // per-session value), NOT from the ENC body. pushToken comes from the optional
160
160
  // push client — empty by default.
161
+ // A field with no value is omitted from the body rather than sent empty.
162
+ // buildForm drops null and keeps '', and the registration server validates the
163
+ // shape of what it receives: an empty push_code is a malformed push_code, and
164
+ // it answers with bad_param/bad_format naming that field. An absent field has
165
+ // nothing to validate, which is what an unattested client should send.
166
+ // Real values are still sent whenever a Frida attestation server supplies them.
167
+ const orNull = (v) => (v === '' || v === undefined ? null : v);
168
+
161
169
  async function attestationFields(device, nonceB64, opts) {
162
170
  opts = opts || {};
163
- const pushToken = opts.pushToken || '';
171
+ const pushToken = orNull(opts.pushToken);
164
172
 
165
173
  if (device && device.os === 'android') {
166
174
  const a = await androidIntegrity(nonceB64 || '');
167
175
  return [
168
- 'gpia', a.gpia,
169
- '_gg', a.gg,
170
- '_gi', a.gi,
171
- '_gp', a.gp,
172
- '_ge', a.ge,
173
- '_ga', a.ga,
176
+ 'gpia', orNull(a.gpia),
177
+ '_gg', orNull(a.gg),
178
+ '_gi', orNull(a.gi),
179
+ '_gp', orNull(a.gp),
180
+ '_ge', orNull(a.ge),
181
+ '_ga', orNull(a.ga),
174
182
  'push_token', pushToken
175
183
  ];
176
184
  }
@@ -424,7 +424,9 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
424
424
  'manage_call_permission', 'false',
425
425
  'clicked_education_link', 'false',
426
426
  'aid', '',
427
- 'push_code', store.pushCode || ''
427
+ // Omitted unless a push client supplies a real code — an empty value is
428
+ // rejected as bad_format, an absent field is not validated at all.
429
+ 'push_code', null
428
430
  ];
429
431
  }
430
432
 
@@ -434,7 +436,8 @@ function getRequestVerificationCodeParameters(store, method, meta, device, attem
434
436
  'sim_mcc', meta.mcc,
435
437
  'sim_mnc', meta.mnc,
436
438
  'jailbroken', '0',
437
- 'push_code', store.pushCode || '',
439
+ // Omitted unless a push client supplies a real code (see above).
440
+ 'push_code', null,
438
441
  'cellular_strength', '1'
439
442
  ];
440
443
  }
package/lib/Store.js CHANGED
@@ -64,11 +64,6 @@ function createNewStore(phoneNumber) {
64
64
  // Registration.js): advertising id (UUID) and the 20-byte backup token.
65
65
  const advertisingId = uuidv4();
66
66
  const backupToken = crypto.randomBytes(20);
67
- // Push-notification token sent as `push_code` on /code. The server validates
68
- // its shape and rejects an empty value with bad_param/bad_format, so it has
69
- // to be a real 32-hex-character token. Generated once and persisted, because
70
- // the value must stay the same across the /code and /register pair.
71
- const pushCode = crypto.randomBytes(16).toString('hex');
72
67
 
73
68
  const device = getDeviceConfig();
74
69
  const version = device.os === 'android' ? ANDROID_VERSION_FALLBACK : IOS_VERSION_FALLBACK;
@@ -91,7 +86,6 @@ function createNewStore(phoneNumber) {
91
86
  identityId,
92
87
  advertisingId,
93
88
  backupToken,
94
- pushCode,
95
89
  registered: false,
96
90
  codePending: false, // true after /code request, cleared on successful /register
97
91
  name: 'User',
@@ -146,7 +140,6 @@ function storeToJson(store) {
146
140
  identityId: store.identityId.toString('base64'),
147
141
  advertisingId: store.advertisingId || null,
148
142
  backupToken: store.backupToken ? store.backupToken.toString('base64') : null,
149
- pushCode: store.pushCode || null,
150
143
  registered: !!store.registered,
151
144
  codePending: store.codePending || false,
152
145
  name,
@@ -189,7 +182,6 @@ function storeFromJson(obj) {
189
182
  // enriched /code request always has stable values.
190
183
  advertisingId: obj.advertisingId || uuidv4(),
191
184
  backupToken: obj.backupToken ? Buffer.from(obj.backupToken, 'base64') : crypto.randomBytes(20),
192
- pushCode: obj.pushCode || crypto.randomBytes(16).toString('hex'),
193
185
  registered: !!obj.registered,
194
186
  codePending: obj.codePending || false,
195
187
  name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whalibmob",
3
- "version": "5.5.68",
3
+ "version": "5.5.70",
4
4
  "description": "WhatsApp library for interaction with WhatsApp Mobile API no web",
5
5
  "author": "Kunboruto50",
6
6
  "main": "index.js",