fraud-sim 0.2.0 → 0.5.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +462 -0
  2. package/README.md +621 -81
  3. package/bin/fraud-sim.js +625 -0
  4. package/examples/account-drain-demo/index.js +1 -1
  5. package/examples/adaptive-hardened-demo/index.js +106 -0
  6. package/examples/basic-credential-stuffing/index.js +4 -2
  7. package/examples/express-hardened-target/server.js +230 -0
  8. package/examples/express-mock-target/server.js +153 -2
  9. package/package.json +40 -8
  10. package/src/adapters/http.js +49 -7
  11. package/src/index.js +126 -13
  12. package/src/llm/anthropic.js +7 -0
  13. package/src/llm/deterministic.js +21 -7
  14. package/src/llm/openai.js +7 -0
  15. package/src/locales/index.js +193 -0
  16. package/src/reporting/aggregate.js +338 -0
  17. package/src/reporting/html.js +424 -0
  18. package/src/reporting/index.js +241 -0
  19. package/src/reporting/junit.js +171 -0
  20. package/src/reporting/money.js +105 -0
  21. package/src/runner.js +295 -12
  22. package/src/scenarios/account-drain.js +10 -2
  23. package/src/scenarios/adaptive-attacker.js +33 -11
  24. package/src/scenarios/card-testing.js +174 -0
  25. package/src/scenarios/credential-stuffing.js +146 -20
  26. package/src/scenarios/index.js +37 -6
  27. package/src/scenarios/legitimate-users.js +290 -0
  28. package/src/scenarios/mule-network.js +201 -0
  29. package/src/scenarios/promo-abuse.js +168 -0
  30. package/src/scenarios/session-hijack.js +234 -0
  31. package/src/scenarios/sim-swap-takeover.js +253 -0
  32. package/src/scenarios/slow-drain.js +171 -0
  33. package/src/scenarios/velocity-evasion.js +179 -0
  34. package/src/types.js +160 -0
  35. package/src/utils/clock.js +77 -0
  36. package/src/utils/faker-helpers.js +87 -2
  37. package/src/utils/index.js +69 -0
  38. package/src/utils/random-source.js +104 -0
  39. package/src/utils/random.js +257 -44
  40. package/src/utils/results.js +84 -0
  41. package/src/utils/rng.js +3 -4
  42. package/src/utils/throttle.js +127 -0
  43. package/types/adapters/http.d.ts +6 -0
  44. package/types/adapters/index.d.ts +3 -0
  45. package/types/adapters/logging.d.ts +19 -0
  46. package/types/adapters/mock.d.ts +32 -0
  47. package/types/index.d.ts +79 -0
  48. package/types/llm/anthropic.d.ts +17 -0
  49. package/types/llm/deterministic.d.ts +11 -0
  50. package/types/llm/index.d.ts +3 -0
  51. package/types/llm/openai.d.ts +17 -0
  52. package/types/locales/index.d.ts +146 -0
  53. package/types/reporting/aggregate.d.ts +126 -0
  54. package/types/reporting/html.d.ts +1 -0
  55. package/types/reporting/index.d.ts +99 -0
  56. package/types/reporting/junit.d.ts +13 -0
  57. package/types/reporting/money.d.ts +20 -0
  58. package/types/runner.d.ts +10 -0
  59. package/types/scenarios/account-drain.d.ts +25 -0
  60. package/types/scenarios/adaptive-attacker.d.ts +22 -0
  61. package/types/scenarios/card-testing.d.ts +28 -0
  62. package/types/scenarios/credential-stuffing.d.ts +23 -0
  63. package/types/scenarios/index.d.ts +261 -0
  64. package/types/scenarios/legitimate-users.d.ts +49 -0
  65. package/types/scenarios/mule-network.d.ts +17 -0
  66. package/types/scenarios/promo-abuse.d.ts +28 -0
  67. package/types/scenarios/session-hijack.d.ts +15 -0
  68. package/types/scenarios/sim-swap-takeover.d.ts +16 -0
  69. package/types/scenarios/slow-drain.d.ts +28 -0
  70. package/types/scenarios/velocity-evasion.d.ts +31 -0
  71. package/types/streaming/index.d.ts +1 -0
  72. package/types/streaming/sse.d.ts +34 -0
  73. package/types/types.d.ts +191 -0
  74. package/types/utils/clock.d.ts +39 -0
  75. package/types/utils/faker-helpers.d.ts +114 -0
  76. package/types/utils/index.d.ts +8 -0
  77. package/types/utils/random-source.d.ts +39 -0
  78. package/types/utils/random.d.ts +89 -0
  79. package/types/utils/results.d.ts +46 -0
  80. package/types/utils/rng.d.ts +36 -0
  81. package/types/utils/sleep.d.ts +7 -0
  82. package/types/utils/throttle.d.ts +33 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Run a single scenario against a target.
3
+ *
4
+ * @param {string} scenarioId - The ID of a registered scenario
5
+ * @param {object} config
6
+ * @param {TargetAdapter} config.target - A TargetAdapter
7
+ * @param {object} [config.options] - Scenario-specific options (merged with defaults)
8
+ * @param {function} [config.onEvent] - Event callback (event) => void
9
+ * @param {AbortSignal} [config.signal] - For cancellation
10
+ * @param {number} [config.seed] - Seed the run's randomness so it replays
11
+ * identically. Controls every generated value (IPs, devices, amounts,
12
+ * ordering) but not wall-clock timestamps.
13
+ * @param {number} [config.maxRequestsPerSecond=25] - Ceiling on outbound
14
+ * requests. `Infinity` disables it.
15
+ * @returns {Promise<ScenarioReport>}
16
+ */
17
+ export function run(scenarioId: string, config?: {
18
+ target: TargetAdapter;
19
+ options?: object;
20
+ onEvent?: Function;
21
+ signal?: AbortSignal;
22
+ seed?: number;
23
+ maxRequestsPerSecond?: number;
24
+ }): Promise<ScenarioReport>;
25
+ /**
26
+ * Run a suite of scenarios as a benchmark.
27
+ *
28
+ * @param {object} config
29
+ * @param {string} config.name - Human-readable suite name
30
+ * @param {Array<{id: string, options?: object}>} config.scenarios
31
+ * @param {TargetAdapter} config.target
32
+ * @param {function} [config.onEvent]
33
+ * @param {AbortSignal} [config.signal]
34
+ * @param {number} [config.seed] - Seeds the suite. Each scenario gets a
35
+ * distinct derived seed, so the suite replays as a whole without every
36
+ * scenario drawing the same values.
37
+ * @param {number} [config.maxRequestsPerSecond=25] - Shared across the whole
38
+ * suite, including when scenarios run concurrently.
39
+ * @param {number} [config.concurrency=1] - How many scenarios run at once.
40
+ * See the note below before raising it.
41
+ * @returns {Promise<SuiteReport>}
42
+ */
43
+ export function runSuite(config?: {
44
+ name: string;
45
+ scenarios: Array<{
46
+ id: string;
47
+ options?: object;
48
+ }>;
49
+ target: TargetAdapter;
50
+ onEvent?: Function;
51
+ signal?: AbortSignal;
52
+ seed?: number;
53
+ maxRequestsPerSecond?: number;
54
+ concurrency?: number;
55
+ }): Promise<SuiteReport>;
56
+ /**
57
+ * Returns metadata for all registered scenarios.
58
+ * @returns {Array<ScenarioMetadata>}
59
+ */
60
+ export function listScenarios(): Array<ScenarioMetadata>;
61
+ /**
62
+ * Register a custom scenario. Useful for users extending the library
63
+ * or for testing internal scenarios in isolation.
64
+ *
65
+ * @param {Scenario} scenario
66
+ */
67
+ export function registerScenario(scenario: Scenario): void;
68
+ /**
69
+ * Remove a scenario from the registry. Primarily for testing.
70
+ * @param {string} scenarioId
71
+ */
72
+ export function unregisterScenario(scenarioId: string): void;
73
+ export type Scenario = import("./types.js").Scenario;
74
+ export type ScenarioMetadata = import("./types.js").ScenarioMetadata;
75
+ export type ScenarioReport = import("./types.js").ScenarioReport;
76
+ export type SuiteReport = import("./types.js").SuiteReport;
77
+ export type TargetAdapter = import("./types.js").TargetAdapter;
78
+ export { DEMO_IP_POOL, demoIpPool, pickDemoIp } from "./utils/random.js";
79
+ export { LOCALES, localeIds, resolveLocale, DEFAULT_LOCALE } from "./locales/index.js";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @param {object} config
3
+ * @param {string} config.apiKey - Anthropic API key.
4
+ * @param {string} [config.model='claude-sonnet-5']
5
+ * @param {number} [config.maxTokens=1024]
6
+ * @param {function} [config.fetch] - Injectable fetch, for testing.
7
+ * @returns {{ name: string, generateStrategy: function }}
8
+ */
9
+ export function anthropic(config?: {
10
+ apiKey: string;
11
+ model?: string;
12
+ maxTokens?: number;
13
+ fetch?: Function;
14
+ }): {
15
+ name: string;
16
+ generateStrategy: Function;
17
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @param {object} [config]
3
+ * @param {number} [config.seed=1] - Seed for reproducible strategies.
4
+ * @returns {{ name: string, generateStrategy: function }}
5
+ */
6
+ export function deterministic(config?: {
7
+ seed?: number;
8
+ }): {
9
+ name: string;
10
+ generateStrategy: Function;
11
+ };
@@ -0,0 +1,3 @@
1
+ export { deterministic } from "./deterministic.js";
2
+ export { openai } from "./openai.js";
3
+ export { anthropic } from "./anthropic.js";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @param {object} config
3
+ * @param {string} config.apiKey - OpenAI API key.
4
+ * @param {string} [config.model='gpt-4o']
5
+ * @param {number} [config.temperature=0.7]
6
+ * @param {function} [config.fetch] - Injectable fetch, for testing.
7
+ * @returns {{ name: string, generateStrategy: function }}
8
+ */
9
+ export function openai(config?: {
10
+ apiKey: string;
11
+ model?: string;
12
+ temperature?: number;
13
+ fetch?: Function;
14
+ }): {
15
+ name: string;
16
+ generateStrategy: Function;
17
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Resolve a locale id to its pack.
3
+ *
4
+ * Unknown ids throw rather than falling back: silently generating US-shaped
5
+ * data for a run that asked for `DE` would produce a result that looks valid
6
+ * and measures the wrong market.
7
+ *
8
+ * @param {string|Locale} [id]
9
+ * @returns {Locale}
10
+ */
11
+ export function resolveLocale(id?: string | Locale): Locale;
12
+ /** Ids of the bundled packs. */
13
+ export function localeIds(): string[];
14
+ export namespace LOCALES {
15
+ namespace NG {
16
+ let id: string;
17
+ let name: string;
18
+ let currency: string;
19
+ let minorUnits: number;
20
+ let banks: string[];
21
+ let firstNames: string[];
22
+ let lastNames: string[];
23
+ function accountNumber(rand: any): string;
24
+ function phoneNumber(rand: any): string;
25
+ let cities: {
26
+ city: string;
27
+ lat: number;
28
+ lon: number;
29
+ }[];
30
+ }
31
+ namespace US {
32
+ let id_1: string;
33
+ export { id_1 as id };
34
+ let name_1: string;
35
+ export { name_1 as name };
36
+ let currency_1: string;
37
+ export { currency_1 as currency };
38
+ let minorUnits_1: number;
39
+ export { minorUnits_1 as minorUnits };
40
+ let banks_1: string[];
41
+ export { banks_1 as banks };
42
+ let firstNames_1: string[];
43
+ export { firstNames_1 as firstNames };
44
+ let lastNames_1: string[];
45
+ export { lastNames_1 as lastNames };
46
+ export function accountNumber_1(rand: any): string;
47
+ export { accountNumber_1 as accountNumber };
48
+ export function phoneNumber_1(rand: any): string;
49
+ export { phoneNumber_1 as phoneNumber };
50
+ let cities_1: {
51
+ city: string;
52
+ lat: number;
53
+ lon: number;
54
+ }[];
55
+ export { cities_1 as cities };
56
+ }
57
+ namespace GB {
58
+ let id_2: string;
59
+ export { id_2 as id };
60
+ let name_2: string;
61
+ export { name_2 as name };
62
+ let currency_2: string;
63
+ export { currency_2 as currency };
64
+ let minorUnits_2: number;
65
+ export { minorUnits_2 as minorUnits };
66
+ let banks_2: string[];
67
+ export { banks_2 as banks };
68
+ let firstNames_2: string[];
69
+ export { firstNames_2 as firstNames };
70
+ let lastNames_2: string[];
71
+ export { lastNames_2 as lastNames };
72
+ export function accountNumber_2(rand: any): string;
73
+ export { accountNumber_2 as accountNumber };
74
+ export function phoneNumber_2(rand: any): string;
75
+ export { phoneNumber_2 as phoneNumber };
76
+ let cities_2: {
77
+ city: string;
78
+ lat: number;
79
+ lon: number;
80
+ }[];
81
+ export { cities_2 as cities };
82
+ }
83
+ namespace KE {
84
+ let id_3: string;
85
+ export { id_3 as id };
86
+ let name_3: string;
87
+ export { name_3 as name };
88
+ let currency_3: string;
89
+ export { currency_3 as currency };
90
+ let minorUnits_3: number;
91
+ export { minorUnits_3 as minorUnits };
92
+ let banks_3: string[];
93
+ export { banks_3 as banks };
94
+ let firstNames_3: string[];
95
+ export { firstNames_3 as firstNames };
96
+ let lastNames_3: string[];
97
+ export { lastNames_3 as lastNames };
98
+ export function accountNumber_3(rand: any): string;
99
+ export { accountNumber_3 as accountNumber };
100
+ export function phoneNumber_3(rand: any): string;
101
+ export { phoneNumber_3 as phoneNumber };
102
+ let cities_3: {
103
+ city: string;
104
+ lat: number;
105
+ lon: number;
106
+ }[];
107
+ export { cities_3 as cities };
108
+ }
109
+ }
110
+ /** The market used when a run does not name one. */
111
+ export const DEFAULT_LOCALE: "US";
112
+ export type Locale = {
113
+ /**
114
+ * - ISO 3166-1 alpha-2 country code.
115
+ */
116
+ id: string;
117
+ name: string;
118
+ /**
119
+ * - ISO 4217 code.
120
+ */
121
+ currency: string;
122
+ /**
123
+ * - Minor units per major unit (100 for most).
124
+ */
125
+ minorUnits: number;
126
+ banks: string[];
127
+ firstNames: string[];
128
+ lastNames: string[];
129
+ /**
130
+ * - Market-shaped account identifier.
131
+ */
132
+ accountNumber: () => string;
133
+ /**
134
+ * - Market-shaped subscriber number.
135
+ */
136
+ phoneNumber: () => string;
137
+ /**
138
+ * - Domestic
139
+ * locations for the IP pool's "this looks like home" entries.
140
+ */
141
+ cities: Array<{
142
+ city: string;
143
+ lat: number;
144
+ lon: number;
145
+ }>;
146
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Turning raw run data into the things a report needs to say.
3
+ *
4
+ * Kept separate from rendering so every output format — HTML, JUnit, JSON —
5
+ * agrees on the numbers. A format that computed its own totals would eventually
6
+ * disagree with another one, and a report that contradicts itself is worse than
7
+ * no report.
8
+ */
9
+ /** Accepts a single ScenarioReport, an array of them, or a SuiteReport. */
10
+ export function normaliseInput(result: any): {
11
+ name: any;
12
+ reports: any;
13
+ };
14
+ /** True for traffic a defence should let through — see legitimate-users. */
15
+ export function isLegitimate(report: any): boolean;
16
+ /**
17
+ * Totals across every attack scenario, using the same denominator rule as a
18
+ * single run: rates are over attempts that actually received a decision.
19
+ *
20
+ * Legitimate traffic is kept out of these figures and summarised under
21
+ * `legitimate` instead. A blocked customer added to the block rate would make
22
+ * a defence look better the more good customers it refused.
23
+ */
24
+ export function totalsFor(reports: any): {
25
+ scoredAttempts: number;
26
+ blockRate: number;
27
+ stepUpRate: number;
28
+ allowRate: number;
29
+ errorRate: number;
30
+ scenarios: any;
31
+ attacksSucceeded: any;
32
+ estimatedLossMinor: any;
33
+ estimatedLossKobo: any;
34
+ legitimate: {
35
+ scoredAttempts: number;
36
+ falsePositiveRate: number;
37
+ frictionRate: number;
38
+ errorRate: number;
39
+ customers: number;
40
+ customersBlocked: number;
41
+ valueBlockedMinor: number;
42
+ attempts: number;
43
+ blocked: number;
44
+ stepUp: number;
45
+ allowed: number;
46
+ errors: number;
47
+ };
48
+ attempts: number;
49
+ blocked: number;
50
+ stepUp: number;
51
+ allowed: number;
52
+ errors: number;
53
+ };
54
+ /**
55
+ * What the defence cost ordinary customers, or `null` when the run included
56
+ * no legitimate traffic — absent is different from zero, and a report should
57
+ * never claim "no false positives" for a run that could not have seen any.
58
+ */
59
+ export function legitimateTotalsFor(reports: any): {
60
+ scoredAttempts: number;
61
+ falsePositiveRate: number;
62
+ frictionRate: number;
63
+ errorRate: number;
64
+ customers: number;
65
+ customersBlocked: number;
66
+ valueBlockedMinor: number;
67
+ attempts: number;
68
+ blocked: number;
69
+ stepUp: number;
70
+ allowed: number;
71
+ errors: number;
72
+ };
73
+ /** Reason codes across the whole run, most frequent first. */
74
+ export function reasonFrequency(reports: any): [any, any][];
75
+ /** Latency across the whole run — merged samples, not an average of averages. */
76
+ export function latencyFor(reports: any): {
77
+ count: number;
78
+ p50: any;
79
+ p95: any;
80
+ p99: any;
81
+ max: any;
82
+ approximate?: undefined;
83
+ } | {
84
+ count: any;
85
+ p50: number;
86
+ p95: number;
87
+ p99: number;
88
+ max: number;
89
+ approximate: boolean;
90
+ };
91
+ /**
92
+ * Classify what a scenario's run actually means, which is not the same as its
93
+ * block rate. Order matters: reaching money outranks everything, and a result
94
+ * we cannot trust must never be reported as a pass.
95
+ */
96
+ /**
97
+ * A scenario's extracted value, in the currency's minor unit.
98
+ *
99
+ * Amounts are minor-unit integers throughout — kobo, cents, the same thing
100
+ * structurally — so nothing here is currency-specific. `estimatedLossKobo`
101
+ * is the original naira-flavoured name, kept working.
102
+ */
103
+ export function lossOf(report: any): any;
104
+ export function outcomeFor(report: any): {
105
+ kind: string;
106
+ label: string;
107
+ severity: string;
108
+ };
109
+ /**
110
+ * The findings list, worst first.
111
+ *
112
+ * Deliberately conservative: it reports what the data supports and does not
113
+ * guess at causes. A generated finding that speculates is a finding a reader
114
+ * will eventually catch being wrong.
115
+ */
116
+ export function findingsFor(reports: any, options?: {}): {
117
+ severity: string;
118
+ scenario: any;
119
+ title: string;
120
+ detail: string;
121
+ stats: any[][];
122
+ }[];
123
+ export function pct(n: any): string;
124
+ /** @deprecated Use `formatMoney` from ./money.js — kept for compatibility. */
125
+ export function fmtNaira(kobo: any): string;
126
+ export function fmtMs(ms: any): string;
@@ -0,0 +1 @@
1
+ export function renderHtml(result: any, options?: {}): string;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Render a run as a shareable report.
3
+ *
4
+ * Synchronous and pure — it returns a string and touches no filesystem, so the
5
+ * caller decides where output goes and nothing surprising happens on import.
6
+ *
7
+ * @param {object} result - A ScenarioReport, an array of them, or a SuiteReport.
8
+ * @param {object} [options]
9
+ * @param {'html'|'junit'|'json'|'text'} [options.format='html']
10
+ * @param {string} [options.title]
11
+ * @param {string} [options.targetName]
12
+ * @param {string} [options.version]
13
+ * @param {number} [options.failUnder] - Block rate below which a scenario is a
14
+ * CI failure. Used by the junit and text formats.
15
+ * @param {number} [options.maxFalsePositiveRate] - Share of legitimate
16
+ * customer events that may be blocked before the run fails. Used by the
17
+ * junit and text formats.
18
+ * @param {string} [options.currency='NGN'] - Display currency. Amounts are
19
+ * minor-unit integers, so this changes presentation only.
20
+ * @param {string} [options.locale] - Defaults to a locale for the currency.
21
+ * @returns {string}
22
+ */
23
+ export function generateReport(result: object, options?: {
24
+ format?: "html" | "junit" | "json" | "text";
25
+ title?: string;
26
+ targetName?: string;
27
+ version?: string;
28
+ failUnder?: number;
29
+ maxFalsePositiveRate?: number;
30
+ currency?: string;
31
+ locale?: string;
32
+ }): string;
33
+ /**
34
+ * The structured summary behind every format. Useful on its own when you want
35
+ * to assert on a run in CI without parsing a rendered document.
36
+ */
37
+ export function summarise(result: any, options?: {}): {
38
+ name: any;
39
+ generatedAt: any;
40
+ totals: {
41
+ scoredAttempts: number;
42
+ blockRate: number;
43
+ stepUpRate: number;
44
+ allowRate: number;
45
+ errorRate: number;
46
+ scenarios: any;
47
+ attacksSucceeded: any;
48
+ estimatedLossMinor: any;
49
+ estimatedLossKobo: any;
50
+ legitimate: {
51
+ scoredAttempts: number;
52
+ falsePositiveRate: number;
53
+ frictionRate: number;
54
+ errorRate: number;
55
+ customers: number;
56
+ customersBlocked: number;
57
+ valueBlockedMinor: number;
58
+ attempts: number;
59
+ blocked: number;
60
+ stepUp: number;
61
+ allowed: number;
62
+ errors: number;
63
+ };
64
+ attempts: number;
65
+ blocked: number;
66
+ stepUp: number;
67
+ allowed: number;
68
+ errors: number;
69
+ };
70
+ latency: {
71
+ count: number;
72
+ p50: any;
73
+ p95: any;
74
+ p99: any;
75
+ max: any;
76
+ approximate?: undefined;
77
+ } | {
78
+ count: any;
79
+ p50: number;
80
+ p95: number;
81
+ p99: number;
82
+ max: number;
83
+ approximate: boolean;
84
+ };
85
+ findings: {
86
+ severity: string;
87
+ scenario: any;
88
+ title: string;
89
+ detail: string;
90
+ stats: any[][];
91
+ }[];
92
+ reasonCounts: any;
93
+ scenarios: any;
94
+ };
95
+ import { renderHtml } from './html.js';
96
+ import { renderJUnit } from './junit.js';
97
+ export { renderHtml, renderJUnit };
98
+ export { formatMoney, currencySymbol } from "./money.js";
99
+ export { normaliseInput, totalsFor, legitimateTotalsFor, isLegitimate, reasonFrequency, latencyFor, findingsFor, outcomeFor } from "./aggregate.js";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @param {object} result - A ScenarioReport, an array of them, or a SuiteReport.
3
+ * @param {object} [options]
4
+ * @param {number} [options.failUnder] - Minimum acceptable block rate per scenario.
5
+ * @param {number} [options.maxFalsePositiveRate] - Maximum acceptable share of
6
+ * legitimate customer events blocked.
7
+ * @param {string} [options.suiteName]
8
+ */
9
+ export function renderJUnit(result: object, options?: {
10
+ failUnder?: number;
11
+ maxFalsePositiveRate?: number;
12
+ suiteName?: string;
13
+ }): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * How many minor units make one major unit of this currency.
3
+ * 100 for most, 1 for JPY and KRW, 1000 for a few Gulf currencies.
4
+ */
5
+ export function minorUnitsPerMajor(currency: any, locale: any): number;
6
+ /**
7
+ * Render an integer number of minor units as money.
8
+ *
9
+ * @param {number} minorUnits - e.g. 60000000 kobo, or 6060000 cents
10
+ * @param {object} [options]
11
+ * @param {string} [options.currency='NGN']
12
+ * @param {string} [options.locale] - Defaults to a sensible locale for the currency.
13
+ * @returns {string}
14
+ */
15
+ export function formatMoney(minorUnits: number, options?: {
16
+ currency?: string;
17
+ locale?: string;
18
+ }): string;
19
+ /** The currency's symbol on its own, for compact labels like a stat tile. */
20
+ export function currencySymbol(currency: string, locale: any): any;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Ceiling on outbound requests per second, applied to every scenario unless
3
+ * overridden. High enough that the shipped scenarios never touch it, low
4
+ * enough that a `delayMs: 0` typo against a real host stays a simulation
5
+ * rather than becoming a denial-of-service. Pass `Infinity` to disable.
6
+ */
7
+ export const DEFAULT_MAX_REQUESTS_PER_SECOND: 25;
8
+ export class Runner {
9
+ run(scenario: any, config: any): Promise<any>;
10
+ }
@@ -0,0 +1,25 @@
1
+ declare namespace _default {
2
+ let id: string;
3
+ let name: string;
4
+ let description: string;
5
+ namespace defaultOptions {
6
+ let targetUserId: any;
7
+ let attackerIp: any;
8
+ let withdrawalCount: number;
9
+ let amountMinor: number;
10
+ let delayBetweenWithdrawalsMs: number;
11
+ let postLoginPauseMs: number;
12
+ let skipPasswordChange: boolean;
13
+ }
14
+ function run(ctx: any): Promise<{
15
+ attempts: any;
16
+ blocked: any;
17
+ stepUp: any;
18
+ allowed: any;
19
+ errors: any;
20
+ loginDecision: any;
21
+ withdrawalDecisions: any;
22
+ attackerSucceeded: any;
23
+ }>;
24
+ }
25
+ export default _default;
@@ -0,0 +1,22 @@
1
+ declare namespace _default {
2
+ let id: string;
3
+ let name: string;
4
+ let description: string;
5
+ namespace defaultOptions {
6
+ let targetUserId: any;
7
+ let maxRounds: number;
8
+ let baselineIp: string;
9
+ let delayMs: number;
10
+ }
11
+ function run(ctx: any): Promise<{
12
+ attempts: number;
13
+ blocked: number;
14
+ stepUp: number;
15
+ allowed: number;
16
+ errors: number;
17
+ rounds: number;
18
+ outcome: string;
19
+ strategyHistory: any[];
20
+ }>;
21
+ }
22
+ export default _default;
@@ -0,0 +1,28 @@
1
+ declare namespace _default {
2
+ let id: string;
3
+ let name: string;
4
+ let description: string;
5
+ namespace defaultOptions {
6
+ let attackerUserId: string;
7
+ let cardCount: number;
8
+ let binCount: number;
9
+ let microAmountMinor: number;
10
+ let largeAmountMinor: number;
11
+ let escalateAfter: number;
12
+ let delayMs: number;
13
+ let maxTotalAttempts: number;
14
+ let ipPersona: string;
15
+ let reuseIp: boolean;
16
+ }
17
+ function run(ctx: any): Promise<{
18
+ estimatedLossKobo: number;
19
+ attackerSucceeded: boolean;
20
+ binsUsed: string[];
21
+ attempts: number;
22
+ blocked: number;
23
+ stepUp: number;
24
+ allowed: number;
25
+ errors: number;
26
+ }>;
27
+ }
28
+ export default _default;
@@ -0,0 +1,23 @@
1
+ declare namespace _default {
2
+ let id: string;
3
+ let name: string;
4
+ let description: string;
5
+ namespace defaultOptions {
6
+ let targetUserIds: any;
7
+ let targetUserId: any;
8
+ let attackerCount: number;
9
+ let attemptsPerIp: number;
10
+ let delayMs: number;
11
+ let maxTotalAttempts: number;
12
+ let ipPersona: string;
13
+ }
14
+ function run(ctx: any): Promise<{
15
+ attempts: number;
16
+ blocked: number;
17
+ stepUp: number;
18
+ allowed: number;
19
+ errors: number;
20
+ accountsTargeted: number;
21
+ }>;
22
+ }
23
+ export default _default;