n8n-nodes-fortisiem 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 (29) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/dist/credentials/FortiSiemApi.credentials.d.ts +10 -0
  4. package/dist/credentials/FortiSiemApi.credentials.d.ts.map +1 -0
  5. package/dist/credentials/FortiSiemApi.credentials.js +67 -0
  6. package/dist/credentials/FortiSiemApi.credentials.js.map +1 -0
  7. package/dist/icons/fortisiem.png +0 -0
  8. package/dist/nodes/FortiSiem/FortiSiem.node.d.ts +6 -0
  9. package/dist/nodes/FortiSiem/FortiSiem.node.d.ts.map +1 -0
  10. package/dist/nodes/FortiSiem/FortiSiem.node.js +284 -0
  11. package/dist/nodes/FortiSiem/FortiSiem.node.js.map +1 -0
  12. package/dist/nodes/FortiSiem/descriptions/EventDescription.d.ts +4 -0
  13. package/dist/nodes/FortiSiem/descriptions/EventDescription.d.ts.map +1 -0
  14. package/dist/nodes/FortiSiem/descriptions/EventDescription.js +68 -0
  15. package/dist/nodes/FortiSiem/descriptions/EventDescription.js.map +1 -0
  16. package/dist/nodes/FortiSiem/descriptions/IncidentDescription.d.ts +4 -0
  17. package/dist/nodes/FortiSiem/descriptions/IncidentDescription.d.ts.map +1 -0
  18. package/dist/nodes/FortiSiem/descriptions/IncidentDescription.js +275 -0
  19. package/dist/nodes/FortiSiem/descriptions/IncidentDescription.js.map +1 -0
  20. package/dist/nodes/FortiSiem/descriptions/OrganizationDescription.d.ts +4 -0
  21. package/dist/nodes/FortiSiem/descriptions/OrganizationDescription.d.ts.map +1 -0
  22. package/dist/nodes/FortiSiem/descriptions/OrganizationDescription.js +60 -0
  23. package/dist/nodes/FortiSiem/descriptions/OrganizationDescription.js.map +1 -0
  24. package/dist/nodes/FortiSiem/descriptions/WatchlistDescription.d.ts +4 -0
  25. package/dist/nodes/FortiSiem/descriptions/WatchlistDescription.d.ts.map +1 -0
  26. package/dist/nodes/FortiSiem/descriptions/WatchlistDescription.js +106 -0
  27. package/dist/nodes/FortiSiem/descriptions/WatchlistDescription.js.map +1 -0
  28. package/icons/fortisiem.png +0 -0
  29. package/package.json +57 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kboykov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # n8n-nodes-fortisiem
2
+
3
+ An [n8n](https://n8n.io/) community node for [FortiSIEM](https://www.fortinet.com/products/siem/fortisiem) — Fortinet's security information and event management (SIEM) platform for real-time monitoring, analytics, and incident management.
4
+
5
+ This node lets you interact with the FortiSIEM REST API directly from your n8n workflows: fetch and update incidents, retrieve triggering events, run analytic event queries, manage monitored organizations, and maintain watchlists.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/n8n-nodes-fortisiem.svg)](https://www.npmjs.com/package/n8n-nodes-fortisiem)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
9
+ [![n8n community node](https://img.shields.io/badge/n8n-community--node-orange)](https://docs.n8n.io/integrations/community-nodes/)
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ - [Features](#features)
16
+ - [Prerequisites](#prerequisites)
17
+ - [Installation](#installation)
18
+ - [Credentials](#credentials)
19
+ - [Resources & Operations](#resources--operations)
20
+ - [Incident](#incident)
21
+ - [Event](#event)
22
+ - [Organization](#organization)
23
+ - [Watchlist](#watchlist)
24
+ - [Usage Examples](#usage-examples)
25
+ - [Development](#development)
26
+ - [Publishing to npm](#publishing-to-npm)
27
+ - [Compatibility](#compatibility)
28
+ - [License](#license)
29
+
30
+ ---
31
+
32
+ ## Features
33
+
34
+ - **13 operations** across 4 FortiSIEM resource types
35
+ - HTTP Basic authentication with built-in credential testing
36
+ - Automatic JSON parsing of FortiSIEM's `text/plain` responses
37
+ - Optional "Ignore SSL Issues" toggle for self-signed appliance certificates
38
+ - `continueOnFail` support — process remaining items even when one fails
39
+ - Compatible with n8n's **AI Tool** interface (`usableAsTool: true`)
40
+ - Full TypeScript source with strict mode enabled
41
+
42
+ ---
43
+
44
+ ## Prerequisites
45
+
46
+ | Requirement | Version |
47
+ |---|---|
48
+ | n8n | ≥ 1.0.0 |
49
+ | FortiSIEM | ≥ 6.x (REST API, `/phoenix/rest`) |
50
+ | Node.js | ≥ 18.x |
51
+
52
+ ---
53
+
54
+ ## Installation
55
+
56
+ ### Via n8n UI (recommended)
57
+
58
+ 1. Open n8n → **Settings** → **Community Nodes**
59
+ 2. Click **Install**
60
+ 3. Enter `n8n-nodes-fortisiem` and confirm
61
+
62
+ ### Via npm (self-hosted)
63
+
64
+ ```bash
65
+ npm install n8n-nodes-fortisiem
66
+ ```
67
+
68
+ Then restart your n8n instance.
69
+
70
+ ---
71
+
72
+ ## Credentials
73
+
74
+ Create a **FortiSIEM API** credential with the following fields:
75
+
76
+ | Field | Description | Example |
77
+ |---|---|---|
78
+ | **Base URL** | Base URL of your FortiSIEM Supervisor (no trailing slash) | `https://fortisiem.example.com` |
79
+ | **Username** | API user as `<organization>/<user>` | `super/admin` |
80
+ | **Password** | Account password | `••••••••` |
81
+ | **Ignore SSL Issues (Insecure)** | Connect even if the TLS certificate fails validation | `true` for self-signed certs |
82
+
83
+ The node authenticates every request using HTTP Basic auth. Click **Test connection** on the credential card to verify connectivity — this calls `GET /phoenix/rest/config/Domain` (list monitored organizations).
84
+
85
+ > **Tip:** Create a dedicated FortiSIEM API user with the minimum required role for your workflows.
86
+
87
+ ---
88
+
89
+ ## Resources & Operations
90
+
91
+ ### Incident
92
+
93
+ | Operation | Description | Endpoint |
94
+ |---|---|---|
95
+ | **Fetch** | Fetch incidents by filters, time range, paging and ordering | `POST /phoenix/rest/pub/incident` |
96
+ | **Get Many** | Retrieve incidents, optionally filtered by status | `GET /phoenix/rest/pub/incident` |
97
+ | **Get Triggering Events** | Retrieve the events that triggered an incident | `GET /phoenix/rest/pub/incident/triggeringEvents` |
98
+ | **Update** | Update the external ticket state of an incident | `POST /phoenix/rest/pub/incident/update/{incidentId}` |
99
+
100
+ **Fetch** filter options: incident IDs, status, `timeFrom`/`timeTo` (epoch ms), `start`, `size`, `orderBy`, `descending`, and the list of `fields` to return.
101
+
102
+ ---
103
+
104
+ ### Event
105
+
106
+ | Operation | Description | Endpoint |
107
+ |---|---|---|
108
+ | **Query** | Run an analytic event query defined by an XML report | `POST /phoenix/rest/query/` |
109
+
110
+ The **Report XML** field is pre-filled with a sample report definition (top events by count) to get you started.
111
+
112
+ ---
113
+
114
+ ### Organization
115
+
116
+ | Operation | Description | Endpoint |
117
+ |---|---|---|
118
+ | **Get Many** | Retrieve the list of monitored organizations | `GET /phoenix/rest/config/Domain` |
119
+ | **Add** | Add a new organization | `POST /phoenix/rest/organization/add` |
120
+ | **Update** | Update an organization | `GET /phoenix/rest/organization/update` |
121
+
122
+ ---
123
+
124
+ ### Watchlist
125
+
126
+ | Operation | Description | Endpoint |
127
+ |---|---|---|
128
+ | **Get Many** | Retrieve all watchlists | `GET /phoenix/rest/watchlist/all` |
129
+ | **Get By Entry** | Get the watchlist containing a specific entry | `GET /phoenix/rest/watchlist/byEntry/{id}` |
130
+ | **Add Entry** | Add an entry to a watchlist | `POST /phoenix/rest/watchlist/addTo` |
131
+ | **Create Group** | Create a new watchlist group | `POST /phoenix/rest/watchlist/save` |
132
+ | **Delete Entry** | Delete one or more watchlist entries | `POST /phoenix/rest/watchlist/entry/delete` |
133
+
134
+ ---
135
+
136
+ ## Usage Examples
137
+
138
+ ### Poll for new active incidents and alert
139
+
140
+ 1. Add a **Schedule Trigger**
141
+ 2. Add a **FortiSIEM** node → resource **Incident** → **Fetch**, set **Time From**/**Time To** to your window and **Status** to `0` (active)
142
+ 3. Add an **IF** node to check severity
143
+ 4. Add a **Slack** / **Email** node to notify
144
+
145
+ ### Sync a FortiSIEM incident to an external ticketing system
146
+
147
+ 1. **FortiSIEM** → Incident → **Fetch** the incidents you care about
148
+ 2. Create the ticket in your ITSM tool
149
+ 3. **FortiSIEM** → Incident → **Update** with the **External Ticket ID** and **External Ticket State** (e.g. `Closed`)
150
+
151
+ ### Investigate an incident's root cause
152
+
153
+ 1. **FortiSIEM** → Incident → **Get Triggering Events**, expression `{{ $json.incidentId }}` as Incident ID
154
+ 2. Inspect the raw events that generated the incident
155
+
156
+ ---
157
+
158
+ ## Development
159
+
160
+ ### Requirements
161
+
162
+ - Node.js ≥ 18
163
+ - npm ≥ 9
164
+
165
+ ### Setup
166
+
167
+ ```bash
168
+ git clone https://github.com/kboykov/n8n-nodes-fortisiem.git
169
+ cd n8n-nodes-fortisiem
170
+ npm install
171
+ ```
172
+
173
+ ### Build
174
+
175
+ ```bash
176
+ npm run build
177
+ ```
178
+
179
+ This runs `tsc` followed by `gulp build:icons` to copy the icon into `dist/`.
180
+
181
+ ### Lint & format
182
+
183
+ ```bash
184
+ npm run lint
185
+ npm run lint:fix # auto-fix where possible
186
+ npm run format # prettier
187
+ ```
188
+
189
+ ### Local testing with n8n
190
+
191
+ ```bash
192
+ # In this repo
193
+ npm link
194
+
195
+ # In your local n8n installation directory
196
+ npm link n8n-nodes-fortisiem
197
+ ```
198
+
199
+ Then restart n8n. The FortiSIEM node will appear in the node picker.
200
+
201
+ ### Project structure
202
+
203
+ ```
204
+ n8n-nodes-fortisiem/
205
+ ├── credentials/
206
+ │ └── FortiSiemApi.credentials.ts # Credential definition (Basic auth)
207
+ ├── nodes/
208
+ │ └── FortiSiem/
209
+ │ ├── FortiSiem.node.ts # Main node implementation
210
+ │ └── descriptions/
211
+ │ ├── IncidentDescription.ts
212
+ │ ├── EventDescription.ts
213
+ │ ├── OrganizationDescription.ts
214
+ │ └── WatchlistDescription.ts
215
+ ├── icons/
216
+ │ └── fortisiem.png
217
+ ├── .github/workflows/publish.yml # Automated npm publish on tag
218
+ ├── gulpfile.js
219
+ ├── package.json
220
+ └── tsconfig.json
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Publishing to npm
226
+
227
+ Releases are published to [npm](https://www.npmjs.com/package/n8n-nodes-fortisiem) automatically by the [`Publish`](.github/workflows/publish.yml) GitHub Actions workflow, which triggers on any pushed tag matching `*.*.*` (e.g. `0.1.0`).
228
+
229
+ The workflow runs `npm ci`, then `npm publish --provenance --access public`, so releases include [npm provenance](https://docs.npmjs.com/generating-provenance-statements) attestation.
230
+
231
+ ### One-time setup
232
+
233
+ 1. Create an **automation** access token on npmjs.com (Account → Access Tokens → Generate New Token → *Automation*).
234
+ 2. In this repository, go to **Settings → Secrets and variables → Actions** and add a secret named **`NPM_TOKEN`** with that token.
235
+
236
+ ### Cutting a release
237
+
238
+ ```bash
239
+ # Bump the version in package.json and create a matching git tag
240
+ npm version patch # or: minor / major
241
+
242
+ # Push the commit and the tag — the tag push triggers the workflow
243
+ git push --follow-tags
244
+ ```
245
+
246
+ Pushing the tag (for example `v0.1.1` → the workflow matches the `0.1.1` portion) starts the workflow, which builds via the `prepublishOnly` script and publishes to npm.
247
+
248
+ ---
249
+
250
+ ## Compatibility
251
+
252
+ | Component | Status |
253
+ |---|---|
254
+ | n8n Nodes API | v1 |
255
+ | FortiSIEM REST API | `/phoenix/rest` (FortiSIEM ≥ 6.x) |
256
+
257
+ ---
258
+
259
+ ## License
260
+
261
+ [MIT](LICENSE)
262
+
263
+ ---
264
+
265
+ ## Contributing
266
+
267
+ Issues and pull requests are welcome. Please open an issue first to discuss any significant changes.
@@ -0,0 +1,10 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class FortiSiemApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ authenticate: IAuthenticateGeneric;
8
+ test: ICredentialTestRequest;
9
+ }
10
+ //# sourceMappingURL=FortiSiemApi.credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FortiSiemApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/FortiSiemApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,MAAM,cAAc,CAAC;AAEtB,qBAAa,YAAa,YAAW,eAAe;IACnD,IAAI,SAAkB;IAEtB,WAAW,SAAmB;IAE9B,gBAAgB,SACiG;IAEjH,UAAU,EAAE,eAAe,EAAE,CAsC3B;IAGF,YAAY,EAAE,oBAAoB,CAQhC;IAGF,IAAI,EAAE,sBAAsB,CAO1B;CACF"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FortiSiemApi = void 0;
4
+ class FortiSiemApi {
5
+ constructor() {
6
+ this.name = 'fortiSiemApi';
7
+ this.displayName = 'FortiSIEM API';
8
+ this.documentationUrl = 'https://docs.fortinet.com/document/fortisiem/6.4.0/integration-api-guide/895612/json-api-incident-integration';
9
+ this.properties = [
10
+ {
11
+ displayName: 'Base URL',
12
+ name: 'baseUrl',
13
+ type: 'string',
14
+ default: '',
15
+ required: true,
16
+ placeholder: 'https://your-fortisiem-supervisor',
17
+ description: 'Base URL of your FortiSIEM Supervisor, including protocol and (optional) port, without a trailing slash',
18
+ },
19
+ {
20
+ displayName: 'Username',
21
+ name: 'username',
22
+ type: 'string',
23
+ default: '',
24
+ required: true,
25
+ description: 'FortiSIEM API user in the form <organization>/<user> (for the default organization, e.g. super/admin)',
26
+ },
27
+ {
28
+ displayName: 'Password',
29
+ name: 'password',
30
+ type: 'string',
31
+ typeOptions: {
32
+ password: true,
33
+ },
34
+ default: '',
35
+ required: true,
36
+ },
37
+ {
38
+ displayName: 'Ignore SSL Issues (Insecure)',
39
+ name: 'allowUnauthorizedCerts',
40
+ type: 'boolean',
41
+ default: false,
42
+ description: 'Whether to connect even if SSL certificate validation fails. FortiSIEM appliances often use self-signed certificates.',
43
+ },
44
+ ];
45
+ // FortiSIEM's REST API uses HTTP Basic authentication.
46
+ this.authenticate = {
47
+ type: 'generic',
48
+ properties: {
49
+ auth: {
50
+ username: '={{$credentials.username}}',
51
+ password: '={{$credentials.password}}',
52
+ },
53
+ },
54
+ };
55
+ // Validate the credentials by listing monitored organizations.
56
+ this.test = {
57
+ request: {
58
+ method: 'GET',
59
+ baseURL: '={{$credentials.baseUrl}}',
60
+ url: '/phoenix/rest/config/Domain',
61
+ skipSslCertificateValidation: '={{$credentials.allowUnauthorizedCerts}}',
62
+ },
63
+ };
64
+ }
65
+ }
66
+ exports.FortiSiemApi = FortiSiemApi;
67
+ //# sourceMappingURL=FortiSiemApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FortiSiemApi.credentials.js","sourceRoot":"","sources":["../../credentials/FortiSiemApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,YAAY;IAAzB;QACC,SAAI,GAAG,cAAc,CAAC;QAEtB,gBAAW,GAAG,eAAe,CAAC;QAE9B,qBAAgB,GACf,+GAA+G,CAAC;QAEjH,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,mCAAmC;gBAChD,WAAW,EACV,yGAAyG;aAC1G;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACV,uGAAuG;aACxG;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE;oBACZ,QAAQ,EAAE,IAAI;iBACd;gBACD,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,8BAA8B;gBAC3C,IAAI,EAAE,wBAAwB;gBAC9B,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;gBACd,WAAW,EACV,uHAAuH;aACxH;SACD,CAAC;QAEF,uDAAuD;QACvD,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,IAAI,EAAE;oBACL,QAAQ,EAAE,4BAA4B;oBACtC,QAAQ,EAAE,4BAA4B;iBACtC;aACD;SACD,CAAC;QAEF,+DAA+D;QAC/D,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,6BAA6B;gBAClC,4BAA4B,EAAE,0CAA0C;aACxE;SACD,CAAC;IACH,CAAC;CAAA;AApED,oCAoEC"}
Binary file
@@ -0,0 +1,6 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class FortiSiem implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
6
+ //# sourceMappingURL=FortiSiem.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FortiSiem.node.d.ts","sourceRoot":"","sources":["../../../nodes/FortiSiem/FortiSiem.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,iBAAiB,EAGjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EACpB,MAAM,cAAc,CAAC;AA6CtB,qBAAa,SAAU,YAAW,SAAS;IAC1C,WAAW,EAAE,oBAAoB,CAgE/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAuNvE"}
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FortiSiem = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const EventDescription_1 = require("./descriptions/EventDescription");
6
+ const IncidentDescription_1 = require("./descriptions/IncidentDescription");
7
+ const OrganizationDescription_1 = require("./descriptions/OrganizationDescription");
8
+ const WatchlistDescription_1 = require("./descriptions/WatchlistDescription");
9
+ // FortiSIEM returns most payloads as text/plain even though the body is JSON,
10
+ // so parse it ourselves and fall back to the raw string when it isn't JSON.
11
+ function parseResponse(response) {
12
+ if (typeof response !== 'string') {
13
+ return response;
14
+ }
15
+ const trimmed = response.trim();
16
+ if (trimmed === '') {
17
+ return { success: true };
18
+ }
19
+ try {
20
+ return JSON.parse(trimmed);
21
+ }
22
+ catch {
23
+ return { data: response };
24
+ }
25
+ }
26
+ // Split a comma-separated string into a trimmed, non-empty list.
27
+ function splitList(value) {
28
+ return value
29
+ .split(',')
30
+ .map((part) => part.trim())
31
+ .filter((part) => part !== '');
32
+ }
33
+ class FortiSiem {
34
+ constructor() {
35
+ this.description = {
36
+ displayName: 'FortiSIEM',
37
+ name: 'fortiSiem',
38
+ icon: 'file:../../icons/fortisiem.png',
39
+ // 'input' is correct for nodes that fetch / mutate data in an external system
40
+ group: ['input'],
41
+ version: 1,
42
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
43
+ description: 'Interact with the FortiSIEM security information and event management platform',
44
+ defaults: {
45
+ name: 'FortiSIEM',
46
+ },
47
+ usableAsTool: true,
48
+ inputs: ['main'],
49
+ outputs: ['main'],
50
+ credentials: [
51
+ {
52
+ name: 'fortiSiemApi',
53
+ required: true,
54
+ },
55
+ ],
56
+ properties: [
57
+ {
58
+ displayName: 'Resource',
59
+ name: 'resource',
60
+ type: 'options',
61
+ noDataExpression: true,
62
+ options: [
63
+ {
64
+ name: 'Event',
65
+ value: 'event',
66
+ description: 'Run analytic queries against collected events',
67
+ },
68
+ {
69
+ name: 'Incident',
70
+ value: 'incident',
71
+ description: 'Fetch, inspect and update incidents',
72
+ },
73
+ {
74
+ name: 'Organization',
75
+ value: 'organization',
76
+ description: 'Manage monitored organizations',
77
+ },
78
+ {
79
+ name: 'Watchlist',
80
+ value: 'watchlist',
81
+ description: 'Manage watchlists and their entries',
82
+ },
83
+ ],
84
+ default: 'incident',
85
+ },
86
+ // ── Operations (one block per resource) ─────────────────────
87
+ ...EventDescription_1.eventOperations,
88
+ ...IncidentDescription_1.incidentOperations,
89
+ ...OrganizationDescription_1.organizationOperations,
90
+ ...WatchlistDescription_1.watchlistOperations,
91
+ // ── Fields (one block per resource) ─────────────────────────
92
+ ...EventDescription_1.eventFields,
93
+ ...IncidentDescription_1.incidentFields,
94
+ ...OrganizationDescription_1.organizationFields,
95
+ ...WatchlistDescription_1.watchlistFields,
96
+ ],
97
+ };
98
+ }
99
+ async execute() {
100
+ const items = this.getInputData();
101
+ const returnData = [];
102
+ const credentials = (await this.getCredentials('fortiSiemApi'));
103
+ const baseUrl = credentials.baseUrl.replace(/\/$/, '');
104
+ // Typed helper — authentication (Basic auth) is injected via the credential.
105
+ const makeRequest = async (method, path, options = {}) => {
106
+ var _a;
107
+ const requestOptions = {
108
+ method,
109
+ url: `${baseUrl}${path}`,
110
+ headers: {
111
+ Accept: 'application/json',
112
+ ...(options.contentType ? { 'Content-Type': options.contentType } : {}),
113
+ },
114
+ qs: options.qs && Object.keys(options.qs).length ? options.qs : undefined,
115
+ body: options.body,
116
+ json: (_a = options.json) !== null && _a !== void 0 ? _a : false,
117
+ skipSslCertificateValidation: credentials.allowUnauthorizedCerts,
118
+ };
119
+ return this.helpers.httpRequestWithAuthentication.call(this, 'fortiSiemApi', requestOptions);
120
+ };
121
+ for (let i = 0; i < items.length; i++) {
122
+ try {
123
+ const resource = this.getNodeParameter('resource', i);
124
+ const operation = this.getNodeParameter('operation', i);
125
+ let responseData;
126
+ // ── INCIDENT ─────────────────────────────────────────────
127
+ if (resource === 'incident') {
128
+ if (operation === 'getMany') {
129
+ const status = this.getNodeParameter('status', i, '');
130
+ const qs = {};
131
+ if (status !== '')
132
+ qs.status = status;
133
+ responseData = parseResponse(await makeRequest('GET', '/phoenix/rest/pub/incident', { qs }));
134
+ }
135
+ else if (operation === 'fetch') {
136
+ const filters = this.getNodeParameter('filterOptions', i, {});
137
+ const body = {};
138
+ const filterClause = {};
139
+ if (filters.incidentId) {
140
+ filterClause.incidentId = splitList(filters.incidentId).map(Number);
141
+ }
142
+ if (filters.status) {
143
+ filterClause.status = splitList(filters.status).map(Number);
144
+ }
145
+ if (Object.keys(filterClause).length)
146
+ body.filters = filterClause;
147
+ if (filters.timeFrom)
148
+ body.timeFrom = filters.timeFrom;
149
+ if (filters.timeTo)
150
+ body.timeTo = filters.timeTo;
151
+ if (filters.start !== undefined)
152
+ body.start = filters.start;
153
+ if (filters.size !== undefined)
154
+ body.size = filters.size;
155
+ if (filters.orderBy)
156
+ body.orderBy = filters.orderBy;
157
+ if (filters.descending !== undefined)
158
+ body.descending = filters.descending;
159
+ if (filters.fields)
160
+ body.fields = splitList(filters.fields);
161
+ responseData = parseResponse(await makeRequest('POST', '/phoenix/rest/pub/incident', {
162
+ body,
163
+ contentType: 'application/json',
164
+ json: true,
165
+ }));
166
+ }
167
+ else if (operation === 'getTriggeringEvents') {
168
+ const incidentId = this.getNodeParameter('incidentId', i);
169
+ const size = this.getNodeParameter('size', i);
170
+ responseData = parseResponse(await makeRequest('GET', '/phoenix/rest/pub/incident/triggeringEvents', {
171
+ qs: { incidentId, size },
172
+ }));
173
+ }
174
+ else if (operation === 'update') {
175
+ const incidentId = this.getNodeParameter('incidentId', i);
176
+ const incidentExtTicketId = this.getNodeParameter('incidentExtTicketId', i);
177
+ const updateFields = this.getNodeParameter('updateFields', i, {});
178
+ const body = { incidentExtTicketId, ...updateFields };
179
+ responseData = parseResponse(await makeRequest('POST', `/phoenix/rest/pub/incident/update/${encodeURIComponent(incidentId)}`, { body, contentType: 'application/json', json: true }));
180
+ }
181
+ else {
182
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
183
+ itemIndex: i,
184
+ });
185
+ }
186
+ // ── EVENT ────────────────────────────────────────────────
187
+ }
188
+ else if (resource === 'event') {
189
+ if (operation === 'query') {
190
+ const reportXml = this.getNodeParameter('reportXml', i);
191
+ responseData = parseResponse(await makeRequest('POST', '/phoenix/rest/query/', {
192
+ body: reportXml,
193
+ contentType: 'text/xml',
194
+ }));
195
+ }
196
+ else {
197
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
198
+ itemIndex: i,
199
+ });
200
+ }
201
+ // ── ORGANIZATION ─────────────────────────────────────────
202
+ }
203
+ else if (resource === 'organization') {
204
+ if (operation === 'getMany') {
205
+ responseData = parseResponse(await makeRequest('GET', '/phoenix/rest/config/Domain'));
206
+ }
207
+ else if (operation === 'add') {
208
+ const body = this.getNodeParameter('body', i);
209
+ responseData = parseResponse(await makeRequest('POST', '/phoenix/rest/organization/add', {
210
+ body,
211
+ contentType: 'text/xml',
212
+ }));
213
+ }
214
+ else if (operation === 'update') {
215
+ responseData = parseResponse(await makeRequest('GET', '/phoenix/rest/organization/update'));
216
+ }
217
+ else {
218
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
219
+ itemIndex: i,
220
+ });
221
+ }
222
+ // ── WATCHLIST ────────────────────────────────────────────
223
+ }
224
+ else if (resource === 'watchlist') {
225
+ if (operation === 'getMany') {
226
+ responseData = parseResponse(await makeRequest('GET', '/phoenix/rest/watchlist/all'));
227
+ }
228
+ else if (operation === 'getByEntry') {
229
+ const entryId = this.getNodeParameter('entryId', i);
230
+ responseData = parseResponse(await makeRequest('GET', `/phoenix/rest/watchlist/byEntry/${encodeURIComponent(entryId)}`));
231
+ }
232
+ else if (operation === 'addEntry') {
233
+ const watchlistId = this.getNodeParameter('watchlistId', i);
234
+ const body = this.getNodeParameter('body', i);
235
+ responseData = parseResponse(await makeRequest('POST', '/phoenix/rest/watchlist/addTo', {
236
+ body,
237
+ qs: { watchlistId },
238
+ contentType: 'application/json',
239
+ }));
240
+ }
241
+ else if (operation === 'createGroup') {
242
+ const body = this.getNodeParameter('body', i);
243
+ responseData = parseResponse(await makeRequest('POST', '/phoenix/rest/watchlist/save', {
244
+ body,
245
+ contentType: 'application/json',
246
+ }));
247
+ }
248
+ else if (operation === 'deleteEntry') {
249
+ const body = this.getNodeParameter('body', i);
250
+ responseData = parseResponse(await makeRequest('POST', '/phoenix/rest/watchlist/entry/delete', {
251
+ body,
252
+ contentType: 'application/json',
253
+ }));
254
+ }
255
+ else {
256
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
257
+ itemIndex: i,
258
+ });
259
+ }
260
+ }
261
+ else {
262
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown resource: ${resource}`, {
263
+ itemIndex: i,
264
+ });
265
+ }
266
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
267
+ returnData.push(...executionData);
268
+ }
269
+ catch (error) {
270
+ if (this.continueOnFail()) {
271
+ returnData.push({
272
+ json: { error: error.message },
273
+ pairedItem: { item: i },
274
+ });
275
+ continue;
276
+ }
277
+ throw error;
278
+ }
279
+ }
280
+ return [returnData];
281
+ }
282
+ }
283
+ exports.FortiSiem = FortiSiem;
284
+ //# sourceMappingURL=FortiSiem.node.js.map