@qulib/core 0.5.0 → 0.5.1

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.
@@ -29,7 +29,7 @@ export function parseCredentialsJsonString(json) {
29
29
  return out;
30
30
  }
31
31
  export function resolveFormLoginPath(baseUrl, authOptions, authPathId) {
32
- const formPaths = (authOptions ?? []).filter((o) => o.type === 'form-login' && o.requirements.method === 'credentials');
32
+ const formPaths = (authOptions ?? []).filter((o) => (o.type === 'form-login' || o.type === 'form-multi') && o.requirements.method === 'credentials');
33
33
  if (formPaths.length === 0) {
34
34
  throw new Error(`No automatable form-login path detected on ${baseUrl}. Use \`qulib auth init\` for manual login.`);
35
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth-login-run.d.ts","sourceRoot":"","sources":["../../src/cli/auth-login-run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAsB5D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEhE;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoIhB"}
1
+ {"version":3,"file":"auth-login-run.d.ts","sourceRoot":"","sources":["../../src/cli/auth-login-run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAsB5D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAMhE;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoIhB"}
@@ -16,7 +16,9 @@ function sleep(ms) {
16
16
  return new Promise((r) => setTimeout(r, ms));
17
17
  }
18
18
  export function authPathNeedsClickReveal(path) {
19
- return path.type === 'form-login' && path.source === 'heuristic' && !builtInOAuthIds.has(path.id);
19
+ return ((path.type === 'form-login' || path.type === 'form-multi') &&
20
+ (path.source === 'heuristic' || path.source === 'user-local') &&
21
+ !builtInOAuthIds.has(path.id));
20
22
  }
21
23
  export async function runAutomatedAuthLogin(params) {
22
24
  const { chromium } = await import('@playwright/test');
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAIzE,MAAM,MAAM,yBAAyB,GACjC,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,cAAc,GACd,yBAAyB,GACzB,iBAAiB,GACjB,SAAS,CAAC;AAEd,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAsQD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;CAC9B,GAAG,4BAA4B,CA6B/B;AAOD,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAgD9C;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,SAAS,SAAQ,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAelD;AAED,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,SAAS,SAAQ,GAChB,OAAO,CAAC,4BAA4B,CAAC,CAyDvC;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,SAAQ,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,YAAY,CAAC,CAqJvB"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/detect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAIzE,MAAM,MAAM,yBAAyB,GACjC,cAAc,GACd,iBAAiB,GACjB,cAAc,GACd,cAAc,GACd,yBAAyB,GACzB,iBAAiB,GACjB,SAAS,CAAC;AAEd,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAwQD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;CAC9B,GAAG,4BAA4B,CA6B/B;AAOD,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAgD9C;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,EACf,SAAS,SAAQ,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAelD;AAED,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,SAAS,SAAQ,GAChB,OAAO,CAAC,4BAA4B,CAAC,CAyDvC;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,SAAQ,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,YAAY,CAAC,CAqJvB"}
@@ -151,7 +151,7 @@ function authPathsFromOauthButtons(oauthButtons, loginUrl) {
151
151
  }
152
152
  async function probeClickToRevealForms(page, loginUrl, alreadyMatchedTexts, timeoutMs, progress) {
153
153
  const out = [];
154
- const buttons = page.locator('button');
154
+ const buttons = page.locator('button, [role="button"]');
155
155
  const n = await buttons.count();
156
156
  const seenLabels = new Set();
157
157
  const SUBMIT_RE = /^(sign in|log in|submit|continue|next|cancel|close)$/i;
@@ -207,8 +207,9 @@ async function probeClickToRevealForms(page, loginUrl, alreadyMatchedTexts, time
207
207
  await waitNetworkIdleBestEffort(page);
208
208
  continue;
209
209
  }
210
+ await waitNetworkIdleBestEffort(page);
210
211
  try {
211
- await page.locator('input[type="password"]:visible').first().waitFor({ state: 'visible', timeout: 2000 });
212
+ await page.locator('input[type="password"]:visible').first().waitFor({ state: 'visible', timeout: 5000 });
212
213
  }
213
214
  catch {
214
215
  await page.goto(loginUrl, { timeout: timeoutMs, waitUntil: 'domcontentloaded' });
@@ -1 +1 @@
1
- {"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/explore.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,eAAe,EAGrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAiNzE,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,SAAS,SAAQ,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAgL1B"}
1
+ {"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../../src/tools/auth/explore.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,eAAe,EAGrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AA2PzE,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,SAAS,SAAQ,EACjB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAiM1B"}
@@ -185,6 +185,43 @@ async function buildFormPaths(page) {
185
185
  },
186
186
  ];
187
187
  }
188
+ async function probeUserLocalProviderClick(page, providerLabel, loginUrl, timeoutMs) {
189
+ const originBefore = new URL(page.url()).origin;
190
+ let clicked = false;
191
+ try {
192
+ await page.getByRole('button', { name: providerLabel, exact: true }).first().click({ timeout: 3000 });
193
+ clicked = true;
194
+ }
195
+ catch {
196
+ try {
197
+ const escaped = providerLabel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
198
+ await page
199
+ .locator('button, [role="button"]')
200
+ .filter({ hasText: new RegExp(`^\\s*${escaped}\\s*$`, 'i') })
201
+ .first()
202
+ .click({ timeout: 3000 });
203
+ clicked = true;
204
+ }
205
+ catch {
206
+ /* skip */
207
+ }
208
+ }
209
+ if (!clicked)
210
+ return [];
211
+ try {
212
+ await page.waitForLoadState('domcontentloaded', { timeout: 5000 });
213
+ }
214
+ catch { /* best-effort */ }
215
+ await waitNetworkIdleBestEffort(page);
216
+ if (new URL(page.url()).origin !== originBefore) {
217
+ await page.goto(loginUrl, { timeout: timeoutMs, waitUntil: 'domcontentloaded' });
218
+ return [];
219
+ }
220
+ const formPaths = await buildFormPaths(page);
221
+ await page.goto(loginUrl, { timeout: timeoutMs, waitUntil: 'domcontentloaded' });
222
+ await waitNetworkIdleBestEffort(page);
223
+ return formPaths;
224
+ }
188
225
  export async function exploreAuth(url, timeoutMs = 20000, progress) {
189
226
  const browser = await launchBrowser();
190
227
  try {
@@ -246,6 +283,21 @@ export async function exploreAuth(url, timeoutMs = 20000, progress) {
246
283
  continue;
247
284
  }
248
285
  consumed.add(id);
286
+ if (p.source === 'user-local') {
287
+ const probed = await probeUserLocalProviderClick(page, p.label, finalUrl, timeoutMs);
288
+ if (probed.length > 0) {
289
+ for (const fp of probed) {
290
+ authPaths.push({
291
+ ...fp,
292
+ id: p.id,
293
+ label: p.label,
294
+ source: 'user-local',
295
+ });
296
+ progress?.info(`explore_auth path id=${p.id} type=${fp.type} automatable=${fp.automatable} (user-local probe)`);
297
+ }
298
+ continue;
299
+ }
300
+ }
249
301
  authPaths.push({
250
302
  id,
251
303
  label: p.label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qulib/core",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Qulib — analyze deployed web apps for honest quality gaps (CLI + programmatic API)",
5
5
  "license": "MIT",
6
6
  "author": "Tapesh Nagarwal",