@ia-qa/qa-discovery 0.1.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 (84) hide show
  1. package/README.md +378 -0
  2. package/ROADMAP.md +321 -0
  3. package/dist/ai/classify.d.ts +170 -0
  4. package/dist/ai/classify.js +431 -0
  5. package/dist/ai/classify.js.map +1 -0
  6. package/dist/browser/extractPage.d.ts +41 -0
  7. package/dist/browser/extractPage.js +427 -0
  8. package/dist/browser/extractPage.js.map +1 -0
  9. package/dist/browser/openables.d.ts +19 -0
  10. package/dist/browser/openables.js +127 -0
  11. package/dist/browser/openables.js.map +1 -0
  12. package/dist/capture/page.d.ts +128 -0
  13. package/dist/capture/page.js +78 -0
  14. package/dist/capture/page.js.map +1 -0
  15. package/dist/citations.d.ts +80 -0
  16. package/dist/citations.js +197 -0
  17. package/dist/citations.js.map +1 -0
  18. package/dist/classificationView.d.ts +118 -0
  19. package/dist/classificationView.js +178 -0
  20. package/dist/classificationView.js.map +1 -0
  21. package/dist/cli/args.d.ts +3 -0
  22. package/dist/cli/args.js +38 -0
  23. package/dist/cli/args.js.map +1 -0
  24. package/dist/cli/history.d.ts +1 -0
  25. package/dist/cli/history.js +35 -0
  26. package/dist/cli/history.js.map +1 -0
  27. package/dist/cli/index.d.ts +5 -0
  28. package/dist/cli/index.js +172 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/cli/login.d.ts +29 -0
  31. package/dist/cli/login.js +179 -0
  32. package/dist/cli/login.js.map +1 -0
  33. package/dist/cli/scan.d.ts +11 -0
  34. package/dist/cli/scan.js +246 -0
  35. package/dist/cli/scan.js.map +1 -0
  36. package/dist/cli-ai/index.d.ts +7 -0
  37. package/dist/cli-ai/index.js +360 -0
  38. package/dist/cli-ai/index.js.map +1 -0
  39. package/dist/config.d.ts +112 -0
  40. package/dist/config.js +178 -0
  41. package/dist/config.js.map +1 -0
  42. package/dist/diff.d.ts +12 -0
  43. package/dist/diff.js +43 -0
  44. package/dist/diff.js.map +1 -0
  45. package/dist/explore.d.ts +59 -0
  46. package/dist/explore.js +119 -0
  47. package/dist/explore.js.map +1 -0
  48. package/dist/healingLink.d.ts +30 -0
  49. package/dist/healingLink.js +127 -0
  50. package/dist/healingLink.js.map +1 -0
  51. package/dist/history.d.ts +38 -0
  52. package/dist/history.js +120 -0
  53. package/dist/history.js.map +1 -0
  54. package/dist/htmlReport.d.ts +2 -0
  55. package/dist/htmlReport.js +371 -0
  56. package/dist/htmlReport.js.map +1 -0
  57. package/dist/index.d.ts +24 -0
  58. package/dist/index.js +65 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/mcp/sandbox.d.ts +9 -0
  61. package/dist/mcp/sandbox.js +82 -0
  62. package/dist/mcp/sandbox.js.map +1 -0
  63. package/dist/mcp/server.d.ts +120 -0
  64. package/dist/mcp/server.js +315 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/network.d.ts +60 -0
  67. package/dist/network.js +99 -0
  68. package/dist/network.js.map +1 -0
  69. package/dist/overview.d.ts +40 -0
  70. package/dist/overview.js +370 -0
  71. package/dist/overview.js.map +1 -0
  72. package/dist/overviewFile.d.ts +31 -0
  73. package/dist/overviewFile.js +101 -0
  74. package/dist/overviewFile.js.map +1 -0
  75. package/dist/scan.d.ts +159 -0
  76. package/dist/scan.js +402 -0
  77. package/dist/scan.js.map +1 -0
  78. package/dist/sharedCalls.d.ts +28 -0
  79. package/dist/sharedCalls.js +61 -0
  80. package/dist/sharedCalls.js.map +1 -0
  81. package/dist/taxonomy.d.ts +77 -0
  82. package/dist/taxonomy.js +153 -0
  83. package/dist/taxonomy.js.map +1 -0
  84. package/package.json +61 -0
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ /**
3
+ * The closed, versioned referentials F2 classifies against.
4
+ *
5
+ * Closed on purpose: a model that may invent a label produces a vocabulary that
6
+ * drifts between runs, and two scans of one app stop being comparable — which
7
+ * kills the diff this package is built around. The model picks from these lists
8
+ * or refuses; there is no third option.
9
+ *
10
+ * THE ADMISSION RULE, and it is what keeps these lists from bloating into
11
+ * uselessness:
12
+ *
13
+ * > An intent earns its place only if it changes **what you would test**.
14
+ *
15
+ * Two labels that lead to the same test plan are one label. This mirrors the
16
+ * rule `@ia-qa/self-healing`'s `check` uses to refuse new checks ("it must be
17
+ * able to fail for a reason the diff would not already catch") — without such a
18
+ * rule every plausible noun becomes a category and the classification stops
19
+ * discriminating anything.
20
+ *
21
+ * TWO AXES, NOT ONE. The original spec asks for "what intention, what data
22
+ * sensitivity" — those are different questions and collapsing them makes both
23
+ * wrong. `admin` is not an intent, it is an access dimension: an admin page can
24
+ * be data entry, configuration or navigation. Sensitivity is likewise
25
+ * orthogonal — a login form and a payment form are both high-sensitivity and
26
+ * need completely different tests.
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.UNKNOWN_INTENT = exports.SENSITIVITY_IDS = exports.INTENT_IDS = exports.SENSITIVITIES = exports.INTENTS = exports.TAXONOMY_VERSION = void 0;
30
+ exports.isIntent = isIntent;
31
+ exports.isSensitivity = isSensitivity;
32
+ exports.securityToolsFor = securityToolsFor;
33
+ exports.taxonomyForPrompt = taxonomyForPrompt;
34
+ exports.TAXONOMY_VERSION = 'qa-discovery-taxonomy@1';
35
+ /**
36
+ * What an entry point is FOR.
37
+ *
38
+ * `content` looks like the least useful entry and is one of the most: it is what
39
+ * lets F4 *deprioritise*. A terms page and a checkout page both being "a page"
40
+ * is how a test plan ends up spending its budget on the wrong half of an app.
41
+ */
42
+ exports.INTENTS = [
43
+ {
44
+ id: 'authentication',
45
+ means: 'Establishing or ending identity: sign in, sign up, sign out, password reset, MFA.',
46
+ testsDifferently: 'Session lifetime, invalid credentials, lockout, redirect-after-login, and what a logged-out user can still reach.',
47
+ },
48
+ {
49
+ id: 'payment',
50
+ means: 'Moving money: checkout, card or bank details, subscription, billing.',
51
+ testsDifferently: 'Amounts and currency, double submission and idempotency, a declined payment, and what is left behind when it fails halfway.',
52
+ },
53
+ {
54
+ id: 'data-entry',
55
+ means: 'Creating or editing a business object — a form that is not auth, payment or settings.',
56
+ testsDifferently: 'Field validation and boundaries, required fields, persistence after reload, and concurrent edits.',
57
+ },
58
+ {
59
+ id: 'search',
60
+ means: 'Querying, filtering or sorting a collection.',
61
+ testsDifferently: 'No results, one result, special characters and injection payloads, pagination edges, and result ordering.',
62
+ },
63
+ {
64
+ id: 'file-transfer',
65
+ means: 'Uploading or downloading a file.',
66
+ testsDifferently: 'Type and size limits, a malicious or malformed file, an interrupted transfer — none of which a text field ever needs.',
67
+ },
68
+ {
69
+ id: 'communication',
70
+ means: 'Sending something to a person: contact form, message, invitation, notification.',
71
+ testsDifferently: 'It has a side effect outside the app. Delivery, header/template injection, and what a retry sends twice.',
72
+ },
73
+ {
74
+ id: 'configuration',
75
+ means: 'Changing how the app or the account behaves: settings, preferences, feature toggles.',
76
+ testsDifferently: 'Defaults, persistence across sessions, and blast radius — a setting that changes what other pages do.',
77
+ },
78
+ {
79
+ id: 'navigation',
80
+ means: 'Getting somewhere else: menus, dashboards, index and listing pages.',
81
+ testsDifferently: 'Link integrity and permission gates — whether every route it offers actually resolves, and for whom.',
82
+ },
83
+ {
84
+ id: 'content',
85
+ means: 'Reading: terms, policies, documentation, marketing copy.',
86
+ testsDifferently: 'Almost nothing behavioural — presence, links, accessibility. Its value is that it tells F4 what NOT to spend budget on.',
87
+ },
88
+ ];
89
+ /** What an entry point HANDLES. Orthogonal to intent, and it drives risk, not test shape. */
90
+ exports.SENSITIVITIES = [
91
+ { id: 'none', means: 'Nothing personal or secret — a search box, a filter, a page of docs.', raises: 'Nothing.' },
92
+ {
93
+ id: 'personal',
94
+ means: 'Identifies a person: name, email, phone, address, account identifier.',
95
+ raises: 'Exposure and retention. A leak here is a privacy incident, not just a bug.',
96
+ },
97
+ {
98
+ id: 'credential',
99
+ means: 'Grants access: password, token, API key, session material.',
100
+ raises: 'A leak is an intrusion. Highest priority regardless of how simple the form looks.',
101
+ },
102
+ {
103
+ id: 'financial',
104
+ means: 'Money or the means to move it: card, IBAN, amount, invoice.',
105
+ raises: 'Direct loss, and a compliance surface (PCI) that changes what may even be stored.',
106
+ },
107
+ ];
108
+ exports.INTENT_IDS = exports.INTENTS.map((i) => i.id);
109
+ exports.SENSITIVITY_IDS = exports.SENSITIVITIES.map((s) => s.id);
110
+ /**
111
+ * The refusal. Guardrail 5 made concrete: a model asked to classify will always
112
+ * classify, so "I cannot tell from this evidence" has to be a value it can
113
+ * return, not a state it has to invent a label to escape.
114
+ */
115
+ exports.UNKNOWN_INTENT = 'unknown';
116
+ function isIntent(value) {
117
+ return typeof value === 'string' && (exports.INTENT_IDS.includes(value) || value === exports.UNKNOWN_INTENT);
118
+ }
119
+ function isSensitivity(value) {
120
+ return typeof value === 'string' && exports.SENSITIVITY_IDS.includes(value);
121
+ }
122
+ function securityToolsFor(intent, sensitivity) {
123
+ const out = [];
124
+ const add = (tool, why) => {
125
+ if (!out.some((t) => t.tool === tool))
126
+ out.push({ tool, why });
127
+ };
128
+ if (intent === 'authentication' || sensitivity === 'credential') {
129
+ add('cookie_security_audit', 'Session material is handled here — HttpOnly, Secure and SameSite decide whether it can be stolen.');
130
+ add('security_headers_check', 'An auth surface is the one worth clickjacking or framing.');
131
+ add('secret_scan', 'A credential surface is where a key most often ends up hard-coded.');
132
+ }
133
+ if (intent === 'payment' || sensitivity === 'financial') {
134
+ add('ssl_certificate_check', 'Money in transit — an expiring or weak certificate is the failure CI exists to catch early.');
135
+ add('security_headers_check', 'A payment page is the highest-value target for injection and framing.');
136
+ }
137
+ if (sensitivity === 'personal') {
138
+ add('security_headers_check', 'Personal data on the page raises the cost of an injection or a leak through a third party.');
139
+ }
140
+ return out;
141
+ }
142
+ /** Everything a prompt needs to state the closed vocabulary, without duplicating it. */
143
+ function taxonomyForPrompt() {
144
+ const lines = [`Taxonomy ${exports.TAXONOMY_VERSION}. Choose from these ids only.`, '', 'INTENT — what the entry point is for:'];
145
+ for (const i of exports.INTENTS)
146
+ lines.push(` ${i.id}: ${i.means}`);
147
+ lines.push(` ${exports.UNKNOWN_INTENT}: the evidence does not support any of the above. Prefer this over guessing.`);
148
+ lines.push('', 'DATA SENSITIVITY — what it handles:');
149
+ for (const s of exports.SENSITIVITIES)
150
+ lines.push(` ${s.id}: ${s.means}`);
151
+ return lines.join('\n');
152
+ }
153
+ //# sourceMappingURL=taxonomy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taxonomy.js","sourceRoot":"","sources":["../src/taxonomy.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@ia-qa/qa-discovery",
3
+ "version": "0.1.0",
4
+ "description": "Deterministic surface reconnaissance for an unknown web app — crawl, capture, diff. No LLM, no hosting: runs entirely on your machine, with a local MCP server for agents.",
5
+ "keywords": [
6
+ "qa",
7
+ "testing",
8
+ "test-discovery",
9
+ "crawler",
10
+ "playwright",
11
+ "mcp",
12
+ "model-context-protocol"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Jean-Christophe Jamet",
16
+ "homepage": "https://www.ia-qa.com",
17
+ "type": "commonjs",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ },
25
+ "./dist/*": [
26
+ "./dist/*",
27
+ "./dist/*.js"
28
+ ],
29
+ "./package.json": "./package.json"
30
+ },
31
+ "bin": {
32
+ "ia-qa-discover": "dist/cli/index.js",
33
+ "ia-qa-discover-mcp": "dist/mcp/server.js",
34
+ "ia-qa-discover-ai": "dist/cli-ai/index.js"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md",
39
+ "ROADMAP.md"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.json",
43
+ "dev": "tsc -p tsconfig.json --watch",
44
+ "start:mcp": "node dist/mcp/server.js",
45
+ "prepublishOnly": "npm run build"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "dependencies": {
51
+ "@ia-qa/self-healing": "^1.7.0"
52
+ },
53
+ "peerDependencies": {
54
+ "playwright": ">=1.40.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^20.14.0",
58
+ "playwright": "^1.45.0",
59
+ "typescript": "^5.5.0"
60
+ }
61
+ }