@crowdstrike/foundry-js 0.11.0 → 0.12.1

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 CHANGED
@@ -1,8 +1,10 @@
1
+ ![@crowdstrike/foundry-js](https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png)
2
+
1
3
  # @crowdstrike/foundry-js
2
4
 
3
- `foundry.js` is the JavaScript SDK for authoring UI Extensions for CrowdStrike's Foundry platform.
5
+ `foundry.js` javascript library provides convenient access to the CrowdStrike's Foundry API to author UI pages and extensions.
4
6
 
5
- ## Installation
7
+ ### Installation
6
8
 
7
9
  ```sh
8
10
  npm install @crowdstrike/foundry-js
@@ -12,6 +14,227 @@ yarn add @crowdstrike/foundry-js
12
14
  pnpm add @crowdstrike/foundry-js
13
15
  ```
14
16
 
17
+ ## Overview 🔎
18
+
19
+ SDK provides abstractions to build Foundry Pages, Extensions and interact with Foundry artifacts - Workflows, Collections, LogScale, API Integrations and CrowdStrike APIs.
20
+
15
21
  ## Usage
16
22
 
17
- > TODO
23
+ When application starts, it should establish connection to Falcon Console. If connection is not establishes in first 5 seconds - app or extension will be dropped from loading on the page.
24
+
25
+ ```javascript
26
+ import FalconApi from '@crowdstrike/foundry-js';
27
+
28
+ (async () => {
29
+ const falcon = new FalconApi();
30
+
31
+ await falcon.connect();
32
+ });
33
+ ```
34
+
35
+ ### Receive events from Falcon Console
36
+
37
+ When UI extensions is loaded, it might receive data for the context it is loaded,
38
+ for example if UI extension was built for Detection side panel, it will receive detection associated data.
39
+ If `data` is updated in Falcon Console - event will automatically execute and pass new data.
40
+
41
+ ```javascript
42
+ (async () => {
43
+ falcon.events.on('data', (data) => {
44
+ // store received `data` and use it inside your application
45
+ });
46
+ });
47
+ ```
48
+
49
+ ### Working with Workflows
50
+
51
+ To call on-demand workflow:
52
+
53
+ ```javascript
54
+ (async () => {
55
+ const config = { name: 'WorkflowName', depth: 0 };
56
+
57
+ const pendingResult = await falcon.api.workflows.postEntitiesExecuteV1({}, config);
58
+
59
+ const result = await falcon.api.workflows.getEntitiesExecutionResultsV1({ ids: triggerResult.resources[0] });
60
+ });
61
+ ```
62
+
63
+ ### Working with Collections
64
+
65
+ ```javascript
66
+ (async () => {
67
+ const sampleData = {
68
+ "name": "John",
69
+ "age": 42,
70
+ "aliases": ["Doe", "Foundry"]
71
+ };
72
+
73
+ const collection = falcon
74
+ .collection({collection: '<collectionName>' });
75
+
76
+ // to write a collection
77
+ const result = await collection.write('test-key', sampleData);
78
+
79
+ // read collection
80
+ const record = await collection.read('test-key');
81
+ // record.age === 42
82
+
83
+ // search collection, `filter` uses FQL (Falcon Query Language)
84
+ const searchResult = await collection.search({ filter: `name:'*'` });
85
+
86
+ // deletes record
87
+ const deleteResponse = await collection.delete('test-key');
88
+ });
89
+ ```
90
+
91
+ ### Working with LogScale
92
+
93
+ ```javascript
94
+ (async () => {
95
+ // write to LogScale
96
+ const writeResult = await falcon.logscale.write({ test: 'check' });
97
+ // writeResult.resources?.[0]?.rows_written === 1
98
+
99
+ // run dynamic query
100
+ const queryResult = await falcon.logscale.query({ search_query: "*", start: "1h" });
101
+ // queryResult.resources?.[0]?.event_count > 0
102
+
103
+ // run saved query
104
+ const savedQueryResult = await falcon.logscale.savedQuery({ id: "<savedQueryId>", start: "30d", mode: 'sync' });
105
+ // savedQueryResult.resources?.[0]?.event_count > 0
106
+ });
107
+ ```
108
+
109
+ ### Working with API Integration
110
+
111
+ To call API Integration, App should be initially provisioned, and configuration for API Integration should be set up.
112
+
113
+ ```javascript
114
+ (async () => {
115
+ // we assume, that API Integration was created and operation Get Cities exists
116
+
117
+ const apiIntegration = falcon.apiIntegration({
118
+ operationId: 'Get Cities',
119
+ });
120
+
121
+ const response = await apiIntegration.execute({
122
+ request: {
123
+ params: {
124
+ path: {
125
+ country: 'Spain'
126
+ }
127
+ }
128
+ }
129
+ });
130
+ // response.resources?.[0]?.status_code === 200
131
+ });
132
+ ```
133
+
134
+ ### Working with Cloud Functions
135
+
136
+ ```javascript
137
+ (async () => {
138
+ const config = {
139
+ name: 'CloudFunctionName',
140
+ version: 1
141
+ };
142
+
143
+ const cloudFunction = falcon.cloudFunction(config);
144
+
145
+ // you can specify path parameters that will be passsed to your Cloud Function.
146
+ // `id` and `mode` - example query params that your Cloud Function will receive
147
+ const getResponse = await cloudFunction.path('/?id=150&mode=compact')
148
+ .get();
149
+
150
+ // you can call different HTTP methods - GET, POST, PATCH, PUT, DELETE
151
+ const postResponse = await cloudFunction.path('/')
152
+ .post({ name: 'test' });
153
+
154
+ const patchResponse = cloudFunction.path('/')
155
+ .patch({ name: 'test' });
156
+
157
+ const putResponse = cloudFunction.path('/')
158
+ .put({ name: 'test' });
159
+
160
+ const deleteResponse = cloudFunction.path('/?id=100')
161
+ .delete();
162
+ });
163
+ ```
164
+
165
+ ### Navigation utilities
166
+
167
+ As Page or UI extension will run inside sandboxed iframe, clicking links or navigating will be limited.
168
+ When browser URL changes, Foundry UI Page will receive that data via iframe hash change event.
169
+ You can listen to hash change event and process your app internal navigation.
170
+
171
+ ```javascript
172
+ window.addEventListener("hashchange", (event) => {
173
+ let rawHash = new URL(event.newURL).hash;
174
+
175
+ const path = rawHash.substring(1);
176
+ }, false);
177
+
178
+ // to read initial hash when your app loads:
179
+ const initialPath = document.location.hash.substring(1);
180
+ ```
181
+
182
+ If you have links in your application, that point to the internal URLs of your application (for example navigation from /page-1 to /page-2) -
183
+ you can add `data-` attribute to those links, and add onClick handler, that will handle navigation outside of iframe and will update iframe hash.
184
+
185
+ ```javascript
186
+ // find all links with data attribute - `data-internal-link`
187
+ document.querySelector('[data-internal-link]')
188
+ .addEventListener('click', (event) => falcon.navigation.onClick(event, '_self', 'internal'));
189
+ ```
190
+
191
+ If you have external links that you want to navigate to, for example www.crowdstrike.com, you can add `data-` attribute to identify those:
192
+
193
+ ```javascript
194
+ // find all links with data attribute - `data-external-link`
195
+ document.querySelector('[data-external-link]')
196
+ .addEventListener('click', (event) => falcon.navigation.onClick(event));
197
+ ```
198
+
199
+ ### Modal utility
200
+
201
+ To open a modal within Falcon Console, rendering UI extension of your choice:
202
+
203
+ ```javascript
204
+ const result = await api.ui.openModal(
205
+ {
206
+ id: '<extension ID as defined in the manifest>',
207
+ type: 'extension' // 'extension' | 'page'
208
+ },
209
+ 'Modal title',
210
+ {
211
+ path: '/', // initial path that will be set when page or extension loads
212
+ data: { foo: 'bar' }, // data to pass to the modal
213
+ size: 'lg', // width of the modal - 'sm', 'md', 'lg', 'xl'. 'md' is default
214
+ align: 'top', // vertical alignment - 'top' or undefined
215
+ } // OpenModalOptions
216
+ );
217
+
218
+ // to close modal:
219
+ await api.ui.closeModal();
220
+
221
+ await api.ui.closeModal({ foo: 'bar' });// you can pass payload
222
+ ```
223
+
224
+ ## Sample apps
225
+
226
+ | Application | Framework |
227
+ |------------------------------------------------------------------------------------|-----------|
228
+ | [ Triage with MITRE Attack ](https://github.com/CrowdStrike/foundry-sample-mitre ) | Vue |
229
+ | [ Scalable RTR ]( https://github.com/CrowdStrike/foundry-sample-scalable-rtr ) | React |
230
+ | [ Rapid Response ]( https://github.com/CrowdStrike/foundry-sample-rapid-response ) | React |
231
+
232
+ ## Additionally
233
+
234
+ | | Description |
235
+ |-------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
236
+ | [ Javascript Blueprint ](https://github.com/CrowdStrike/foundry-js-blueprint-javascript ) | Starter Javascript blueprint used in Foundry CLI |
237
+ | [ React Blueprint ]( https://github.com/CrowdStrike/foundry-js-blueprint-react ) | Starter React blueprint used in Foundry CLI |
238
+ | [Falcon Shoelace](https://github.com/CrowdStrike/falcon-shoelace) | [Shoelace Library](https://shoelace.style/) of web components styled to fit in Falcon Console |
239
+
240
+
package/dist/index.js CHANGED
@@ -2975,12 +2975,15 @@ class FalconApi {
2975
2975
  * This establishes a connection to send messages between the extension and the Falcon Console. Only when established you will be able to call other APIs.
2976
2976
  */
2977
2977
  async connect() {
2978
- const { origin, data } = await this.bridge.postMessage({ type: 'connect' });
2979
- this.bridge.setOrigin(origin);
2980
- this.data = data;
2981
- this.updateTheme(data?.theme);
2978
+ const response = await this.bridge.postMessage({ type: 'connect' });
2979
+ if (response !== undefined) {
2980
+ const { origin, data } = response;
2981
+ this.bridge.setOrigin(origin);
2982
+ this.data = data;
2983
+ this.updateTheme(data?.theme);
2984
+ this.isConnected = true;
2985
+ }
2982
2986
  this.resizeTracker = new ResizeTracker(this.bridge);
2983
- this.isConnected = true;
2984
2987
  }
2985
2988
  /**
2986
2989
  * The ID of the Foundry app this UI extension belongs to.