@dataparade/cli 0.0.3 → 0.0.4
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 +24 -0
- package/dist/package.json +85 -0
- package/dist/src/ai-enrichment/agent-orchestrator.d.ts +2 -0
- package/dist/src/ai-enrichment/agent-orchestrator.js +8 -5
- package/dist/src/ai-enrichment/providers/hosted-worker-infer-provider.d.ts +10 -0
- package/dist/src/ai-enrichment/providers/hosted-worker-infer-provider.js +47 -0
- package/dist/src/cli.js +85 -1
- package/dist/src/config/env.js +2 -0
- package/dist/src/config/resolve.js +3 -0
- package/dist/src/config/scan-env.d.ts +1 -0
- package/dist/src/config/scan-env.js +5 -0
- package/dist/src/config/types.d.ts +1 -0
- package/dist/src/config/upload-env.d.ts +1 -0
- package/dist/src/config/upload-env.js +10 -0
- package/dist/src/config/validate-scan-ai.d.ts +1 -1
- package/dist/src/config/validate-scan-ai.js +5 -0
- package/dist/src/core/pipeline/ai-orchestrator-options.js +5 -0
- package/dist/src/core/schema/scan-config.schema.d.ts +2 -0
- package/dist/src/core/schema/scan-config.schema.js +2 -1
- package/dist/src/core/types/config.d.ts +3 -1
- package/dist/src/observability/scan-sentry.d.ts +1 -1
- package/dist/src/platform-api/dataparade-app-base-url.d.ts +7 -0
- package/dist/src/platform-api/dataparade-app-base-url.js +14 -0
- package/dist/src/platform-api/upload-client.d.ts +3 -0
- package/dist/src/platform-api/upload-client.js +38 -0
- package/dist/src/platform-api/upload.types.d.ts +10 -0
- package/dist/src/platform-api/upload.types.js +2 -0
- package/dist/src/upload/build-preview-url.d.ts +1 -0
- package/dist/src/upload/build-preview-url.js +10 -0
- package/dist/src/upload/run-upload.d.ts +2 -0
- package/dist/src/upload/run-upload.js +18 -0
- package/dist/src/upload/types.d.ts +11 -0
- package/dist/src/upload/types.js +2 -0
- package/dist/tests/unit/cli/scan-quota-flow.spec.js +2 -0
- package/dist/tests/unit/config/upload-env.spec.d.ts +1 -0
- package/dist/tests/unit/config/upload-env.spec.js +12 -0
- package/dist/tests/unit/config/validate-scan-ai.spec.js +8 -0
- package/dist/tests/unit/platform-api/dataparade-app-base-url.spec.d.ts +1 -0
- package/dist/tests/unit/platform-api/dataparade-app-base-url.spec.js +13 -0
- package/dist/tests/unit/platform-api/upload-client.spec.d.ts +1 -0
- package/dist/tests/unit/platform-api/upload-client.spec.js +40 -0
- package/dist/tests/unit/publish-manifest.spec.d.ts +1 -0
- package/dist/tests/unit/publish-manifest.spec.js +18 -0
- package/dist/tests/unit/upload/build-preview-url.spec.d.ts +1 -0
- package/dist/tests/unit/upload/build-preview-url.spec.js +19 -0
- package/package.json +4 -3
- package/patterns/README.md +322 -0
- package/patterns/actor.patterns.yaml +51 -0
- package/patterns/aws-terraform-catalog.snapshot.json +1760 -0
- package/patterns/aws-terraform-service-hints.generated.json +1446 -0
- package/patterns/azure-terraform-catalog.snapshot.json +1195 -0
- package/patterns/azure-terraform-service-hints.generated.json +864 -0
- package/patterns/classifier/actors.classifier.yaml +17 -0
- package/patterns/classifier/components.classifier.yaml +110 -0
- package/patterns/classifier/third-party.classifier.yaml +169 -0
- package/patterns/kubernetes-terraform-catalog.snapshot.json +94 -0
- package/patterns/kubernetes-terraform-service-hints.generated.json +258 -0
- package/patterns/non-pii-signals.rules.yaml +84 -0
- package/patterns/pii-signals.rules.yaml +115 -0
- package/patterns/property.patterns.yaml +431 -0
- package/patterns/provider-topology.rules.yaml +765 -0
- package/patterns/python.md +211 -0
- package/patterns/python.patterns.yaml +160 -0
- package/patterns/terraform.md +37 -0
- package/patterns/terraform.patterns.yaml +495 -0
- package/patterns/third-party.patterns.yaml +167 -0
- package/patterns/typescript.patterns.yaml +139 -0
|
@@ -27,10 +27,12 @@ describe("scan quota flow", () => {
|
|
|
27
27
|
afterEach(() => {
|
|
28
28
|
fs_1.default.rmSync(tempRoot, { recursive: true, force: true });
|
|
29
29
|
delete process.env.DATAPARADE_WORKSPACE_API_KEY;
|
|
30
|
+
delete process.env.DATAPARADE_SKIP_AUTO_UPLOAD;
|
|
30
31
|
process.exitCode = 0;
|
|
31
32
|
});
|
|
32
33
|
it("skips quota API when workspace key is present but AI is disabled", async () => {
|
|
33
34
|
process.env.DATAPARADE_WORKSPACE_API_KEY = "dp_live_test";
|
|
35
|
+
process.env.DATAPARADE_SKIP_AUTO_UPLOAD = "true";
|
|
34
36
|
await (0, cli_1.run)(["node", "cli", "scan", tempRoot]);
|
|
35
37
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
36
38
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const upload_env_1 = require("../../../src/config/upload-env");
|
|
4
|
+
describe("resolveSkipAutoUpload", () => {
|
|
5
|
+
it("is false when unset", () => {
|
|
6
|
+
expect((0, upload_env_1.resolveSkipAutoUpload)({})).toBe(false);
|
|
7
|
+
});
|
|
8
|
+
it("is true for truthy env values", () => {
|
|
9
|
+
expect((0, upload_env_1.resolveSkipAutoUpload)({ DATAPARADE_SKIP_AUTO_UPLOAD: "true" })).toBe(true);
|
|
10
|
+
expect((0, upload_env_1.resolveSkipAutoUpload)({ DATAPARADE_SKIP_AUTO_UPLOAD: "1" })).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -39,6 +39,14 @@ describe("validate-scan-ai", () => {
|
|
|
39
39
|
expect((0, validate_scan_ai_1.validateAiInferenceCredentials)(config)).toEqual([]);
|
|
40
40
|
expect((0, validate_scan_ai_1.resolveAiMode)(config)).toBe("platform");
|
|
41
41
|
});
|
|
42
|
+
it("accepts hosted worker infer proxy without BYOK or workspace key", () => {
|
|
43
|
+
const config = baseConfig({
|
|
44
|
+
enableAiInference: true,
|
|
45
|
+
hostedInferProxyUrl: "http://127.0.0.1:4567/infer",
|
|
46
|
+
});
|
|
47
|
+
expect((0, validate_scan_ai_1.validateAiInferenceCredentials)(config)).toEqual([]);
|
|
48
|
+
expect((0, validate_scan_ai_1.resolveAiMode)(config)).toBe("hosted_worker");
|
|
49
|
+
});
|
|
42
50
|
it("allows workspace key with AI disabled", () => {
|
|
43
51
|
const errors = (0, validate_scan_ai_1.validateAiInferenceCredentials)(baseConfig({
|
|
44
52
|
enableAiInference: false,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const dataparade_app_base_url_1 = require("../../../src/platform-api/dataparade-app-base-url");
|
|
4
|
+
describe("getDataparadeAppBaseUrl", () => {
|
|
5
|
+
it("defaults to production frontend URL", () => {
|
|
6
|
+
expect((0, dataparade_app_base_url_1.getDataparadeAppBaseUrl)({})).toBe(dataparade_app_base_url_1.DEFAULT_DATAPARADE_APP_URL);
|
|
7
|
+
});
|
|
8
|
+
it("strips trailing slash from DATAPARADE_APP_URL", () => {
|
|
9
|
+
expect((0, dataparade_app_base_url_1.getDataparadeAppBaseUrl)({
|
|
10
|
+
DATAPARADE_APP_URL: "http://localhost:3001/",
|
|
11
|
+
})).toBe("http://localhost:3001");
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const upload_client_1 = require("../../../src/platform-api/upload-client");
|
|
4
|
+
describe("cliUploadPreview", () => {
|
|
5
|
+
const originalFetch = global.fetch;
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
global.fetch = originalFetch;
|
|
8
|
+
jest.restoreAllMocks();
|
|
9
|
+
});
|
|
10
|
+
it("posts dataflow to upload endpoint", async () => {
|
|
11
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
12
|
+
ok: true,
|
|
13
|
+
json: async () => ({ draftId: "draft-1", projectName: "proj" }),
|
|
14
|
+
});
|
|
15
|
+
const result = await (0, upload_client_1.cliUploadPreview)({
|
|
16
|
+
apiKey: "dp_live_testkey123456",
|
|
17
|
+
dataflow: { schemaVersion: "1.0", graph: { nodes: [], edges: [] } },
|
|
18
|
+
projectName: "proj",
|
|
19
|
+
scanJobId: "job-1",
|
|
20
|
+
});
|
|
21
|
+
expect(result.draftId).toBe("draft-1");
|
|
22
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.stringContaining("/api/scans/cli/upload"), expect.objectContaining({
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: expect.objectContaining({
|
|
25
|
+
Authorization: "Bearer dp_live_testkey123456",
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
});
|
|
29
|
+
it("throws with API error message", async () => {
|
|
30
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
31
|
+
ok: false,
|
|
32
|
+
status: 400,
|
|
33
|
+
json: async () => ({ message: "Invalid dataflow" }),
|
|
34
|
+
});
|
|
35
|
+
await expect((0, upload_client_1.cliUploadPreview)({
|
|
36
|
+
apiKey: "dp_live_testkey123456",
|
|
37
|
+
dataflow: {},
|
|
38
|
+
})).rejects.toThrow("Invalid dataflow");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
describe("npm publish manifest", () => {
|
|
9
|
+
const pkgPath = path_1.default.resolve(__dirname, "../../package.json");
|
|
10
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, "utf8"));
|
|
11
|
+
it("lists typescript as a runtime dependency (required by TS analyzer)", () => {
|
|
12
|
+
expect(pkg.dependencies?.typescript).toBeDefined();
|
|
13
|
+
expect(pkg.devDependencies?.typescript).toBeUndefined();
|
|
14
|
+
});
|
|
15
|
+
it("includes patterns/ in published files (runtime YAML configs)", () => {
|
|
16
|
+
expect(pkg.files).toContain("patterns");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const build_preview_url_1 = require("../../../src/upload/build-preview-url");
|
|
4
|
+
describe("buildImportPreviewUrl", () => {
|
|
5
|
+
const original = process.env.DATAPARADE_APP_URL;
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
if (original === undefined) {
|
|
8
|
+
delete process.env.DATAPARADE_APP_URL;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
process.env.DATAPARADE_APP_URL = original;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
it("builds dashboard deep link with importDraft query param", () => {
|
|
15
|
+
process.env.DATAPARADE_APP_URL = "http://localhost:3001";
|
|
16
|
+
const url = (0, build_preview_url_1.buildImportPreviewUrl)("draft-abc");
|
|
17
|
+
expect(url).toBe("http://localhost:3001/dashboard?importDraft=draft-abc");
|
|
18
|
+
});
|
|
19
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dataparade/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "dataPARADE CLI — scan codebases for privacy and security data flows",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -58,9 +58,10 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@sentry/node": "^10.38.0",
|
|
61
|
+
"commander": "^12.1.0",
|
|
61
62
|
"dotenv": "^16.4.7",
|
|
62
63
|
"langsmith": "^0.3.14",
|
|
63
|
-
"
|
|
64
|
+
"typescript": "^5.7.3",
|
|
64
65
|
"yaml": "^2.8.2",
|
|
65
66
|
"zod": "^4.3.5"
|
|
66
67
|
},
|
|
@@ -72,11 +73,11 @@
|
|
|
72
73
|
"globals": "^15.14.0",
|
|
73
74
|
"jest": "^29.7.0",
|
|
74
75
|
"ts-jest": "^29.2.5",
|
|
75
|
-
"typescript": "^5.7.3",
|
|
76
76
|
"typescript-eslint": "^8.20.0"
|
|
77
77
|
},
|
|
78
78
|
"files": [
|
|
79
79
|
"dist",
|
|
80
|
+
"patterns",
|
|
80
81
|
"LICENSE",
|
|
81
82
|
"README.md",
|
|
82
83
|
"package.json"
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# TypeScript / JavaScript Patterns
|
|
2
|
+
|
|
3
|
+
This document describes the TypeScript/JavaScript patterns implemented by the
|
|
4
|
+
CLI TS/JS analyzer for **DP-P0-CLI-104**. These patterns are heuristic and not
|
|
5
|
+
exhaustive; they are designed to provide useful structural signals for the
|
|
6
|
+
classifier and data-flow detector.
|
|
7
|
+
|
|
8
|
+
Patterns are configured in two layers:
|
|
9
|
+
|
|
10
|
+
- A **YAML configuration file** at `cli/patterns/typescript.patterns.yaml`
|
|
11
|
+
which defines pattern IDs, known libraries/SDKs, regexes, and confidences.
|
|
12
|
+
- A **TypeScript execution engine** (`cli/src/analyzers/typescript/typescript-detection.ts`
|
|
13
|
+
and `cli/src/analyzers/typescript/typescript-detection-config.ts`) that loads the YAML,
|
|
14
|
+
validates it, compiles regexes, and applies them to `FileInfo` plus the TS/JS
|
|
15
|
+
code model.
|
|
16
|
+
|
|
17
|
+
Each detected match produces a `RawFinding` object using the shared `PatternId`
|
|
18
|
+
union from `cli/src/core/types/detection.ts`. The YAML file controls **what**
|
|
19
|
+
we look for (libraries, regexes, keys); the TypeScript engine controls **how**
|
|
20
|
+
those patterns are applied.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## `express_route`
|
|
25
|
+
|
|
26
|
+
**Purpose:** Detect HTTP route handlers in common web frameworks.
|
|
27
|
+
|
|
28
|
+
**Frameworks covered (heuristic):**
|
|
29
|
+
|
|
30
|
+
- Express (imports from `"express"`)
|
|
31
|
+
- NestJS (imports from `"@nestjs/common"` / `"@nestjs/core"`)
|
|
32
|
+
- Next.js / React route handlers (file-path based heuristic such as
|
|
33
|
+
`pages/api/*`, `app/*`, `*route.ts`, `*route.js`)
|
|
34
|
+
|
|
35
|
+
**Examples:**
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// Express
|
|
39
|
+
import express from "express";
|
|
40
|
+
|
|
41
|
+
const app = express();
|
|
42
|
+
app.get("/users", (req, res) => {
|
|
43
|
+
res.send("ok");
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// NestJS
|
|
49
|
+
import { Controller, Get } from "@nestjs/common";
|
|
50
|
+
|
|
51
|
+
@Controller("users")
|
|
52
|
+
export class UsersController {
|
|
53
|
+
@Get("/")
|
|
54
|
+
findAll() {}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// Next.js API route (pages/api/users.ts)
|
|
60
|
+
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
61
|
+
res.status(200).json({ ok: true });
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Properties set on `RawFinding.properties`:**
|
|
66
|
+
|
|
67
|
+
- `framework: "express" | "nest" | "next_or_react_route"`
|
|
68
|
+
- `httpMethods: string[]` – e.g. `["GET"]` when a method can be inferred
|
|
69
|
+
- `path?: string` – route path when it can be parsed (e.g. `"/users"`)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## `database_connection`
|
|
74
|
+
|
|
75
|
+
**Purpose:** Detect creation or usage of database and cache clients.
|
|
76
|
+
|
|
77
|
+
**Libraries covered (heuristic):**
|
|
78
|
+
|
|
79
|
+
- `pg` (`new Pool(...)`, `new Client(...)`)
|
|
80
|
+
- `mysql2` (`createConnection(...)`)
|
|
81
|
+
- Prisma (`new PrismaClient(...)`)
|
|
82
|
+
- Mongoose (`mongoose.connect(...)`)
|
|
83
|
+
- Redis (`createClient(...)`)
|
|
84
|
+
- Supabase (`createClient(...)` from `@supabase/supabase-js`)
|
|
85
|
+
|
|
86
|
+
**Examples:**
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { Pool } from "pg";
|
|
90
|
+
|
|
91
|
+
const pool = new Pool({
|
|
92
|
+
connectionString: process.env.DATABASE_URL,
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { PrismaClient } from "@prisma/client";
|
|
98
|
+
|
|
99
|
+
const prisma = new PrismaClient();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import mongoose from "mongoose";
|
|
104
|
+
|
|
105
|
+
mongoose.connect(process.env.MONGO_URL!);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Properties set on `RawFinding.properties`:**
|
|
109
|
+
|
|
110
|
+
- `client: string` – e.g. `"pg"`, `"mysql2"`, `"prisma"`, `"mongoose"`, `"redis"`, `"supabase"`
|
|
111
|
+
- `databaseType: string` – e.g. `"postgres"`, `"mysql"`, `"mongo"`, `"redis"`, `"unknown"`
|
|
112
|
+
- For heuristic SQL keyword matches, an additional hint:
|
|
113
|
+
- `hint: "raw_sql_keyword"`
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## `external_api_call`
|
|
118
|
+
|
|
119
|
+
**Purpose:** Detect calls to external HTTP APIs or third-party SDKs.
|
|
120
|
+
|
|
121
|
+
**HTTP clients covered (heuristic):**
|
|
122
|
+
|
|
123
|
+
- Global `fetch(...)`
|
|
124
|
+
- `axios.get/post/put/delete/patch(...)`
|
|
125
|
+
- `got(...)`
|
|
126
|
+
|
|
127
|
+
**Known SDK/service hints:**
|
|
128
|
+
|
|
129
|
+
- Stripe (`"stripe"`)
|
|
130
|
+
- SendGrid (`"sendgrid"`)
|
|
131
|
+
- Twilio (`"twilio"`)
|
|
132
|
+
- OpenAI (`"openai"`)
|
|
133
|
+
- AWS SDK (`"@aws-sdk/..."`)
|
|
134
|
+
- Firebase (`"firebase"`)
|
|
135
|
+
|
|
136
|
+
**Examples:**
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// Generic HTTP call
|
|
140
|
+
const res = await fetch("https://api.example.com/users");
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// Axios with method
|
|
145
|
+
import axios from "axios";
|
|
146
|
+
|
|
147
|
+
await axios.post("https://api.example.com/users", { name: "Alice" });
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// Stripe SDK import
|
|
152
|
+
import Stripe from "stripe";
|
|
153
|
+
|
|
154
|
+
const stripe = new Stripe(process.env.STRIPE_KEY!, { apiVersion: "2022-11-15" });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Properties set on `RawFinding.properties`:**
|
|
158
|
+
|
|
159
|
+
- For direct HTTP calls:
|
|
160
|
+
- `client: string` – e.g. `"fetch"`, `"axios.get"`, `"axios.post"`, `"got"`
|
|
161
|
+
- `url: string` – full URL if present in the call
|
|
162
|
+
- `httpMethod?: string` – uppercase method when derivable
|
|
163
|
+
- For SDK / service hints:
|
|
164
|
+
- `client: string` – service/client identifier, e.g. `"stripe"`
|
|
165
|
+
- `serviceName: string` – same as `client` for now
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## `auth_middleware`
|
|
170
|
+
|
|
171
|
+
**Purpose:** Detect authentication/authorization-related middleware and guards.
|
|
172
|
+
|
|
173
|
+
**Libraries and patterns covered (heuristic):**
|
|
174
|
+
|
|
175
|
+
- Passport (`passport.authenticate(...)`, imports from `"passport"` or modules containing `"passport"`)
|
|
176
|
+
- JSON Web Tokens (`jsonwebtoken`, `jwt.sign(...)`, `jwt.verify(...)`)
|
|
177
|
+
- NestJS auth guards (`@nestjs/passport`, `UseGuards(...)`)
|
|
178
|
+
|
|
179
|
+
**Examples:**
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import passport from "passport";
|
|
183
|
+
|
|
184
|
+
app.get(
|
|
185
|
+
"/profile",
|
|
186
|
+
passport.authenticate("jwt", { session: false }),
|
|
187
|
+
(req, res) => res.send("ok"),
|
|
188
|
+
);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import jwt from "jsonwebtoken";
|
|
193
|
+
|
|
194
|
+
const token = jwt.sign({ sub: user.id }, process.env.JWT_SECRET!);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { UseGuards } from "@nestjs/common";
|
|
199
|
+
import { AuthGuard } from "@nestjs/passport";
|
|
200
|
+
|
|
201
|
+
@UseGuards(AuthGuard("jwt"))
|
|
202
|
+
@Get("me")
|
|
203
|
+
getProfile() {}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Properties set on `RawFinding.properties`:**
|
|
207
|
+
|
|
208
|
+
- For Passport:
|
|
209
|
+
- `library: "passport"`
|
|
210
|
+
- `strategy?: string` – strategy name when parsed from `passport.authenticate("strategy", ...)`
|
|
211
|
+
- For JWT:
|
|
212
|
+
- `library: "jsonwebtoken"`
|
|
213
|
+
- For NestJS guards:
|
|
214
|
+
- `library: "nestjs_auth"`
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## `env_variable`
|
|
219
|
+
|
|
220
|
+
**Purpose:** Detect environment variable usage in TS/JS code.
|
|
221
|
+
|
|
222
|
+
**Pattern:** `process.env.<KEY>`
|
|
223
|
+
|
|
224
|
+
**Examples:**
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
228
|
+
const apiKey = process.env.API_KEY;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Properties set on `RawFinding.properties`:**
|
|
232
|
+
|
|
233
|
+
- `key: string` – the environment variable key, e.g. `"DATABASE_URL"`, `"API_KEY"`
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## `config_file`
|
|
238
|
+
|
|
239
|
+
**Purpose:** Detect access to configuration objects in TS/JS code that likely
|
|
240
|
+
point to configuration or secret values (for later mapping to assets or
|
|
241
|
+
environment configuration).
|
|
242
|
+
|
|
243
|
+
**Pattern:** `config.<field>` where `<field>` is one of:
|
|
244
|
+
|
|
245
|
+
- `database`
|
|
246
|
+
- `db`
|
|
247
|
+
- `apiKey`
|
|
248
|
+
- `api_key`
|
|
249
|
+
- `auth`
|
|
250
|
+
- `redis`
|
|
251
|
+
|
|
252
|
+
**Examples:**
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import config from "./config";
|
|
256
|
+
|
|
257
|
+
const dbConfig = config.database;
|
|
258
|
+
const redisConfig = config.redis;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Properties set on `RawFinding.properties`:**
|
|
262
|
+
|
|
263
|
+
- `key: string` – the accessed config key, e.g. `"database"`, `"apiKey"`
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Actor detection (`web_actor`, `service_actor`)
|
|
268
|
+
|
|
269
|
+
**Purpose:** Detect human and system actors so the classifier can emit
|
|
270
|
+
`DetectedComponent` entries with `type: "actor"` alongside assets and third
|
|
271
|
+
parties.
|
|
272
|
+
|
|
273
|
+
Actor detection is configured in a shared YAML file:
|
|
274
|
+
|
|
275
|
+
-- `cli/patterns/actor.patterns.yaml` – defines regexes and rules for emitting
|
|
276
|
+
`web_actor` and `service_actor` findings, and attaches basic properties such
|
|
277
|
+
as `actorType`, `roleNames`, and `sourceContext`.
|
|
278
|
+
|
|
279
|
+
The classifier maps these pattern IDs to actors via `classifier/components.classifier.yaml`:
|
|
280
|
+
|
|
281
|
+
- `web_actor` → `type: actor`, `subType: customer`
|
|
282
|
+
- `service_actor` → `type: actor`, `subType: employee`
|
|
283
|
+
|
|
284
|
+
### Signals used by the TS/JS analyzer
|
|
285
|
+
|
|
286
|
+
The TypeScript/JavaScript analyzer loads `actor.patterns.yaml` through
|
|
287
|
+
`cli/src/config/actor-detection-config.ts` and applies rules in
|
|
288
|
+
`cli/src/analyzers/typescript/actor-detection.ts`. Current heuristics include:
|
|
289
|
+
|
|
290
|
+
- **Frontend session hook**
|
|
291
|
+
- File path matches `hooks/useSession.ts` / `hooks/useSession.tsx`.
|
|
292
|
+
- Emits a `web_actor` finding named `"Customer"` with:
|
|
293
|
+
- `properties.actorType = "customer"`
|
|
294
|
+
- `properties.sourceContext = "frontend_session"`.
|
|
295
|
+
|
|
296
|
+
- **Backend controllers using `req.user`**
|
|
297
|
+
- Content contains `req.user` (e.g. NestJS/Express controllers).
|
|
298
|
+
- Emits a `web_actor` finding named `"Customer"` with:
|
|
299
|
+
- `properties.actorType = "customer"`
|
|
300
|
+
- `properties.sourceContext = "backend_controller"`.
|
|
301
|
+
|
|
302
|
+
- **Backend admin role checks**
|
|
303
|
+
- Content contains simple admin role checks (e.g. `isAdmin`, `role: "admin"`).
|
|
304
|
+
- Emits a `service_actor` finding named `"Admin user"` with:
|
|
305
|
+
- `properties.actorType = "employee"`
|
|
306
|
+
- `properties.roleNames = ["admin"]`
|
|
307
|
+
- `properties.sourceContext = "backend_role_check"`.
|
|
308
|
+
|
|
309
|
+
Other language analyzers can reuse the same `web_actor` / `service_actor`
|
|
310
|
+
pattern IDs by loading `actor.patterns.yaml` and emitting compatible
|
|
311
|
+
`RawFinding`s; the classifier does not need to change for each language.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
These patterns are intentionally conservative and heuristic. They are meant to
|
|
316
|
+
provide a good baseline for:
|
|
317
|
+
|
|
318
|
+
- Component classification (assets, third parties, etc.).
|
|
319
|
+
- Structural data-flow detection (who talks to which DB / third-party).
|
|
320
|
+
|
|
321
|
+
Additional patterns and refinements can be added in future tasks and should be
|
|
322
|
+
documented by extending this file.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Shared actor-detection config: regexes and rules to emit actor-related findings.
|
|
3
|
+
# This is language-agnostic; analyzers (TypeScript, Python, etc.) can all load it.
|
|
4
|
+
#
|
|
5
|
+
# The classifier maps patternIds like web_actor / service_actor → type: actor and
|
|
6
|
+
# coarse subTypes (e.g. customer, employee) via classifier/components.classifier.yaml.
|
|
7
|
+
# This file focuses on *where* we see actors in code and which properties to attach.
|
|
8
|
+
|
|
9
|
+
regexes:
|
|
10
|
+
# Frontend: Next.js/React session hook file
|
|
11
|
+
ts_frontend_session_hook_file: "hooks/useSession\\.tsx?$"
|
|
12
|
+
|
|
13
|
+
# Backend: NestJS / Express controllers using req.user
|
|
14
|
+
ts_backend_req_user: "\\breq\\.user\\b"
|
|
15
|
+
|
|
16
|
+
# Backend: simple admin role checks
|
|
17
|
+
ts_role_admin: "\\b(isAdmin|is_admin)\\b|\\brole[s]?\\s*[:=]\\s*['\"]admin['\"]"
|
|
18
|
+
|
|
19
|
+
rules:
|
|
20
|
+
# Frontend web customer: useSession hook representing logged-in customer user
|
|
21
|
+
- id: web_actor_frontend_session
|
|
22
|
+
patternId: web_actor
|
|
23
|
+
name: Customer
|
|
24
|
+
file_path_regex: ts_frontend_session_hook_file
|
|
25
|
+
confidence: 0.9
|
|
26
|
+
properties:
|
|
27
|
+
actorType: customer
|
|
28
|
+
sourceContext: frontend_session
|
|
29
|
+
|
|
30
|
+
# Backend web customer: controllers using req.user (authenticated user context)
|
|
31
|
+
- id: web_actor_backend_req_user
|
|
32
|
+
patternId: web_actor
|
|
33
|
+
name: Customer
|
|
34
|
+
content_regex: ts_backend_req_user
|
|
35
|
+
confidence: 0.85
|
|
36
|
+
properties:
|
|
37
|
+
actorType: customer
|
|
38
|
+
sourceContext: backend_controller
|
|
39
|
+
|
|
40
|
+
# Backend admin employee: admin checks around user/role handling
|
|
41
|
+
- id: service_actor_admin_employee
|
|
42
|
+
patternId: service_actor
|
|
43
|
+
name: Admin user
|
|
44
|
+
content_regex: ts_role_admin
|
|
45
|
+
confidence: 0.8
|
|
46
|
+
properties:
|
|
47
|
+
actorType: employee
|
|
48
|
+
roleNames:
|
|
49
|
+
- admin
|
|
50
|
+
sourceContext: backend_role_check
|
|
51
|
+
|