@agentsim/sdk 0.21.0 → 0.22.0
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/README.md +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/playwright.d.ts +17 -0
- package/dist/playwright.d.ts.map +1 -0
- package/dist/playwright.js +62 -0
- package/dist/playwright.js.map +1 -0
- package/dist/playwright.test.d.ts +2 -0
- package/dist/playwright.test.d.ts.map +1 -0
- package/dist/playwright.test.js +121 -0
- package/dist/playwright.test.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @agentsim/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AgentSIM is the control plane for agents hitting the real world's auth walls. SMS OTP is the first challenge type. This TypeScript/JavaScript SDK has zero runtime dependencies and works in Node.js 18+, Bun, Deno, and Edge runtimes.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ import { provision } from "@agentsim/sdk";
|
|
|
16
16
|
|
|
17
17
|
// Using AsyncDisposable (TypeScript 5.2+, recommended)
|
|
18
18
|
await using num = await provision({ agentId: "checkout-bot", country: "US" });
|
|
19
|
-
await enterPhoneNumber(num.number); // "+
|
|
19
|
+
await enterPhoneNumber(num.number); // "+12025550142"
|
|
20
20
|
const otp = await num.waitForOtp({ timeout: 60 });
|
|
21
21
|
await enterOtp(otp.otpCode); // "847291"
|
|
22
22
|
// number auto-released via [Symbol.asyncDispose]
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { ProvisionOptions } from "./client.js";
|
|
|
4
4
|
export { AgentSimClient } from "./client.js";
|
|
5
5
|
export { NumberSession } from "./session.js";
|
|
6
6
|
export type { ProvisionOptions, OtpResult, SmsMessage, WaitForOtpOptions, ReregistrationInfo } from "./client.js";
|
|
7
|
+
export { classifyStall } from "./playwright.js";
|
|
8
|
+
export type { ChallengeChannel, LocatorLike, PageLike, StallOutcome, StallSessionEvidence } from "./playwright.js";
|
|
7
9
|
export { AgentSimError, AuthenticationError, ForbiddenError, PoolExhaustedError, SessionNotFoundError, OtpTimeoutError, RateLimitError, ValidationError, ApiError, } from "./errors.js";
|
|
8
10
|
/**
|
|
9
11
|
* Provision a temporary phone number for the given agent. Starts a billable session.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,QAAQ,GACT,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,gBAAgB,EACzB,cAAc,CAAC,EAAE,cAAc,GAAG,MAAM,GACvC,OAAO,CAAC,aAAa,CAAC,CAkBxB"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACnH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,QAAQ,GACT,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,gBAAgB,EACzB,cAAc,CAAC,EAAE,cAAc,GAAG,MAAM,GACvC,OAAO,CAAC,aAAa,CAAC,CAkBxB"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { AgentSimClient } from "./client.js";
|
|
|
2
2
|
import { NumberSession } from "./session.js";
|
|
3
3
|
export { AgentSimClient } from "./client.js";
|
|
4
4
|
export { NumberSession } from "./session.js";
|
|
5
|
+
export { classifyStall } from "./playwright.js";
|
|
5
6
|
export { AgentSimError, AuthenticationError, ForbiddenError, PoolExhaustedError, SessionNotFoundError, OtpTimeoutError, RateLimitError, ValidationError, ApiError, } from "./errors.js";
|
|
6
7
|
/**
|
|
7
8
|
* Provision a temporary phone number for the given agent. Starts a billable session.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,QAAQ,GACT,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAyB,EACzB,cAAwC;IAExC,IAAI,MAAsB,CAAC;IAE3B,IAAI,cAAc,YAAY,cAAc,EAAE,CAAC;QAC7C,MAAM,GAAG,cAAc,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,IAAI,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAwB,MAAM,EAAE,WAAW,EAAE;QAC5E,QAAQ,EAAE,OAAO,CAAC,OAAO;QACzB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,WAAW,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;QACvC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnE,CAAC,CAAC;IAEH,OAAO,IAAI,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,eAAe,EACf,QAAQ,GACT,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAyB,EACzB,cAAwC;IAExC,IAAI,MAAsB,CAAC;IAE3B,IAAI,cAAc,YAAY,cAAc,EAAE,CAAC;QAC7C,MAAM,GAAG,cAAc,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,IAAI,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAwB,MAAM,EAAE,WAAW,EAAE;QAC5E,QAAQ,EAAE,OAAO,CAAC,OAAO;QACzB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,WAAW,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;QACvC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnE,CAAC,CAAC;IAEH,OAAO,IAAI,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ChallengeChannel = "sms_otp" | "email_otp" | "magic_link" | "webauthn_required";
|
|
2
|
+
export type StallOutcome = "otp_received" | "sms_no_otp" | "no_sms" | "phone_rejected" | "anti_bot_gate" | "email_received" | "magic_link_received" | "webauthn_required" | "no_email";
|
|
3
|
+
export interface LocatorLike {
|
|
4
|
+
first(): LocatorLike;
|
|
5
|
+
isVisible(): Promise<boolean>;
|
|
6
|
+
innerText(): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
export interface PageLike {
|
|
9
|
+
locator(selector: string): LocatorLike;
|
|
10
|
+
}
|
|
11
|
+
export interface StallSessionEvidence {
|
|
12
|
+
channel: ChallengeChannel;
|
|
13
|
+
challengeValue?: string | null;
|
|
14
|
+
messageCount: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function classifyStall(page: PageLike, session: StallSessionEvidence): Promise<StallOutcome>;
|
|
17
|
+
//# sourceMappingURL=playwright.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,mBAAmB,CAAC;AAE5F,MAAM,MAAM,YAAY,GACpB,cAAc,GACd,YAAY,GACZ,QAAQ,GACR,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,mBAAmB,GACnB,UAAU,CAAC;AAEf,MAAM,WAAW,WAAW;IAC1B,KAAK,IAAI,WAAW,CAAC;IACrB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AAgCD,wBAAsB,aAAa,CACjC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC,CA4CvB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const ANTI_BOT_SELECTOR = [
|
|
2
|
+
'iframe[src*="recaptcha" i]',
|
|
3
|
+
'iframe[src*="hcaptcha" i]',
|
|
4
|
+
'iframe[src*="arkoselabs" i]',
|
|
5
|
+
"[data-sitekey]",
|
|
6
|
+
'[class*="captcha" i]',
|
|
7
|
+
].join(", ");
|
|
8
|
+
const WEBAUTHN_SELECTOR = [
|
|
9
|
+
'iframe[src*="webauthn" i]',
|
|
10
|
+
'[autocomplete="webauthn"]',
|
|
11
|
+
'[data-testid*="passkey" i]',
|
|
12
|
+
'[data-testid*="webauthn" i]',
|
|
13
|
+
'[id*="passkey" i]',
|
|
14
|
+
'[id*="webauthn" i]',
|
|
15
|
+
'[class*="passkey" i]',
|
|
16
|
+
'[class*="webauthn" i]',
|
|
17
|
+
'button[data-action*="passkey" i]',
|
|
18
|
+
'button[data-action*="webauthn" i]',
|
|
19
|
+
].join(", ");
|
|
20
|
+
const EMAIL_FIELD_SELECTOR = 'input[type="email"], input[autocomplete="email"]';
|
|
21
|
+
const OTP_FIELD_SELECTOR = [
|
|
22
|
+
'input[autocomplete="one-time-code"]',
|
|
23
|
+
'input[name*="otp" i]',
|
|
24
|
+
'input[id*="otp" i]',
|
|
25
|
+
'input[name*="code" i]',
|
|
26
|
+
'input[id*="code" i]',
|
|
27
|
+
].join(", ");
|
|
28
|
+
export async function classifyStall(page, session) {
|
|
29
|
+
if (session.challengeValue?.trim()) {
|
|
30
|
+
return {
|
|
31
|
+
sms_otp: "otp_received",
|
|
32
|
+
email_otp: "email_received",
|
|
33
|
+
magic_link: "magic_link_received",
|
|
34
|
+
webauthn_required: "webauthn_required",
|
|
35
|
+
}[session.channel];
|
|
36
|
+
}
|
|
37
|
+
const pageText = await page.locator("body").innerText();
|
|
38
|
+
if (await page.locator(ANTI_BOT_SELECTOR).first().isVisible()
|
|
39
|
+
|| /captcha|verify (?:you are|that you are) human|(?:not a|are you (?:a )?) robot|security challenge/i.test(pageText)) {
|
|
40
|
+
return "anti_bot_gate";
|
|
41
|
+
}
|
|
42
|
+
if (await page.locator(WEBAUTHN_SELECTOR).first().isVisible()
|
|
43
|
+
|| /\b(?:passkey|security key|webauthn|touch id|windows hello|face id|biometric|fingerprint auth(?:entication)?|use your (?:passkey|security key)|authenticate with (?:passkey|security key|biometric))\b/i.test(pageText)
|
|
44
|
+
|| /navigator\.credentials\.get/i.test(pageText)
|
|
45
|
+
|| /PublicKeyCredential/i.test(pageText)) {
|
|
46
|
+
return "webauthn_required";
|
|
47
|
+
}
|
|
48
|
+
if (/\b(?:phone|mobile)(?: number)?\b[\s\S]{0,80}\b(?:cannot|can't|invalid|unsupported|rejected|not (?:accepted|supported|allowed)|try another)\b/i.test(pageText)
|
|
49
|
+
|| /\b(?:invalid|unsupported|rejected)\b[\s\S]{0,40}\b(?:phone|mobile)(?: number)?\b/i.test(pageText)) {
|
|
50
|
+
return "phone_rejected";
|
|
51
|
+
}
|
|
52
|
+
if (await page.locator(EMAIL_FIELD_SELECTOR).first().isVisible()
|
|
53
|
+
|| (session.channel !== "sms_otp"
|
|
54
|
+
&& await page.locator(OTP_FIELD_SELECTOR).first().isVisible())) {
|
|
55
|
+
return "no_email";
|
|
56
|
+
}
|
|
57
|
+
if (session.channel === "sms_otp" && session.messageCount > 0) {
|
|
58
|
+
return "sms_no_otp";
|
|
59
|
+
}
|
|
60
|
+
return session.channel === "sms_otp" ? "no_sms" : "no_email";
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=playwright.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.js","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"AA6BA,MAAM,iBAAiB,GAAG;IACxB,4BAA4B;IAC5B,2BAA2B;IAC3B,6BAA6B;IAC7B,gBAAgB;IAChB,sBAAsB;CACvB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,iBAAiB,GAAG;IACxB,2BAA2B;IAC3B,2BAA2B;IAC3B,4BAA4B;IAC5B,6BAA6B;IAC7B,mBAAmB;IACnB,oBAAoB;IACpB,sBAAsB;IACtB,uBAAuB;IACvB,kCAAkC;IAClC,mCAAmC;CACpC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,oBAAoB,GAAG,kDAAkD,CAAC;AAChF,MAAM,kBAAkB,GAAG;IACzB,qCAAqC;IACrC,sBAAsB;IACtB,oBAAoB;IACpB,uBAAuB;IACvB,qBAAqB;CACtB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAc,EACd,OAA6B;IAE7B,IAAI,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,EAAE,CAAC;QACnC,OAAQ;YACN,OAAO,EAAE,cAAc;YACvB,SAAS,EAAE,gBAAgB;YAC3B,UAAU,EAAE,qBAAqB;YACjC,iBAAiB,EAAE,mBAAmB;SAC7B,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;IACxD,IACE,MAAM,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE;WACtD,mGAAmG,CAAC,IAAI,CAAC,QAAQ,CAAC,EACrH,CAAC;QACD,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,IACE,MAAM,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE;WACtD,wMAAwM,CAAC,IAAI,CAAC,QAAQ,CAAC;WACvN,8BAA8B,CAAC,IAAI,CAAC,QAAQ,CAAC;WAC7C,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,EACxC,CAAC;QACD,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IACD,IACE,+IAA+I,CAAC,IAAI,CAAC,QAAQ,CAAC;WAC3J,mFAAmF,CAAC,IAAI,CAAC,QAAQ,CAAC,EACrG,CAAC;QACD,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,IACE,MAAM,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE;WACzD,CACD,OAAO,CAAC,OAAO,KAAK,SAAS;eAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS,EAAE,CAC9D,EACD,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;QAC9D,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.test.d.ts","sourceRoot":"","sources":["../src/playwright.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { classifyStall } from "./index.js";
|
|
3
|
+
function fakePage(text = "", visible = []) {
|
|
4
|
+
return {
|
|
5
|
+
locator(selector) {
|
|
6
|
+
const locator = {
|
|
7
|
+
first: () => locator,
|
|
8
|
+
isVisible: async () => visible.some((marker) => selector.includes(marker)),
|
|
9
|
+
innerText: async () => selector === "body" ? text : "",
|
|
10
|
+
};
|
|
11
|
+
return locator;
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
describe("classifyStall", () => {
|
|
16
|
+
it("reports a resolved SMS challenge as OTP received", async () => {
|
|
17
|
+
const page = {
|
|
18
|
+
locator() {
|
|
19
|
+
throw new Error("resolved challenges should not inspect the page");
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
await expect(classifyStall(page, {
|
|
23
|
+
channel: "sms_otp",
|
|
24
|
+
challengeValue: "847291",
|
|
25
|
+
messageCount: 1,
|
|
26
|
+
})).resolves.toBe("otp_received");
|
|
27
|
+
});
|
|
28
|
+
it.each([
|
|
29
|
+
["email_otp", "email_received"],
|
|
30
|
+
["magic_link", "magic_link_received"],
|
|
31
|
+
])("reports a resolved %s challenge", async (channel, outcome) => {
|
|
32
|
+
const page = {
|
|
33
|
+
locator() {
|
|
34
|
+
throw new Error("resolved challenges should not inspect the page");
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
await expect(classifyStall(page, {
|
|
38
|
+
channel,
|
|
39
|
+
challengeValue: "https://example.test/verify",
|
|
40
|
+
messageCount: 1,
|
|
41
|
+
})).resolves.toBe(outcome);
|
|
42
|
+
});
|
|
43
|
+
it.each([
|
|
44
|
+
["anti-bot copy", fakePage("Verify you are human to continue")],
|
|
45
|
+
["robot-check copy", fakePage("Please prove you are not a robot")],
|
|
46
|
+
["a visible CAPTCHA iframe", fakePage("", ["recaptcha"])],
|
|
47
|
+
])("reports an anti-bot gate from %s", async (_source, page) => {
|
|
48
|
+
await expect(classifyStall(page, {
|
|
49
|
+
channel: "sms_otp",
|
|
50
|
+
messageCount: 0,
|
|
51
|
+
})).resolves.toBe("anti_bot_gate");
|
|
52
|
+
});
|
|
53
|
+
it.each([
|
|
54
|
+
["passkey copy", fakePage("Use a passkey to continue")],
|
|
55
|
+
["security key copy", fakePage("Insert your security key to authenticate")],
|
|
56
|
+
["Touch ID copy", fakePage("Authenticate with Touch ID")],
|
|
57
|
+
["Windows Hello copy", fakePage("Sign in with Windows Hello")],
|
|
58
|
+
["Face ID copy", fakePage("Use Face ID to continue")],
|
|
59
|
+
["biometric copy", fakePage("Authenticate with biometric verification")],
|
|
60
|
+
["fingerprint auth copy", fakePage("Use fingerprint authentication to proceed")],
|
|
61
|
+
["navigator.credentials.get code", fakePage("navigator.credentials.get({ publicKey")],
|
|
62
|
+
["PublicKeyCredential code", fakePage("new PublicKeyCredential()")],
|
|
63
|
+
["a visible WebAuthn iframe", fakePage("", ["webauthn"])],
|
|
64
|
+
["a visible passkey button", fakePage("", ["passkey"])],
|
|
65
|
+
])("reports WebAuthn from %s", async (_source, page) => {
|
|
66
|
+
await expect(classifyStall(page, {
|
|
67
|
+
channel: "email_otp",
|
|
68
|
+
messageCount: 0,
|
|
69
|
+
})).resolves.toBe("webauthn_required");
|
|
70
|
+
});
|
|
71
|
+
it.each([
|
|
72
|
+
"This phone number cannot be used for verification.",
|
|
73
|
+
"Phone number\ncannot be used here.",
|
|
74
|
+
])("reports an explicit phone rejection", async (text) => {
|
|
75
|
+
await expect(classifyStall(fakePage(text), {
|
|
76
|
+
channel: "sms_otp",
|
|
77
|
+
messageCount: 0,
|
|
78
|
+
})).resolves.toBe("phone_rejected");
|
|
79
|
+
});
|
|
80
|
+
it("reports SMS received without a parsed OTP", async () => {
|
|
81
|
+
const page = fakePage("Enter the code we sent", ["one-time-code"]);
|
|
82
|
+
await expect(classifyStall(page, {
|
|
83
|
+
channel: "sms_otp",
|
|
84
|
+
messageCount: 1,
|
|
85
|
+
})).resolves.toBe("sms_no_otp");
|
|
86
|
+
});
|
|
87
|
+
it("reports no SMS when no message arrived", async () => {
|
|
88
|
+
await expect(classifyStall(fakePage("Enter the code we sent", ["one-time-code"]), {
|
|
89
|
+
channel: "sms_otp",
|
|
90
|
+
messageCount: 0,
|
|
91
|
+
})).resolves.toBe("no_sms");
|
|
92
|
+
});
|
|
93
|
+
it("reports no email while an email challenge field is visible", async () => {
|
|
94
|
+
const page = fakePage("Check your email", ['input[type="email"']);
|
|
95
|
+
await expect(classifyStall(page, {
|
|
96
|
+
channel: "email_otp",
|
|
97
|
+
messageCount: 0,
|
|
98
|
+
})).resolves.toBe("no_email");
|
|
99
|
+
});
|
|
100
|
+
it("recognizes an email-first stall even on the default SMS channel", async () => {
|
|
101
|
+
const page = fakePage("Enter your email to continue", ['input[type="email"']);
|
|
102
|
+
await expect(classifyStall(page, {
|
|
103
|
+
channel: "sms_otp",
|
|
104
|
+
messageCount: 0,
|
|
105
|
+
})).resolves.toBe("no_email");
|
|
106
|
+
});
|
|
107
|
+
it("reports no email while an OTP field is visible", async () => {
|
|
108
|
+
const page = fakePage("Enter the code from your email", ["one-time-code"]);
|
|
109
|
+
await expect(classifyStall(page, {
|
|
110
|
+
channel: "email_otp",
|
|
111
|
+
messageCount: 0,
|
|
112
|
+
})).resolves.toBe("no_email");
|
|
113
|
+
});
|
|
114
|
+
it.each(["email_otp", "magic_link"])("uses no_email as the honest %s fallback", async (channel) => {
|
|
115
|
+
await expect(classifyStall(fakePage(), {
|
|
116
|
+
channel,
|
|
117
|
+
messageCount: 0,
|
|
118
|
+
})).resolves.toBe("no_email");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=playwright.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.test.js","sourceRoot":"","sources":["../src/playwright.test.ts"],"names":[],"mappings":"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"}
|