From 03bdb9976ce7b7918413e3e7fde20d34cf9ada60 Mon Sep 17 00:00:00 2001 From: "chantal.kelm" Date: Wed, 17 May 2023 10:47:19 -0300 Subject: [PATCH 01/46] parent component --- public/controllers/agent/index.js | 4 +- .../container/os-card-container.scss | 7 + .../container/os-card-container.tsx | 8 + public/controllers/register-agent/index.tsx | 2 + public/services/routes.js | 169 ++++++++---------- public/templates/agents-prev/agents-prev.html | 42 ++++- public/templates/visualize/dashboards.html | 33 +++- 7 files changed, 154 insertions(+), 111 deletions(-) create mode 100644 public/controllers/register-agent/container/os-card-container.scss create mode 100644 public/controllers/register-agent/container/os-card-container.tsx create mode 100644 public/controllers/register-agent/index.tsx diff --git a/public/controllers/agent/index.js b/public/controllers/agent/index.js index c0fefdc072..b4cfe8a759 100644 --- a/public/controllers/agent/index.js +++ b/public/controllers/agent/index.js @@ -11,10 +11,10 @@ */ import { AgentsPreviewController } from './agents-preview'; import { AgentsController } from './agents'; -import { RegisterAgent } from './components/register-agent'; +import RegisterAgent from '../register-agent/container/os-card-container'; import { ExportConfiguration } from './components/export-configuration'; import { AgentsWelcome } from '../../components/common/welcome/agents-welcome'; -import { Mitre } from '../../components/overview' +import { Mitre } from '../../components/overview'; import { AgentsPreview } from './components/agents-preview'; import { AgentsTable } from './components/agents-table'; import { MainModule } from '../../components/common/modules/main'; diff --git a/public/controllers/register-agent/container/os-card-container.scss b/public/controllers/register-agent/container/os-card-container.scss new file mode 100644 index 0000000000..5ab4759aa5 --- /dev/null +++ b/public/controllers/register-agent/container/os-card-container.scss @@ -0,0 +1,7 @@ +.container { + box-sizing: border-box; + height: 1271px; + margin-top: 44px; + background: #ffffff; + border: 1px solid rgba(52, 55, 65, 0.2); +} diff --git a/public/controllers/register-agent/container/os-card-container.tsx b/public/controllers/register-agent/container/os-card-container.tsx new file mode 100644 index 0000000000..7fd47c2088 --- /dev/null +++ b/public/controllers/register-agent/container/os-card-container.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import './os-card-container.scss'; + +const Container: React.FC = () => { + return
; +}; + +export default Container; diff --git a/public/controllers/register-agent/index.tsx b/public/controllers/register-agent/index.tsx new file mode 100644 index 0000000000..fd6b56af64 --- /dev/null +++ b/public/controllers/register-agent/index.tsx @@ -0,0 +1,2 @@ +export Container from './container/os-card-container'; + diff --git a/public/services/routes.js b/public/services/routes.js index 6b24043526..1f352ff20e 100644 --- a/public/services/routes.js +++ b/public/services/routes.js @@ -15,12 +15,7 @@ import 'angular-route'; // Functions to be executed before loading certain routes -import { - settingsWizard, - getSavedSearch, - getIp, - getWzConfig, -} from './resolves'; +import { settingsWizard, getSavedSearch, getIp, getWzConfig } from './resolves'; // HTML templates import healthCheckTemplate from '../templates/health-check/health-check.html'; @@ -52,12 +47,7 @@ const assignPreviousLocation = ($rootScope, $location) => { function ip($q, $rootScope, $window, $location) { const wzMisc = new WzMisc(); assignPreviousLocation($rootScope, $location); - return getIp( - $q, - $window, - $location, - wzMisc - ); + return getIp($q, $window, $location, wzMisc); } function nestedResolve($q, errorHandler, $rootScope, $location, $window) { @@ -77,25 +67,16 @@ function nestedResolve($q, errorHandler, $rootScope, $location, $window) { GenericRequest, errorHandler, wzMisc, - location && location.includes('/health-check') - ) + location && location.includes('/health-check'), + ), ); } -function savedSearch( - $location, - $window, - $rootScope, - $route -) { +function savedSearch($location, $window, $rootScope, $route) { const healthCheckStatus = $window.sessionStorage.getItem('healthCheck'); if (!healthCheckStatus) return; assignPreviousLocation($rootScope, $location); - return getSavedSearch( - $location, - $window, - $route - ); + return getSavedSearch($location, $window, $route); } function wzConfig($q, $rootScope, $location) { @@ -112,7 +93,7 @@ function clearRuleId(commonData) { function enableWzMenu($rootScope, $location) { const location = $location.path(); $rootScope.hideWzMenu = location.includes('/health-check'); - if(!$rootScope.hideWzMenu){ + if (!$rootScope.hideWzMenu) { AppState.setWzMenu(); } } @@ -120,73 +101,73 @@ function enableWzMenu($rootScope, $location) { //Routes const app = getAngularModule(); -app.config(($routeProvider) => { +app.config($routeProvider => { $routeProvider - .when('/health-check', { - template: healthCheckTemplate, - resolve: { wzConfig, ip }, - outerAngularWrapperRoute: true - }) - .when('/agents/:agent?/:tab?/:tabView?', { - template: agentsTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/agents-preview/', { - template: agentsPrevTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/manager/', { - template: managementTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/manager/:tab?', { - template: managementTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, - outerAngularWrapperRoute: true - }) - .when('/overview/', { - template: overviewTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/settings', { - template: settingsTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/security', { - template: securityTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - outerAngularWrapperRoute: true - }) - .when('/wazuh-dev', { - template: toolsTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - outerAngularWrapperRoute: true - }) - .when('/blank-screen', { - template: blankScreenTemplate, - resolve: { enableWzMenu }, - outerAngularWrapperRoute: true - }) - .when('/', { - redirectTo: '/overview/', - outerAngularWrapperRoute: true - }) - .when('', { - redirectTo: '/overview/', - outerAngularWrapperRoute: true - }) - .otherwise({ - redirectTo: '/overview', - outerAngularWrapperRoute: true - }); + .when('/health-check', { + template: healthCheckTemplate, + resolve: { wzConfig, ip }, + outerAngularWrapperRoute: true, + }) + .when('/agents/:agent?/:tab?/:tabView?', { + template: agentsTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/agents-preview/', { + template: agentsPrevTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/manager/', { + template: managementTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/manager/:tab?', { + template: managementTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, + outerAngularWrapperRoute: true, + }) + .when('/overview/', { + template: overviewTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/settings', { + template: settingsTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/security', { + template: securityTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + outerAngularWrapperRoute: true, + }) + .when('/wazuh-dev', { + template: toolsTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + outerAngularWrapperRoute: true, + }) + .when('/blank-screen', { + template: blankScreenTemplate, + resolve: { enableWzMenu }, + outerAngularWrapperRoute: true, + }) + .when('/', { + redirectTo: '/overview/', + outerAngularWrapperRoute: true, + }) + .when('', { + redirectTo: '/overview/', + outerAngularWrapperRoute: true, + }) + .otherwise({ + redirectTo: '/overview', + outerAngularWrapperRoute: true, + }); }); diff --git a/public/templates/agents-prev/agents-prev.html b/public/templates/agents-prev/agents-prev.html index cec8cb3637..573720fea4 100644 --- a/public/templates/agents-prev/agents-prev.html +++ b/public/templates/agents-prev/agents-prev.html @@ -1,6 +1,14 @@ -
+
- +
-
+
- Error fetching - agents + + Error fetching agents

{{ ctrl.errorInit || 'Internal error' }}

-
@@ -37,7 +60,10 @@ layout-align="start space-around" >
- +
diff --git a/public/templates/visualize/dashboards.html b/public/templates/visualize/dashboards.html index f03852120a..da9a9c35e5 100644 --- a/public/templates/visualize/dashboards.html +++ b/public/templates/visualize/dashboards.html @@ -1,6 +1,9 @@
- +
@@ -34,7 +37,9 @@ ng-if="reportBusy && reportStatus && showModuleDashboard" class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentSpaceAround euiFlexGroup--directionRow euiFlexGroup--responsive" > -
+
@@ -82,7 +87,8 @@ d="M13.6 12.186l-1.357-1.358c-.025-.025-.058-.034-.084-.056.53-.794.84-1.746.84-2.773a4.977 4.977 0 0 0-.84-2.772c.026-.02.059-.03.084-.056L13.6 3.813a6.96 6.96 0 0 1 0 8.373zM8 15A6.956 6.956 0 0 1 3.814 13.6l1.358-1.358c.025-.025.034-.057.055-.084C6.02 12.688 6.974 13 8 13a4.978 4.978 0 0 0 2.773-.84c.02.026.03.058.056.083l1.357 1.358A6.956 6.956 0 0 1 8 15zm-5.601-2.813a6.963 6.963 0 0 1 0-8.373l1.359 1.358c.024.025.057.035.084.056A4.97 4.97 0 0 0 3 8c0 1.027.31 1.98.842 2.773-.027.022-.06.031-.084.056l-1.36 1.358zm5.6-.187A4 4 0 1 1 8 4a4 4 0 0 1 0 8zM8 1c1.573 0 3.019.525 4.187 1.4l-1.357 1.358c-.025.025-.035.057-.056.084A4.979 4.979 0 0 0 8 3a4.979 4.979 0 0 0-2.773.842c-.021-.027-.03-.059-.055-.084L3.814 2.4A6.957 6.957 0 0 1 8 1zm0-1a8.001 8.001 0 1 0 .003 16.002A8.001 8.001 0 0 0 8 0z" > - + + No agents were added to this manager: @@ -90,8 +96,14 @@
-
-
+
+
-
+
- +
From 75cce24a327bd28794aa08efd44e9654300737c3 Mon Sep 17 00:00:00 2001 From: "chantal.kelm" Date: Fri, 19 May 2023 14:30:21 -0300 Subject: [PATCH 02/46] Added a title to the container and updated filenames --- public/controllers/agent/index.js | 2 +- .../container/os-card-container.scss | 7 ------- .../container/os-card-container.tsx | 8 ------- .../container/register-agent.scss | 21 +++++++++++++++++++ .../container/register-agent.tsx | 10 +++++++++ public/controllers/register-agent/index.tsx | 3 +-- 6 files changed, 33 insertions(+), 18 deletions(-) delete mode 100644 public/controllers/register-agent/container/os-card-container.scss delete mode 100644 public/controllers/register-agent/container/os-card-container.tsx create mode 100644 public/controllers/register-agent/container/register-agent.scss create mode 100644 public/controllers/register-agent/container/register-agent.tsx diff --git a/public/controllers/agent/index.js b/public/controllers/agent/index.js index b4cfe8a759..c9e06604aa 100644 --- a/public/controllers/agent/index.js +++ b/public/controllers/agent/index.js @@ -11,7 +11,7 @@ */ import { AgentsPreviewController } from './agents-preview'; import { AgentsController } from './agents'; -import RegisterAgent from '../register-agent/container/os-card-container'; +import { RegisterAgent } from '../register-agent/container/register-agent'; import { ExportConfiguration } from './components/export-configuration'; import { AgentsWelcome } from '../../components/common/welcome/agents-welcome'; import { Mitre } from '../../components/overview'; diff --git a/public/controllers/register-agent/container/os-card-container.scss b/public/controllers/register-agent/container/os-card-container.scss deleted file mode 100644 index 5ab4759aa5..0000000000 --- a/public/controllers/register-agent/container/os-card-container.scss +++ /dev/null @@ -1,7 +0,0 @@ -.container { - box-sizing: border-box; - height: 1271px; - margin-top: 44px; - background: #ffffff; - border: 1px solid rgba(52, 55, 65, 0.2); -} diff --git a/public/controllers/register-agent/container/os-card-container.tsx b/public/controllers/register-agent/container/os-card-container.tsx deleted file mode 100644 index 7fd47c2088..0000000000 --- a/public/controllers/register-agent/container/os-card-container.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import './os-card-container.scss'; - -const Container: React.FC = () => { - return
; -}; - -export default Container; diff --git a/public/controllers/register-agent/container/register-agent.scss b/public/controllers/register-agent/container/register-agent.scss new file mode 100644 index 0000000000..317685dde5 --- /dev/null +++ b/public/controllers/register-agent/container/register-agent.scss @@ -0,0 +1,21 @@ +.container { + box-sizing: border-box; + height: 1271px; + margin-top: 44px; + background: #ffffff; + border: 1px solid rgba(52, 55, 65, 0.2); + max-width: 1030px; +} + +.title { + margin-top: 51px; + margin-bottom: 51px; + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-size: 30px; + line-height: 36px; + color: #343741; + display: flex; + justify-content: center; +} diff --git a/public/controllers/register-agent/container/register-agent.tsx b/public/controllers/register-agent/container/register-agent.tsx new file mode 100644 index 0000000000..87a780b0e7 --- /dev/null +++ b/public/controllers/register-agent/container/register-agent.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import './register-agent.scss'; + +export const RegisterAgent: React.FC = () => { + return ( +
+
Deploy new agent
+
+ ); +}; diff --git a/public/controllers/register-agent/index.tsx b/public/controllers/register-agent/index.tsx index fd6b56af64..d516e34a58 100644 --- a/public/controllers/register-agent/index.tsx +++ b/public/controllers/register-agent/index.tsx @@ -1,2 +1 @@ -export Container from './container/os-card-container'; - +export { RegisterAgent } from './container/register-agent'; From edd6ff48a81c3f751f6a76dd36094bf11767ea06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chantal=20Bel=C3=A9n=20kelm?= <99441266+chantal-kelm@users.noreply.github.com> Date: Tue, 23 May 2023 12:29:03 -0300 Subject: [PATCH 03/46] Update register-agent.scss --- public/controllers/register-agent/container/register-agent.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/controllers/register-agent/container/register-agent.scss b/public/controllers/register-agent/container/register-agent.scss index 317685dde5..6f5dc8ed6c 100644 --- a/public/controllers/register-agent/container/register-agent.scss +++ b/public/controllers/register-agent/container/register-agent.scss @@ -5,6 +5,8 @@ background: #ffffff; border: 1px solid rgba(52, 55, 65, 0.2); max-width: 1030px; + padding-left: 72px; + padding-right: 63px; } .title { From 8b646f51d2d4b3afa49ef399ad83eff35fdffd20 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> Date: Wed, 24 May 2023 11:13:34 -0300 Subject: [PATCH 04/46] [Redesign add agent] Register agent reuse common/form component (Settings > Configuration) (#5446) * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * Remove react use inside hook test file * Fix review requested changes --- public/components/common/form/hooks.test.tsx | 357 ++++++++++--------- public/components/common/form/hooks.tsx | 179 ++++++---- public/components/common/form/index.tsx | 22 +- public/components/common/form/types.ts | 93 ++++- 4 files changed, 397 insertions(+), 254 deletions(-) diff --git a/public/components/common/form/hooks.test.tsx b/public/components/common/form/hooks.test.tsx index 38d3f19a27..c96ebe60b8 100644 --- a/public/components/common/form/hooks.test.tsx +++ b/public/components/common/form/hooks.test.tsx @@ -1,178 +1,189 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { useForm } from './hooks'; +import { FormConfiguration, IInputForm } from './types'; describe('[hook] useForm', () => { - - it(`[hook] useForm. Verify the initial state`, async () => { - - const initialFields = { - text1: { - type: 'text', - initialValue: '' - }, - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe('text'); - expect(result.current.fields.text1.value).toBe(''); - expect(result.current.fields.text1.initialValue).toBe(''); - expect(result.current.fields.text1.onChange).toBeDefined(); - }); - - it(`[hook] useForm. Verify the initial state. Multiple fields.`, async () => { - - const initialFields = { - text1: { - type: 'text', - initialValue: '' - }, - number1: { - type: 'number', - initialValue: 1 - }, - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe('text'); - expect(result.current.fields.text1.value).toBe(''); - expect(result.current.fields.text1.initialValue).toBe(''); - expect(result.current.fields.text1.onChange).toBeDefined(); - - expect(result.current.fields.number1.changed).toBe(false); - expect(result.current.fields.number1.error).toBeUndefined(); - expect(result.current.fields.number1.type).toBe('number'); - expect(result.current.fields.number1.value).toBe(1); - expect(result.current.fields.number1.initialValue).toBe(1); - expect(result.current.fields.number1.onChange).toBeDefined(); - }); - - it(`[hook] useForm lifecycle. Set the initial value. Change the field value. Undo changes. Change the field. Do changes.`, async () => { - - const initialFieldValue = ''; - const fieldType = 'text'; - - const initialFields = { - text1: { - type: fieldType, - initialValue: initialFieldValue - } - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(initialFieldValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - expect(result.current.fields.text1.onChange).toBeDefined(); - - // change the input - const changedValue = 't'; - act(() => { - result.current.fields.text1.onChange({ - target: { - value: changedValue - } - }); - }); - - // assert changed state - expect(result.current.fields.text1.changed).toBe(true); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - - // undone changes - act(() => { - result.current.undoChanges(); - }); - - // assert undo changes state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(initialFieldValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - - // change the input - const changedValue2 = 'e'; - act(() => { - result.current.fields.text1.onChange({ - target: { - value: changedValue2 - } - }); - }); - - // assert changed state - expect(result.current.fields.text1.changed).toBe(true); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue2); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - - // done changes - act(() => { - result.current.doChanges() - }); - - // assert do changes state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue2); - expect(result.current.fields.text1.initialValue).toBe(changedValue2); - }); - - it(`[hook] useForm lifecycle. Set the initial value. Change the field value to invalid value`, async () => { - - const initialFieldValue = 'test'; - const fieldType = 'text'; - - const initialFields = { - text1: { - type: fieldType, - initialValue: initialFieldValue, - validate: (value: string): string | undefined => value.length ? undefined : `Validation error: string can be empty.` - } - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(initialFieldValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - expect(result.current.fields.text1.onChange).toBeDefined(); - - // change the input - const changedValue = ''; - act(() => { - result.current.fields.text1.onChange({ - target: { - value: changedValue - } - }); - }); - - // assert changed state - expect(result.current.fields.text1.changed).toBe(true); - expect(result.current.fields.text1.error).toBeTruthy(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - }); + it(`[hook] useForm. Verify the initial state`, async () => { + const initialFields: FormConfiguration = { + text1: { + type: 'text', + initialValue: '', + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe('text'); + expect(result.current.fields.text1.value).toBe(''); + expect(result.current.fields.text1.initialValue).toBe(''); + expect(result.current.fields.text1.onChange).toBeDefined(); + }); + + it(`[hook] useForm. Verify the initial state. Multiple fields.`, async () => { + const initialFields: FormConfiguration = { + text1: { + type: 'text', + initialValue: '', + }, + number1: { + type: 'number', + initialValue: 1, + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe('text'); + expect(result.current.fields.text1.value).toBe(''); + expect(result.current.fields.text1.initialValue).toBe(''); + expect(result.current.fields.text1.onChange).toBeDefined(); + + expect(result.current.fields.number1.changed).toBe(false); + expect(result.current.fields.number1.error).toBeUndefined(); + expect(result.current.fields.number1.type).toBe('number'); + expect(result.current.fields.number1.value).toBe(1); + expect(result.current.fields.number1.initialValue).toBe(1); + expect(result.current.fields.number1.onChange).toBeDefined(); + }); + + it(`[hook] useForm lifecycle. Set the initial value. Change the field value. Undo changes. Change the field. Do changes.`, async () => { + const initialFieldValue = ''; + const fieldType = 'text'; + + const initialFields: FormConfiguration = { + text1: { + type: fieldType, + initialValue: initialFieldValue, + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(initialFieldValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + expect(result.current.fields.text1.onChange).toBeDefined(); + + // change the input + const changedValue = 't'; + act(() => { + result.current.fields.text1.onChange({ + target: { + value: changedValue, + }, + }); + }); + + // assert changed state + expect(result.current.fields.text1.changed).toBe(true); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + + // undone changes + act(() => { + result.current.undoChanges(); + }); + + // assert undo changes state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(initialFieldValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + + // change the input + const changedValue2 = 'e'; + act(() => { + result.current.fields.text1.onChange({ + target: { + value: changedValue2, + }, + }); + }); + + // assert changed state + expect(result.current.fields.text1.changed).toBe(true); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue2); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + + // done changes + act(() => { + result.current.doChanges(); + }); + + // assert do changes state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue2); + expect(result.current.fields.text1.initialValue).toBe(changedValue2); + }); + + it(`[hook] useForm lifecycle. Set the initial value. Change the field value to invalid value`, async () => { + const initialFieldValue = 'test'; + const fieldType = 'text'; + + const initialFields: FormConfiguration = { + text1: { + type: fieldType, + initialValue: initialFieldValue, + validate: (value: string): string | undefined => + value.length ? undefined : `Validation error: string can be empty.`, + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(initialFieldValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + expect(result.current.fields.text1.onChange).toBeDefined(); + + // change the input + const changedValue = ''; + act(() => { + result.current.fields.text1.onChange({ + target: { + value: changedValue, + }, + }); + }); + + // assert changed state + expect(result.current.fields.text1.changed).toBe(true); + expect(result.current.fields.text1.error).toBeTruthy(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + }); + + it('[hook] useForm. Verify the hook behavior when receives a custom field type', async () => { + const formFields: FormConfiguration = { + customField: { + type: 'custom', + initialValue: 'default value', + component: (props:IInputForm) => (<>any component), + }, + }; + + const { result } = renderHook(() => useForm(formFields)); + expect(result.current.fields.customField.component).toBeInstanceOf(Function); + expect(result.current.fields.customField.type).toBe('custom'); + }); }); diff --git a/public/components/common/form/hooks.tsx b/public/components/common/form/hooks.tsx index f3837b4b32..1d2f09e020 100644 --- a/public/components/common/form/hooks.tsx +++ b/public/components/common/form/hooks.tsx @@ -1,91 +1,142 @@ import { useState, useRef } from 'react'; import { isEqual } from 'lodash'; import { EpluginSettingType } from '../../../../common/constants'; +import { + CustomSettingType, + EnhancedFields, + FormConfiguration, + SettingTypes, + UseFormReturn, +} from './types'; -function getValueFromEvent(event, type){ - return (getValueFromEventType[type] || getValueFromEventType.default)(event); -}; -const getValueFromEventType = { - [EpluginSettingType.switch] : (event: any) => event.target.checked, +interface IgetValueFromEventType { + [key: string]: (event: any) => any; +} + +const getValueFromEventType: IgetValueFromEventType = { + [EpluginSettingType.switch]: (event: any) => event.target.checked, [EpluginSettingType.editor]: (value: any) => value, - [EpluginSettingType.filepicker]: (value: any) => value, + custom: (event:any) => event.target, default: (event: any) => event.target.value, }; -export const useForm = (fields) => { - const [formFields, setFormFields] = useState(Object.entries(fields).reduce((accum, [fieldKey, fieldConfiguration]) => ({ - ...accum, - [fieldKey]: { - currentValue: fieldConfiguration.initialValue, - initialValue: fieldConfiguration.initialValue, - } - }), {})); +/** + * Returns the value of the event according to the type of field + * When the type is not found, it returns the value defined in the default key + * + * @param event + * @param type + * @returns event value + */ +function getValueFromEvent( + event: any, + type: SettingTypes | CustomSettingType | string, +): any { + + return getValueFromEventType.hasOwnProperty(type) ? getValueFromEventType[type](event) : getValueFromEventType.default(event) +} + + + +export const useForm = (fields: FormConfiguration): UseFormReturn => { + const [formFields, setFormFields] = useState<{ + [key: string]: { currentValue: any; initialValue: any }; + }>( + Object.entries(fields).reduce( + (accum, [fieldKey, fieldConfiguration]) => ({ + ...accum, + [fieldKey]: { + currentValue: fieldConfiguration.initialValue, + initialValue: fieldConfiguration.initialValue, + }, + }), + {}, + ), + ); - const fieldRefs = useRef({}); + const fieldRefs = useRef<{ [key: string]: any }>({}); - const enhanceFields = Object.entries(formFields).reduce((accum, [fieldKey, {currentValue: value, ...restFieldState}]) => ({ - ...accum, - [fieldKey]: { - ...fields[fieldKey], - ...restFieldState, - type: fields[fieldKey].type, - value, - changed: !isEqual(restFieldState.initialValue, value), - error: fields[fieldKey]?.validate?.(value), - setInputRef: (reference) => {fieldRefs.current[fieldKey] = reference}, - inputRef: fieldRefs.current[fieldKey], - onChange: (event) => { - const inputValue = getValueFromEvent(event, fields[fieldKey].type); - const currentValue = fields[fieldKey]?.transformChangedInputValue?.(inputValue) ?? inputValue; - setFormFields(state => ({ - ...state, - [fieldKey]: { - ...state[fieldKey], - currentValue, - } - })) + const enhanceFields = Object.entries(formFields).reduce( + (accum, [fieldKey, { currentValue: value, ...restFieldState }]) => ({ + ...accum, + [fieldKey]: { + ...fields[fieldKey], + ...restFieldState, + type: fields[fieldKey].type, + value, + changed: !isEqual(restFieldState.initialValue, value), + error: fields[fieldKey]?.validate?.(value), + setInputRef: (reference: any) => { + fieldRefs.current[fieldKey] = reference; + }, + inputRef: fieldRefs.current[fieldKey], + onChange: (event: any) => { + const inputValue = getValueFromEvent(event, fields[fieldKey].type); + const currentValue = + fields[fieldKey]?.transformChangedInputValue?.(inputValue) ?? + inputValue; + setFormFields(state => ({ + ...state, + [fieldKey]: { + ...state[fieldKey], + currentValue, + }, + })); + }, }, - } - }), {}); + }), + {}, + ); const changed = Object.fromEntries( - Object.entries(enhanceFields).filter(([, {changed}]) => changed).map(([fieldKey, {value}]) => ([fieldKey, fields[fieldKey]?.transformChangedOutputValue?.(value) ?? value])) + Object.entries(enhanceFields as EnhancedFields) + .filter(([, { changed }]) => changed) + .map(([fieldKey, { value }]) => [ + fieldKey, + fields[fieldKey]?.transformChangedOutputValue?.(value) ?? value, + ]), ); const errors = Object.fromEntries( - Object.entries(enhanceFields).filter(([, {error}]) => error).map(([fieldKey, {error}]) => ([fieldKey, error])) - ); + Object.entries(enhanceFields as EnhancedFields) + .filter(([, { error }]) => error) + .map(([fieldKey, { error }]) => [fieldKey, error]), + ) as { [key: string]: string }; - function undoChanges(){ - setFormFields(state => Object.fromEntries( - Object.entries(state).map(([fieldKey, fieldConfiguration]) => ([ - fieldKey, - { - ...fieldConfiguration, - currentValue: fieldConfiguration.initialValue - } - ])) - )); - }; + function undoChanges() { + setFormFields(state => + Object.fromEntries( + Object.entries(state).map(([fieldKey, fieldConfiguration]) => [ + fieldKey, + { + ...fieldConfiguration, + currentValue: fieldConfiguration.initialValue, + }, + ]), + ), + ); + } - function doChanges(){ - setFormFields(state => Object.fromEntries( - Object.entries(state).map(([fieldKey, fieldConfiguration]) => ([ - fieldKey, - { - ...fieldConfiguration, - initialValue: fieldConfiguration.currentValue - } - ])) - )); - }; + function doChanges() { + setFormFields(state => + Object.fromEntries( + Object.entries(state).map(([fieldKey, fieldConfiguration]) => [ + fieldKey, + { + ...fieldConfiguration, + initialValue: fieldConfiguration.currentValue, + }, + ]), + ), + ); + } return { fields: enhanceFields, changed, errors, undoChanges, - doChanges + doChanges, }; }; diff --git a/public/components/common/form/index.tsx b/public/components/common/form/index.tsx index 2a6da4610e..0c3fa8a681 100644 --- a/public/components/common/form/index.tsx +++ b/public/components/common/form/index.tsx @@ -7,6 +7,23 @@ import { InputFormSwitch } from './input_switch'; import { InputFormFilePicker } from './input_filepicker'; import { InputFormTextArea } from './input_text_area'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { SettingTypes } from './types'; + +interface InputFormProps { + type: SettingTypes; + value: any; + onChange: (event: React.ChangeEvent) => void; + error?: string; + label?: string; + header?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); + footer?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); + preInput?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); + postInput?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); +} + +interface InputFormComponentProps extends InputFormProps { + rest: any; +} export const InputForm = ({ type, @@ -18,9 +35,10 @@ export const InputForm = ({ footer, preInput, postInput, -...rest}) => { + ...rest +}: InputFormComponentProps) => { - const ComponentInput = Input[type]; + const ComponentInput = Input[type as keyof typeof Input] as React.ComponentType; if(!ComponentInput){ return null; diff --git a/public/components/common/form/types.ts b/public/components/common/form/types.ts index e064804790..737c0c8ab9 100644 --- a/public/components/common/form/types.ts +++ b/public/components/common/form/types.ts @@ -1,19 +1,82 @@ -import { TPluginSettingWithKey } from "../../../../common/constants"; +import { TPluginSettingWithKey } from '../../../../common/constants'; export interface IInputFormType { - field: TPluginSettingWithKey - value: any - onChange: (event: any) => void - isInvalid?: boolean - options: any - setInputRef: (reference: any) => void -}; + field: TPluginSettingWithKey; + value: any; + onChange: (event: any) => void; + isInvalid?: boolean; + options: any; + setInputRef: (reference: any) => void; +} export interface IInputForm { - field: TPluginSettingWithKey - initialValue: any - onChange: (event: any) => void - label?: string - preInput?: ((options: {value: any, error: string | null}) => JSX.Element) - postInput?: ((options: {value: any, error: string | null}) => JSX.Element) -}; + field: TPluginSettingWithKey; + initialValue: any; + onChange: (event: any) => void; + label?: string; + preInput?: (options: { value: any; error: string | null }) => JSX.Element; + postInput?: (options: { value: any; error: string | null }) => JSX.Element; +} + +/// use form hook types +export type SettingTypes = + | 'text' + | 'textarea' + | 'number' + | 'select' + | 'switch' + | 'editor' + | 'filepicker'; + +interface FieldConfiguration { + initialValue: any; + validate?: (value: any) => string | undefined; + transformChangedInputValue?: (value: any) => any; + transformChangedOutputValue?: (value: any) => any; +} + +export interface DefaultFieldConfiguration extends FieldConfiguration { + type: SettingTypes; +} + +export type CustomSettingType = 'custom'; +interface CustomFieldConfiguration extends FieldConfiguration { + type: CustomSettingType; + component: (props: any) => JSX.Element; +} + +export interface FormConfiguration { + [key: string]: DefaultFieldConfiguration | CustomFieldConfiguration; +} + +interface EnhancedField { + currentValue: any; + initialValue: any; + value: any; + changed: boolean; + error: string | null; + setInputRef: (reference: any) => void; + inputRef: any; + onChange: (event: any) => void; +} + +interface EnhancedDefaultField extends EnhancedField { + type: SettingTypes; +} + +interface EnhancedCustomField extends EnhancedField { + type: CustomSettingType; + component: (props: any) => JSX.Element; +} + +export interface EnhancedFields { + [key: string]: EnhancedDefaultField | EnhancedCustomField; +} + +export interface UseFormReturn { + fields: EnhancedFields; + changed: { [key: string]: any }; + errors: { [key: string]: string }; + undoChanges: () => void; + doChanges: () => void; +} From 106cda1ac0f00694fc472929c8466f7cc4d1b541 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:36:24 -0300 Subject: [PATCH 05/46] [Redesign add agent] Add register agent command generator (#5469) --- .../config/os-commands-definitions.ts | 148 +++++++ .../core/register-commands/README.md | 340 +++++++++++++++ .../command-generator.test.ts | 398 ++++++++++++++++++ .../command-generator/command-generator.ts | 179 ++++++++ .../register-commands/exceptions/index.ts | 85 ++++ .../optional-parameters-manager.test.ts | 229 ++++++++++ .../optional-parameters-manager.ts | 49 +++ .../get-install-command.service.test.ts | 118 ++++++ .../services/get-install-command.service.ts | 38 ++ .../search-os-definitions.service.test.ts | 199 +++++++++ .../services/search-os-definitions.service.ts | 86 ++++ .../core/register-commands/types.ts | 98 +++++ .../register-agent/hooks/README.md | 175 ++++++++ .../hooks/use-register-agent-commands.test.ts | 237 +++++++++++ .../hooks/use-register-agent-commands.ts | 109 +++++ 15 files changed, 2488 insertions(+) create mode 100644 public/controllers/register-agent/config/os-commands-definitions.ts create mode 100644 public/controllers/register-agent/core/register-commands/README.md create mode 100644 public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts create mode 100644 public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts create mode 100644 public/controllers/register-agent/core/register-commands/exceptions/index.ts create mode 100644 public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts create mode 100644 public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts create mode 100644 public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts create mode 100644 public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts create mode 100644 public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts create mode 100644 public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts create mode 100644 public/controllers/register-agent/core/register-commands/types.ts create mode 100644 public/controllers/register-agent/hooks/README.md create mode 100644 public/controllers/register-agent/hooks/use-register-agent-commands.test.ts create mode 100644 public/controllers/register-agent/hooks/use-register-agent-commands.ts diff --git a/public/controllers/register-agent/config/os-commands-definitions.ts b/public/controllers/register-agent/config/os-commands-definitions.ts new file mode 100644 index 0000000000..dc69156571 --- /dev/null +++ b/public/controllers/register-agent/config/os-commands-definitions.ts @@ -0,0 +1,148 @@ +import { IOSDefinition, tOptionalParams } from '../core/register-commands/types'; + +// Defined OS combinations +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + + +export type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +/////////////////////////////////////////////////////////////////// +/// Operating system commands definitions +/////////////////////////////////////////////////////////////////// + +const linuxDefinition: IOSDefinition = { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: '32/64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.${props.extension}`, + installCommand: props => + `sudo yum install -y ${props.urlPackage}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + extension: 'deb', + architecture: 'x64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}.${props.extension}`, + installCommand: props => + `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + extension: 'rpm', + architecture: '32/64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.${props.extension}`, + installCommand: props => + `sudo yum install -y ${props.urlPackage}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + extension: 'deb', + architecture: 'x64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64.${props.extension}`, + installCommand: props => + `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + ], +}; + +const windowsDefinition: IOSDefinition = { + name: 'windows', + options: [ + { + extension: 'msi', + architecture: 'x86', + urlPackage: props => + `https://packages.wazuh.com/4.x/windows/wazuh-agent-${props.wazuhVersion}-1.${props.extension}`, + installCommand: props => + `Invoke-WebRequest -Uri ${props.urlPackage} -OutFile \${env.tmp}\\wazuh-agent.${props.extension}; msiexec.exe /i \${env.tmp}\\wazuh-agent.${props.extension} /q`, + startCommand: props => `Start-Service -Name wazuh-agent`, + }, + ], +}; + +const macDefinition: IOSDefinition = { + name: 'mac', + options: [ + { + extension: 'pkg', + architecture: '32/64', + urlPackage: props => + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1.${props.extension}`, + installCommand: props => + `mac -so wazuh-agent.pkg ${props.urlPackage} && sudo launchctl setenv && sudo installer -pkg ./wazuh-agent.pkg -target /`, + startCommand: props => `sudo /Library/Ossec/bin/wazuh-control start`, + }, + ], +}; + +export const osCommandsDefinitions = [ + linuxDefinition, + windowsDefinition, + macDefinition, +]; + +/////////////////////////////////////////////////////////////////// +/// Optional parameters definitions +/////////////////////////////////////////////////////////////////// + +export const optionalParamsDefinitions: tOptionalParams = { + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + } + }, + agent_name: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + } + }, + protocol: { + property: 'WAZUH_MANAGER_PROTOCOL', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + } + }, + agent_group: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + } + }, + wazuh_password: { + property: 'WAZUH_PASSWORD', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + } + } +} diff --git a/public/controllers/register-agent/core/register-commands/README.md b/public/controllers/register-agent/core/register-commands/README.md new file mode 100644 index 0000000000..d53288e940 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/README.md @@ -0,0 +1,340 @@ +# Documentation + +- [Register Agent](#register-agent) + - [Solution details](#solution-details) + - [Configuration details](#configuration-details) + - [OS Definitions](#os-definitions) + - [Configuration example](#configuration-example) + - [Validations](#validations) + - [Optional Parameters Configuration](#optional-parameters-configuration) + - [Configuration example](#configuration-example-1) + - [Validations](#validations-1) + - [Command Generator](#command-generator) + - [Get install command](#get-install-command) + - [Get start command](#get-start-command) + - [Get url package](#get-url-package) + - [Get all commands](#get-all-commands) + +# Register Agent + +The register agent is a process that will allow the user to register an agent in the Wazuh Manager. The plugin will provide a form where the user will be able to select the OS and the package that he wants to install. The plugin will generate the registration commands and will show them to the user. + +# Solution details + +To optimize and make more easier the process to generate the registration commands we have created a class called `Command Generator` that given a set of parameters it will generate the registration commands. + +## Configuration + +To make the command generator works we need to configure the following parameters and pass them to the class: + +## OS Definitions + +The OS definitions are a set of parameters that will be used to generate the registration commands. The parameters are the following: + +```ts + + +// global types + +export interface IOptionsParamConfig { + property: string; + getParamCommand: (props: tOptionalParamsCommandProps) => string; +} + +export type tOptionalParams = { + [key in T]: IOptionsParamConfig; +}; + +export interface IOperationSystem { + name: string; + architecture: string; + extension: string; +} + +/// .... + +interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; // add the necessary OS options + +type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +export interface IOSDefinition { + name: OS['name']; + options: IOSCommandsDefinition[]; +} + +export interface IOSCommandsDefinition { + extension: OS['extension']; + architecture: OS['architecture']; + urlPackage: (props: tOSEntryProps) => string; + installCommand: (props: tOSEntryProps & { urlPackage: string }) => string; + startCommand: (props: tOSEntryProps) => string; +} + +``` + +This configuration will define the different OS that we want to support and the different packages that we want to support for each OS. The `urlPackage` function will be used to generate the URL to download the package, the `installCommand` function will be used to generate the command to install the package and the `startCommand` function will be used to generate the command to start the agent. + +### Configuration example + +```ts + +const osDefinitions: IOSDefinition[] = [{ + name: 'linux', + options: [ + { + extension: 'rpm', + architecture: 'amd64', + urlPackage: props => 'add url package', + installCommand: props => 'add install command', + startCommand: props => `add start command`, + }, + { + extension: 'deb', + architecture: 'amd64', + urlPackage: props => 'add url package', + installCommand: props => 'add install command', + startCommand: props => `add start command`, + } + ], +}, +{ + name: 'windows', + options: [ + { + extension: 'msi', + architecture: '32/64', + urlPackage: props => 'add url package', + installCommand: props => 'add install command', + startCommand: props => `add start command`, + }, + ], + } +}; +``` + +## Validations + +The `Command Generator` will validate the OS Definitions received and will throw an error if the configuration is not valid. The validations are the following: + +- The OS Definitions must not have duplicated OS names defined. +- The OS Definitions must not have duplicated options defined. +- The OS names would be defined in the `tOS` type. +- The Package Extensions would be defined in the `tPackageExtensions` type. + +Another validations will be provided in development time and will be provided by the types added to the project. You can find the types definitions in the `types` file. + + +## Optional Parameters Configuration + +The optional parameters are a set of parameters that will be added to the registration commands. The parameters are the following: + +```ts + +export type tOptionalParamsName = + | 'server_address' + | 'agent_name' + | 'protocol' + | 'agent_group' + | 'wazuh_password'; + +export type tOptionalParams = { + [key in tOptionalParamsName]: { + property: string; + getParamCommand: (props) => string; + }; +} + +``` + +This configuration will define the different optional parameters that we want to support and the way how to we will process and show in the commands.The `getParamCommand` is the function that will process the props received and show the final command format. + +### Configuration example + +```ts + +export const optionalParameters: tOptionalParams = { + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: props => 'returns the optional param command' + } + }, + any_other: { + property: 'PARAM NAME IN THE COMMAND', + getParamCommand: props => 'returns the optional param command' + }, +} + +``` + +## Validations + +The `Command Generator` will validate the Optional Parameters received and will throw an error if the configuration is not valid. The validations are the following: + +- The Optional Parameters must not have duplicated names defined. +- The Optional Parameters name would be defined in the `tOptionalParamsName` type. + + +Another validations will be provided in development time and will be provided by the types added to the project. You can find the types definitions in the `types` file. + + +## Command Generator + +To use the command generator we need to import the class and create a new instance of the class. The class will receive the OS Definitions and the Optional Parameters as parameters. + +```ts +import { CommandGenerator } from 'path/command-generator'; + +// Commange Generator interface/contract + +export interface ICommandGenerator extends ICommandGeneratorMethods { + osDefinitions: IOSDefinition[]; + wazuhVersion: string; +} + +export interface ICommandGeneratorMethods { + selectOS(params: IOperationSystem): void; + addOptionalParams(props: IOptionalParameters): void; + getInstallCommand(): string; + getStartCommand(): string; + getUrlPackage(): string; + getAllCommands(): ICommandsResponse; +} + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +``` + +When the class is created the definitions provided will be validated and if the configuration is not valid an error will be thrown. The errors were mentioned in the configurations `Validations` section. + +### Get install command + +To generate the install command we need to call the `getInstallComand` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested command. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', + extension: 'rpm' +}); + +// get install command +const installCommand = commandGenerator.getInstallCommand(); + +``` + +The `Command Generator` will search the OS provided and search in the OS Definitions and will process the command using the `installCommand` function defined in the OS Definition. If the OS is not found an error will be thrown. +If the `getInstallCommand` but the OS is not selected an error will be thrown. + +## Get start command + +To generate the install command we need to call the `getStartCommand` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested command. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', + extension: 'rpm' +}); + +// get start command +const installCommand = commandGenerator.getStartCommand(); + +``` + +## Get url package + +To generate the install command we need to call the `getUrlPackage` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested command. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', + extension: 'rpm' +}); + +const urlPackage = commandGenerator.getUrlPackage(); + +``` + +## Get all commands + +To generate the install command we need to call the `getAllCommands` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested commands. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', + extension: 'rpm' +}); + +// specify to the command generator the optional parameters that we want to use +commandGenerator.addOptionalParams({ + server_address: 'server-ip', + agent_name: 'agent-name', + any_parameter: 'any-value' +}); + +// get all commands +const installCommand = commandGenerator.getAllCommands(); + +``` + +If we specify the optional parameters the `Command Generator` will process the commands and will add the optional parameters to the commands. The optional parameters processing will be only applied to the commands that have the optional parameters defined in the Optional Parameters Definitions. If the OS Definition does not have the optional parameters defined the `Command Generator` will ignore the optional parameters. + +### getAllComands output + +```ts + +export interface ICommandsResponse { + wazuhVersion: string; + os: string; + architecture: string; + extension: string; + url_package: string; + install_command: string; + start_command: string; + optionals: IOptionalParameters | object; +} + +``` \ No newline at end of file diff --git a/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts new file mode 100644 index 0000000000..71fd77ebd3 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts @@ -0,0 +1,398 @@ +import { CommandGenerator } from './command-generator'; +import { + IOSDefinition, + IOperationSystem, + IOptionalParameters, + tOptionalParams, +} from '../types'; +import { DuplicatedOSException, DuplicatedOSOptionException, NoOSSelectedException, WazuhVersionUndefinedException } from '../exceptions'; + +const mockedCommandValue = 'mocked command'; +const mockedCommandsResponse = jest.fn().mockReturnValue(mockedCommandValue); + +// Defined OS combinations +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +// Defined Optional Parameters + + +export type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + extension: 'deb', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + { + architecture: 'x64', + extension: 'msi', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, +]; + +const optionalParams: tOptionalParams = { + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: props => `${props.property}=${props.value}`, + }, + agent_name: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => `${props.property}=${props.value}`, + }, + protocol: { + property: 'WAZUH_MANAGER_PROTOCOL', + getParamCommand: props => `${props.property}=${props.value}`, + }, + agent_group: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: props => `${props.property}=${props.value}`, + }, + wazuh_password: { + property: 'WAZUH_PASSWORD', + getParamCommand: props => `${props.property}=${props.value}`, + }, +}; + +const optionalValues: IOptionalParameters = { + server_address: '', + agent_name: '', + protocol: '', + agent_group: '', + wazuh_password: '', +}; + +describe('Command Generator', () => { + it('should create an valid instance', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + expect(commandGenerator).toBeDefined(); + }); + + it('should return the install command for the os selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + commandGenerator.selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + commandGenerator.addOptionalParams(optionalValues); + const command = commandGenerator.getInstallCommand(); + expect(command).toBe(mockedCommandValue); + }); + + it('should return the start command for the os selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + commandGenerator.selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + commandGenerator.addOptionalParams(optionalValues); + const command = commandGenerator.getStartCommand(); + expect(command).toBe(mockedCommandValue); + }); + + it('should return all the commands for the os selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + commandGenerator.selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + commandGenerator.addOptionalParams(optionalValues); + const commands = commandGenerator.getAllCommands(); + expect(commands).toEqual({ + os: 'linux', + architecture: 'x64', + extension: 'deb', + wazuhVersion: '4.4', + install_command: mockedCommandValue, + start_command: mockedCommandValue, + url_package: mockedCommandValue, + optionals: {}, + }); + }); + + it('should return commands with the filled optional params', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + + const selectedOs: tOperatingSystem = { + name: 'linux', + architecture: 'x64', + extension: 'deb', + }; + commandGenerator.selectOS(selectedOs); + + const optionalValues = { + server_address: '10.10.10.121', + agent_name: 'agent1', + protocol: 'tcp', + agent_group: '', + wazuh_password: '123456', + }; + commandGenerator.addOptionalParams(optionalValues); + + const commands = commandGenerator.getAllCommands(); + expect(commands).toEqual({ + os: selectedOs.name, + architecture: selectedOs.architecture, + extension: selectedOs.extension, + wazuhVersion: '4.4', + install_command: mockedCommandValue, + start_command: mockedCommandValue, + url_package: mockedCommandValue, + optionals: { + server_address: optionalParams.server_address.getParamCommand({ + property: optionalParams.server_address.property, + value: optionalValues.server_address, + name: 'server_address', + }), + agent_name: optionalParams.agent_name.getParamCommand({ + property: optionalParams.agent_name.property, + value: optionalValues.agent_name, + name: 'agent_name', + }), + protocol: optionalParams.protocol.getParamCommand({ + property: optionalParams.protocol.property, + value: optionalValues.protocol, + name: 'protocol', + }), + wazuh_password: optionalParams.wazuh_password.getParamCommand({ + property: optionalParams.wazuh_password.property, + value: optionalValues.wazuh_password, + name: 'wazuh_password', + }), + }, + }); + }); + + it('should return an ERROR when the os definitions received has a os with options duplicated', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + extension: 'deb', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + { + architecture: 'x64', + extension: 'deb', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, + ]; + + try { + new CommandGenerator(osDefinitions, optionalParams, '4.4'); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(DuplicatedOSOptionException); + } + }); + + it('should return an ERROR when the os definitions received has a os with options duplicated', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + extension: 'deb', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, + { + name: 'linux', + options: [ + { + architecture: 'x64', + extension: 'deb', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, + ]; + + try { + new CommandGenerator(osDefinitions, optionalParams, '4.4'); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(DuplicatedOSException); + } + }); + + it('should return an ERROR when we want to get commands and the os is not selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + try { + commandGenerator.getAllCommands(); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(NoOSSelectedException); + } + }); + + it('should return an ERROR when we want to get the install command and the os is not selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + try { + commandGenerator.getInstallCommand(); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(NoOSSelectedException); + } + }); + + it('should return an ERROR when we want to get the start command and the os is not selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + try { + commandGenerator.getStartCommand(); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(NoOSSelectedException); + } + }); + + it('should return an ERROR when receive an empty version', () => { + try { + new CommandGenerator(osDefinitions, optionalParams, ''); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(WazuhVersionUndefinedException); + } + }); + + it('should receives the solved optional params when the install command is called', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + + const selectedOs: tOperatingSystem = { + name: 'linux', + architecture: 'x64', + extension: 'deb', + }; + commandGenerator.selectOS(selectedOs); + + const optionalValues = { + server_address: 'wazuh-ip', + }; + + commandGenerator.addOptionalParams(optionalValues as IOptionalParameters); + commandGenerator.getInstallCommand(); + expect(mockedCommandsResponse).toHaveBeenCalledWith( + expect.objectContaining({ + optionals: { + server_address: optionalParams.server_address.getParamCommand({ + property: optionalParams.server_address.property, + value: optionalValues.server_address, + name: 'server_address', + }), + }, + }), + ); + }); + + it('should receives the solved optional params when the start command is called', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + + const selectedOs: tOperatingSystem = { + name: 'linux', + architecture: 'x64', + extension: 'deb', + }; + commandGenerator.selectOS(selectedOs); + + const optionalValues = { + server_address: 'wazuh-ip', + }; + + commandGenerator.addOptionalParams(optionalValues as IOptionalParameters); + commandGenerator.getStartCommand(); + expect(mockedCommandsResponse).toHaveBeenCalledWith( + expect.objectContaining({ + optionals: { + server_address: optionalParams.server_address.getParamCommand({ + property: optionalParams.server_address.property, + value: optionalValues.server_address, + name: 'server_address', + }), + }, + }), + ); + }); +}); \ No newline at end of file diff --git a/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts new file mode 100644 index 0000000000..8dd6b69889 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts @@ -0,0 +1,179 @@ +import { + ICommandsResponse, + IOSCommandsDefinition, + IOSDefinition, + IOperationSystem, + IOptionalParameters, + IOptionalParametersManager, + tOptionalParams, +} from '../types'; +import { ICommandGenerator } from '../types'; +import { + searchOSDefinitions, + validateOSDefinitionHasDuplicatedOptions, + validateOSDefinitionsDuplicated, +} from '../services/search-os-definitions.service'; +import { OptionalParametersManager } from '../optional-parameters-manager/optional-parameters-manager'; +import { NoArchitectureSelectedException, NoExtensionSelectedException, NoOSSelectedException, WazuhVersionUndefinedException } from '../exceptions'; +import { version } from '../../../../../../package.json'; + +export class CommandGenerator implements ICommandGenerator { + os: OS['name'] | null = null; + osDefinitionSelected: IOSCommandsDefinition | null = null; + optionalsManager: IOptionalParametersManager; + protected optionals: IOptionalParameters | object = {}; + constructor( + public osDefinitions: IOSDefinition[], + protected optionalParams: tOptionalParams, + public wazuhVersion: string = version, + ) { + // validate os definitions received + validateOSDefinitionsDuplicated(this.osDefinitions); + validateOSDefinitionHasDuplicatedOptions(this.osDefinitions); + if(wazuhVersion == ''){ + throw new WazuhVersionUndefinedException(); + } + this.optionalsManager = new OptionalParametersManager(optionalParams); + } + + /** + * This method selects the operating system to use based on the given parameters + * @param params - The operating system parameters to select + */ + selectOS(params: OS) { + try { + // Check if the selected operating system is valid + this.osDefinitionSelected = this.checkIfOSisValid(params); + // Set the selected operating system + this.os = params.name; + } catch (error) { + // If the selected operating system is not valid, reset the selected OS and OS definition + this.osDefinitionSelected = null; + this.os = null; + throw error; + } + } + + /** + * This method adds the optional parameters to use based on the given parameters + * @param props - The optional parameters to select + * @returns The selected optional parameters + */ + addOptionalParams(props: IOptionalParameters): void { + // Get all the optional parameters based on the given parameters + this.optionals = this.optionalsManager.getAllOptionalParams(props); + } + + /** + * This method checks if the selected operating system is valid + * @param params - The operating system parameters to check + * @returns The selected operating system definition + * @throws An error if the operating system is not valid + */ + private checkIfOSisValid(params: OS): IOSCommandsDefinition { + const { name, architecture, extension } = params; + if (!name) { + throw new NoOSSelectedException(); + } + if (!architecture) { + throw new NoArchitectureSelectedException(); + } + if (!extension) { + throw new NoExtensionSelectedException(); + } + + const option = searchOSDefinitions(this.osDefinitions, { + name, + architecture, + extension, + }); + return option; + } + + /** + * This method gets the URL package for the selected operating system + * @returns The URL package for the selected operating system + * @throws An error if the operating system is not selected + */ + getUrlPackage(): string { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + return this.osDefinitionSelected.urlPackage({ + wazuhVersion: this.wazuhVersion, + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + extension: this.osDefinitionSelected.extension as OS['extension'], + name: this.os as OS['name'], + }); + } + + /** + * This method gets the install command for the selected operating system + * @returns The install command for the selected operating system + * @throws An error if the operating system is not selected + */ + getInstallCommand(): string { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + + return this.osDefinitionSelected.installCommand({ + name: this.os as OS['name'], + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + extension: this.osDefinitionSelected.extension as OS['extension'], + urlPackage: this.getUrlPackage(), + wazuhVersion: this.wazuhVersion, + optionals: this.optionals as IOptionalParameters, + }); + } + + /** + * This method gets the start command for the selected operating system + * @returns The start command for the selected operating system + * @throws An error if the operating system is not selected + */ + getStartCommand(): string { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + + return this.osDefinitionSelected.startCommand({ + name: this.os as OS['name'], + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + extension: this.osDefinitionSelected.extension as OS['extension'], + wazuhVersion: this.wazuhVersion, + optionals: this.optionals as IOptionalParameters, + }); + } + + /** + * This method gets all the commands for the selected operating system + * @returns An object containing all the commands for the selected operating system + * @throws An error if the operating system is not selected + */ + getAllCommands(): ICommandsResponse { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + + return { + wazuhVersion: this.wazuhVersion, + os: this.os as OS['name'], + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + extension: this.osDefinitionSelected.extension as OS['extension'], + url_package: this.getUrlPackage(), + install_command: this.getInstallCommand(), + start_command: this.getStartCommand(), + optionals: this.optionals, + }; + } + + /** + * Returns the optional paramaters processed + * @returns optionals + */ + getOptionalParamsCommands(): IOptionalParameters | object { + return this.optionals; + } + +} diff --git a/public/controllers/register-agent/core/register-commands/exceptions/index.ts b/public/controllers/register-agent/core/register-commands/exceptions/index.ts new file mode 100644 index 0000000000..c84f513157 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/exceptions/index.ts @@ -0,0 +1,85 @@ +export class NoOptionFoundException extends Error { + constructor(osName: string, architecture: string, extension: string) { + super( + `No OS option found for "${osName}" "${architecture}" "${extension}".Please check the OS definitions."`, + ); + } +} + +export class NoOSOptionFoundException extends Error { + constructor(osName: string) { + super( + `No OS option found for "${osName}".Please check the OS definitions."`, + ); + } +} + +export class NoStartCommandDefinitionException extends Error { + constructor(osName: string, architecture: string, extension: string) { + super( + `No start command definition found for "${osName}" "${architecture}" "${extension}". Please check the OS definitions.`, + ); + } +} + +export class NoInstallCommandDefinitionException extends Error { + constructor(osName: string, architecture: string, extension: string) { + super( + `No install command definition found for "${osName}" "${architecture}" "${extension}". Please check the OS definitions.`, + ); + } +} + +export class NoPackageURLDefinitionException extends Error { + constructor(osName: string, architecture: string, extension: string) { + super( + `No package URL definition found for "${osName}" "${architecture}" "${extension}". Please check the OS definitions.`, + ); + } +} + +export class NoOptionalParamFoundException extends Error { + constructor(paramName: string) { + super( + `Optional parameter "${paramName}" not found. Please check the optional parameters definitions.`, + ); + } +} + +export class DuplicatedOSException extends Error { + constructor(osName: string) { + super(`Duplicate OS name found: ${osName}`); + } +} + +export class DuplicatedOSOptionException extends Error { + constructor(osName: string, architecture: string, extension: string) { + super( + `Duplicate OS option found for "${osName}" "${architecture}" "${extension}"`, + ); + } +} + +export class WazuhVersionUndefinedException extends Error { + constructor() { + super(`Wazuh version not defined`); + } +} + +export class NoOSSelectedException extends Error { + constructor() { + super(`OS not selected. Please select`); + } +} + +export class NoArchitectureSelectedException extends Error { + constructor() { + super(`Architecture not selected. Please select`); + } +} + +export class NoExtensionSelectedException extends Error { + constructor() { + super(`Extension not selected. Please select`); + } +} diff --git a/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts b/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts new file mode 100644 index 0000000000..af6bef5b15 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts @@ -0,0 +1,229 @@ +import { NoOptionalParamFoundException } from '../exceptions'; +import { + IOptionalParameters, + tOptionalParams, + tOptionalParamsCommandProps, +} from '../types'; +import { OptionalParametersManager } from './optional-parameters-manager'; + +type tOptionalParamsFieldname = + | 'server_address' + | 'protocol' + | 'agent_group' + | 'wazuh_password' + | 'another_valid_fieldname'; + +const returnOptionalParam = ( + props: tOptionalParamsCommandProps, +) => { + const { property, value } = props; + return `${property}=${value}`; +}; +const optionalParametersDefinition: tOptionalParams = + { + protocol: { + property: 'WAZUH_MANAGER_PROTOCOL', + getParamCommand: returnOptionalParam, + }, + agent_group: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: returnOptionalParam, + }, + wazuh_password: { + property: 'WAZUH_PASSWORD', + getParamCommand: returnOptionalParam, + }, + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: returnOptionalParam, + }, + another_valid_fieldname: { + property: 'WAZUH_ANOTHER_PROPERTY', + getParamCommand: returnOptionalParam, + }, + }; + +describe('Optional Parameters Manager', () => { + it('should create an instance successfully', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + expect(optParamManager).toBeDefined(); + }); + + it.each([ + ['server_address', '10.10.10.27'], + ['protocol', 'TCP'], + ['agent_group', 'group1'], + ['wazuh_password', '123456'], + ['another_valid_fieldname', 'another_valid_value'] + ])( + `should return the corresponding command for "%s" param with "%s" value`, + (name, value) => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const commandParam = optParamManager.getOptionalParam({ + name: name as tOptionalParamsFieldname, + value, + }); + const defs = + optionalParametersDefinition[ + name as keyof typeof optionalParametersDefinition + ]; + expect(commandParam).toBe( + defs.getParamCommand({ + property: defs.property, + value, + name: name as tOptionalParamsFieldname, + }), + ); + }, + ); + + it('should return ERROR when the param received is not defined in the params definition', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const invalidParam = 'invalid_optional_param'; + try { + // @ts-ignore + optParamManager.getOptionalParam({ name: invalidParam, value: 'value' }); + } catch (error) { + expect(error).toBeInstanceOf(NoOptionalParamFoundException); + } + }); + + it('should return the corresponding command for all the params', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues: IOptionalParameters = { + protocol: 'TCP', + agent_group: 'group1', + wazuh_password: '123456', + server_address: 'server', + another_valid_fieldname: 'another_valid_value', + }; + const resolvedParams = optParamManager.getAllOptionalParams(paramsValues); + expect(resolvedParams).toEqual({ + agent_group: optionalParametersDefinition.agent_group.getParamCommand({ + name: 'agent_group', + property: optionalParametersDefinition.agent_group.property, + value: paramsValues.agent_group, + }), + protocol: optionalParametersDefinition.protocol.getParamCommand({ + name: 'protocol', + property: optionalParametersDefinition.protocol.property, + value: paramsValues.protocol, + }), + server_address: + optionalParametersDefinition.server_address.getParamCommand({ + name: 'server_address', + property: optionalParametersDefinition.server_address.property, + value: paramsValues.server_address, + }), + wazuh_password: + optionalParametersDefinition.wazuh_password.getParamCommand({ + name: 'wazuh_password', + property: optionalParametersDefinition.wazuh_password.property, + value: paramsValues.wazuh_password, + }), + another_valid_fieldname: + optionalParametersDefinition.another_valid_fieldname.getParamCommand({ + name: 'another_valid_fieldname', + property: + optionalParametersDefinition.another_valid_fieldname.property, + value: paramsValues.another_valid_fieldname, + }), + } as IOptionalParameters); + }); + + it('should return the corresponse command for all the params with NOT empty values', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues: IOptionalParameters = { + protocol: 'TCP', + agent_group: 'group1', + wazuh_password: '123456', + server_address: 'server', + another_valid_fieldname: 'another_valid_value', + }; + + const resolvedParams = optParamManager.getAllOptionalParams(paramsValues); + expect(resolvedParams).toEqual({ + agent_group: optionalParametersDefinition.agent_group.getParamCommand({ + name: 'agent_group', + property: optionalParametersDefinition.agent_group.property, + value: paramsValues.agent_group, + }), + protocol: optionalParametersDefinition.protocol.getParamCommand({ + name: 'protocol', + property: optionalParametersDefinition.protocol.property, + value: paramsValues.protocol, + }), + server_address: + optionalParametersDefinition.server_address.getParamCommand({ + name: 'server_address', + property: optionalParametersDefinition.server_address.property, + value: paramsValues.server_address, + }), + wazuh_password: + optionalParametersDefinition.wazuh_password.getParamCommand({ + name: 'wazuh_password', + property: optionalParametersDefinition.wazuh_password.property, + value: paramsValues.wazuh_password, + }), + another_valid_fieldname: + optionalParametersDefinition.another_valid_fieldname.getParamCommand({ + name: 'another_valid_fieldname', + property: + optionalParametersDefinition.another_valid_fieldname.property, + value: paramsValues.another_valid_fieldname, + }), + } as IOptionalParameters); + }); + + it('should return ERROR when the param received is not defined in the params definition', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues = { + serverAddress: 'invalid server address property value', + }; + + try { + // @ts-ignore + optParamManager.getAllOptionalParams(paramsValues); + } catch (error) { + expect(error).toBeInstanceOf(NoOptionalParamFoundException); + } + }); + + it('should return empty object response when receive an empty params object', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues = {}; + // @ts-ignore + const optionals = optParamManager.getAllOptionalParams(paramsValues); + expect(optionals).toEqual({}); + }); + + it('should return empty object response when receive all the params values with empty string ("")', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues = { + server_address: '', + agent_name: '', + protocol: '', + agent_group: '', + wazuh_password: '', + }; + // @ts-ignore + const optionals = optParamManager.getAllOptionalParams(paramsValues); + expect(optionals).toEqual({}); + }); +}); diff --git a/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts b/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts new file mode 100644 index 0000000000..34b943f797 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts @@ -0,0 +1,49 @@ +import { NoOptionalParamFoundException } from '../exceptions'; +import { IOptionalParamInput, IOptionalParameters, IOptionalParametersManager, tOptionalParams } from '../types'; + +export class OptionalParametersManager implements IOptionalParametersManager { + constructor(private optionalParamsConfig: tOptionalParams) {} + + /** + * Returns the command string for a given optional parameter. + * @param props - An object containing the optional parameter name and value. + * @returns The command string for the given optional parameter. + * @throws NoOptionalParamFoundException if the given optional parameter name is not found in the configuration. + */ + getOptionalParam(props: IOptionalParamInput) { + const { value, name } = props; + if (!this.optionalParamsConfig[name]) { + throw new NoOptionalParamFoundException(name); + } + return this.optionalParamsConfig[name].getParamCommand({ + value, + property: this.optionalParamsConfig[name].property, + name + }); + } + + /** + * Returns an object containing the command strings for all optional parameters with non-empty values. + * @param paramsValues - An object containing the optional parameter names and values. + * @returns An object containing the command strings for all optional parameters with non-empty values. + * @throws NoOptionalParamFoundException if any of the given optional parameter names is not found in the configuration. + */ + getAllOptionalParams(paramsValues: IOptionalParameters){ + // get keys for only the optional params with values !== '' + const optionalParams = Object.keys(paramsValues).filter(key => paramsValues[key as keyof typeof paramsValues] !== '') as Array; + const resolvedOptionalParams: any = {}; + for(const param of optionalParams){ + if(!this.optionalParamsConfig[param]){ + throw new NoOptionalParamFoundException(param as string); + } + + const paramDef = this.optionalParamsConfig[param]; + resolvedOptionalParams[param as string] = paramDef.getParamCommand({ + name: param as Params, + value: paramsValues[param] as string, + property: paramDef.property + }) as string; + } + return resolvedOptionalParams; + } +} diff --git a/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts b/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts new file mode 100644 index 0000000000..733737e8c5 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts @@ -0,0 +1,118 @@ +import { getInstallCommandByOS } from './get-install-command.service'; +import { IOSCommandsDefinition, IOSDefinition, IOptionalParameters } from '../types'; +import { + NoInstallCommandDefinitionException, + NoPackageURLDefinitionException, + WazuhVersionUndefinedException, +} from '../exceptions'; + + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + + +export type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password' | 'another_optional_parameter'; + +const validOsDefinition: IOSCommandsDefinition = { + architecture: 'x64', + extension: 'deb', + installCommand: props => 'install command mocked', + startCommand: props => 'start command mocked', + urlPackage: props => 'https://package-url.com', +}; +describe('getInstallCommandByOS', () => { + it('should return the correct install command for each OS', () => { + const installCommand = getInstallCommandByOS( + validOsDefinition, + 'https://package-url.com', + '4.4', + 'linux', + ); + expect(installCommand).toBe('install command mocked'); + }); + + it('should return ERROR when the version is not received', () => { + try { + getInstallCommandByOS( + validOsDefinition, + 'https://package-url.com', + '', + 'linux', + ); + } catch (error) { + expect(error).toBeInstanceOf(WazuhVersionUndefinedException); + } + }); + it('should return ERROR when the OS has no install command', () => { + // @ts-ignore + const osDefinition: IOSCommandsDefinition = { + architecture: 'x64', + extension: 'deb', + startCommand: props => 'start command mocked', + urlPackage: props => 'https://package-url.com', + }; + try { + getInstallCommandByOS( + osDefinition, + 'https://package-url.com', + '4.4', + 'linux', + ); + } catch (error) { + expect(error).toBeInstanceOf(NoInstallCommandDefinitionException); + } + }); + it('should return ERROR when the OS has no package url', () => { + try { + getInstallCommandByOS(validOsDefinition, '', '4.4', 'linux'); + } catch (error) { + expect(error).toBeInstanceOf(NoPackageURLDefinitionException); + } + }); + + it('should return install command with optional parameters', () => { + const mockedInstall = jest.fn(); + const validOsDefinition: IOSCommandsDefinition = { + architecture: 'x64', + extension: 'deb', + installCommand: mockedInstall, + startCommand: props => 'start command mocked', + urlPackage: props => 'https://package-url.com', + }; + + const optionalParams: IOptionalParameters = { + agent_group: 'WAZUH_GROUP=agent_group', + agent_name: 'WAZUH_NAME=agent_name', + protocol: 'WAZUH_PROTOCOL=UDP', + server_address: 'WAZUH_MANAGER=server_address', + wazuh_password: 'WAZUH_PASSWORD=1231323', + another_optional_parameter: 'params value' + }; + + getInstallCommandByOS( + validOsDefinition, + 'https://package-url.com', + '4.4', + 'linux', + optionalParams + ); + expect(mockedInstall).toBeCalledTimes(1); + expect(mockedInstall).toBeCalledWith(expect.objectContaining({ optionals: optionalParams })); + }) +}); diff --git a/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts b/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts new file mode 100644 index 0000000000..d66e75c8c0 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts @@ -0,0 +1,38 @@ +import { NoInstallCommandDefinitionException, NoPackageURLDefinitionException, WazuhVersionUndefinedException } from "../exceptions"; +import { IOSCommandsDefinition, IOperationSystem, IOptionalParameters } from "../types"; + +/** + * Returns the installation command for a given operating system. + * @param {IOSCommandsDefinition} osDefinition - The definition of the operating system. + * @param {string} packageUrl - The URL of the package to install. + * @param {string} version - The version of Wazuh to install. + * @param {string} osName - The name of the operating system. + * @param {IOptionalParameters} [optionals] - Optional parameters to include in the command. + * @returns {string} The installation command for the given operating system. + * @throws {NoInstallCommandDefinitionException} If the installation command is not defined for the given operating system. + * @throws {NoPackageURLDefinitionException} If the package URL is not defined. + * @throws {WazuhVersionUndefinedException} If the Wazuh version is not defined. + */ +export function getInstallCommandByOS(osDefinition: IOSCommandsDefinition, packageUrl: string, version: string, osName: string, optionals?: IOptionalParameters) { + + if (!osDefinition.installCommand) { + throw new NoInstallCommandDefinitionException(osName, osDefinition.architecture, osDefinition.extension); + } + + if(!packageUrl || packageUrl === ''){ + throw new NoPackageURLDefinitionException(osName, osDefinition.architecture, osDefinition.extension); + } + + if(!version || version === ''){ + throw new WazuhVersionUndefinedException(); + } + + return osDefinition.installCommand({ + urlPackage: packageUrl, + wazuhVersion: version, + name: osName as OS['name'], + architecture: osDefinition.architecture, + extension: osDefinition.extension, + optionals, + }); +} \ No newline at end of file diff --git a/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts b/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts new file mode 100644 index 0000000000..f08b1f016d --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts @@ -0,0 +1,199 @@ +import { + NoOSOptionFoundException, + NoOptionFoundException, +} from '../exceptions'; +import { IOSDefinition } from '../types'; +import { + searchOSDefinitions, + validateOSDefinitionHasDuplicatedOptions, + validateOSDefinitionsDuplicated, +} from './search-os-definitions.service'; + +const mockedInstallCommand = (props: any) => 'install command mocked'; +const mockedStartCommand = (props: any) => 'start command mocked'; +const mockedUrlPackage = (props: any) => 'https://package-url.com'; + +type tOptionalParamsNames = 'optional1' | 'optional2'; + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +const validOSDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + { + name: 'windows', + options: [ + { + extension: 'msi', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, +]; + +describe('search OS definitions services', () => { + describe('searchOSDefinitions', () => { + it('should return the OS definition if the OS name is found', () => { + const result = searchOSDefinitions(validOSDefinitions, { + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + expect(result).toMatchObject(validOSDefinitions[0].options[0]); + }); + + it('should throw an error if the OS name is not found', () => { + expect(() => + searchOSDefinitions(validOSDefinitions, { + // @ts-ignore + name: 'invalid-os', + architecture: 'x64', + extension: 'deb', + }), + ).toThrow(NoOSOptionFoundException); + }); + + it('should throw an error if the OS name is found but the architecture is not found', () => { + expect(() => + searchOSDefinitions(validOSDefinitions, { + name: 'linux', + architecture: 'invalid-architecture', + extension: 'deb', + }), + ).toThrow(NoOptionFoundException); + }); + }); + + describe('validateOSDefinitionsDuplicated', () => { + it('should not throw an error if there are no duplicated OS definitions', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + { + name: 'windows', + options: [ + { + extension: 'msi', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + ]; + + expect(() => + validateOSDefinitionsDuplicated(osDefinitions), + ).not.toThrow(); + }); + + it('should throw an error if there are duplicated OS definitions', () => { + const osDefinition: IOSDefinition = { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: 'x64', + // @ts-ignore + packageManager: 'aix', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }; + const osDefinitions: IOSDefinition[] = [osDefinition, osDefinition]; + + expect(() => validateOSDefinitionsDuplicated(osDefinitions)).toThrow(); + }); + }); + + describe('validateOSDefinitionHasDuplicatedOptions', () => { + it('should not throw an error if there are no duplicated OS definitions with different options', () => { + expect(() => + validateOSDefinitionHasDuplicatedOptions(validOSDefinitions), + ).not.toThrow(); + }); + + it('should throw an error if there are duplicated OS definitions with different options', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + { + extension: 'deb', + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + ]; + + expect(() => + validateOSDefinitionHasDuplicatedOptions(osDefinitions), + ).toThrow(); + }); + }); +}); diff --git a/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts b/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts new file mode 100644 index 0000000000..fcd5509681 --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts @@ -0,0 +1,86 @@ +import { + DuplicatedOSException, + DuplicatedOSOptionException, + NoOSOptionFoundException, + NoOptionFoundException, +} from '../exceptions'; +import { IOSDefinition, IOperationSystem } from '../types'; + +/** + * Searches for the OS definition option that matches the given operation system parameters. + * Throws an exception if no matching option is found. + * + * @param osDefinitions - The list of OS definitions to search through. + * @param params - The operation system parameters to match against. + * @returns The matching OS definition option. + * @throws NoOSOptionFoundException - If no matching OS definition is found. + * @throws NoOptionFoundException - If no matching OS definition option is found. + */ +export function searchOSDefinitions( + osDefinitions: IOSDefinition[], + params: IOperationSystem, +){ + const { name, architecture, extension } = params; + + const osDefinition = osDefinitions.find(os => os.name === name); + if (!osDefinition) { + throw new NoOSOptionFoundException(name); + } + + const osDefinitionOption = osDefinition.options.find( + option => + option.architecture === architecture && option.extension === extension, + ); + + if (!osDefinitionOption) { + throw new NoOptionFoundException(name, architecture, extension); + } + + return osDefinitionOption; +}; + +/** + * Validates that there are no duplicated OS definitions in the given list. + * Throws an exception if a duplicated OS definition is found. + * + * @param osDefinitions - The list of OS definitions to validate. + * @throws DuplicatedOSException - If a duplicated OS definition is found. + */ +export function validateOSDefinitionsDuplicated( + osDefinitions: IOSDefinition[], +){ + const osNames = new Set(); + + for (const osDefinition of osDefinitions) { + if (osNames.has(osDefinition.name)) { + throw new DuplicatedOSException(osDefinition.name); + } + osNames.add(osDefinition.name); + } +}; + +/** + * Validates that there are no duplicated OS definition options in the given list. + * Throws an exception if a duplicated OS definition option is found. + * + * @param osDefinitions - The list of OS definitions to validate. + * @throws DuplicatedOSOptionException - If a duplicated OS definition option is found. + */ +export function validateOSDefinitionHasDuplicatedOptions( + osDefinitions: IOSDefinition[], +){ + for (const osDefinition of osDefinitions) { + const options = new Set(); + for (const option of osDefinition.options) { + let ext_arch_manager = `${option.extension}_${option.architecture}`; + if (options.has(ext_arch_manager)) { + throw new DuplicatedOSOptionException( + osDefinition.name, + option.extension, + option.architecture, + ); + } + options.add(ext_arch_manager); + } + } +}; diff --git a/public/controllers/register-agent/core/register-commands/types.ts b/public/controllers/register-agent/core/register-commands/types.ts new file mode 100644 index 0000000000..efd36433ff --- /dev/null +++ b/public/controllers/register-agent/core/register-commands/types.ts @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////// +/// Domain +///////////////////////////////////////////////////////// +export interface IOperationSystem { + name: string; + architecture: string; + extension: string; +} + +export type IOptionalParameters = { + [key in Params]: string; +}; + +/////////////////////////////////////////////////////////////////// +/// Operating system commands definitions +/////////////////////////////////////////////////////////////////// + +export interface IOSDefinition { + name: OS['name']; + options: IOSCommandsDefinition[]; +} + +interface IOptionalParamsWithValues { + optionals?: IOptionalParameters +} + + +type tOSEntryProps = IOSProps & IOptionalParamsWithValues; + +export interface IOSCommandsDefinition { + extension: OS['extension']; + architecture: OS['architecture']; + urlPackage: (props: tOSEntryProps) => string; + installCommand: (props: tOSEntryProps & { urlPackage: string }) => string; + startCommand: (props: tOSEntryProps) => string; +} + +export interface IOSProps extends IOperationSystem { + wazuhVersion: string; +} + +/////////////////////////////////////////////////////////////////// +//// Commands optional parameters +/////////////////////////////////////////////////////////////////// +interface IOptionalParamProps { + property: string; + value: string; +} + +export type tOptionalParamsCommandProps = IOptionalParamProps & { + name: T; +}; +export interface IOptionsParamConfig { + property: string; + getParamCommand: (props: tOptionalParamsCommandProps) => string; +} + +export type tOptionalParams = { + [key in T]: IOptionsParamConfig; +}; + +export interface IOptionalParamInput { + value: string; + name: T; +} +export interface IOptionalParametersManager { + getOptionalParam(props: IOptionalParamInput): string; + getAllOptionalParams(paramsValues: IOptionalParameters): object; +} + +/////////////////////////////////////////////////////////////////// +/// Command creator class +/////////////////////////////////////////////////////////////////// + +export type IOSInputs = IOperationSystem & IOptionalParameters; +export interface ICommandGenerator extends ICommandGeneratorMethods { + osDefinitions: IOSDefinition[]; + wazuhVersion: string; +} + +export interface ICommandGeneratorMethods { + selectOS(params: IOperationSystem): void; + addOptionalParams(props: IOptionalParameters): void; + getInstallCommand(): string; + getStartCommand(): string; + getUrlPackage(): string; + getAllCommands(): ICommandsResponse; +} +export interface ICommandsResponse { + wazuhVersion: string; + os: string; + architecture: string; + extension: string; + url_package: string; + install_command: string; + start_command: string; + optionals: IOptionalParameters | object; +} diff --git a/public/controllers/register-agent/hooks/README.md b/public/controllers/register-agent/hooks/README.md new file mode 100644 index 0000000000..0b06f1b93e --- /dev/null +++ b/public/controllers/register-agent/hooks/README.md @@ -0,0 +1,175 @@ +# Documentation + +- [useRegisterAgentCommand hook](#useregisteragentcommand-hook) +- [Advantages](#advantages) +- [Usage](#usage) +- [Types](#types) + - [Hook props](#hook-props) + - [Hook output](#hook-output) +- [Hook with Generic types](#hook-with-generic-types) + - [Operating systems types example](#operating-systems-types-example) + +## useRegisterAgentCommand hook + +This hook makes use of the `Command Generator class` to generate the commands to register agents in the manager and allows to use it in React components. + +## Advantages + +- Ease of use of the Command Generator class. +- The hook returns the methods envolved to create the register commands by the operating system and optionas specified. +- The commands generate are stored in the state of the hook and can be used in the component. + + +## Usage + +```ts + +import { useRegisterAgentCommands } from 'path/to/use-register-agent-commands'; + +import { OSdefintions, paramsDefinitions} from 'path/config/os-definitions'; + +/* + the props recived by the hook must implement types: + - OS: IOSDefinition[] + - optional parameters: tOptionalParams +*/ + +const { + selectOS, + setOptionalParams, + installCommand, + startCommand, + optionalParamsParsed + } = useRegisterAgentCommands(); + +// select OS depending on the specified OS defined in the hook configuration +selectOS({ + name: 'name-OS', + architecture: 'architecture-OS', + extension: 'extension-OS', +}) + +// add optionals params depending on the specified optional parameters in the hook configuration +setOptionalParams({ + field_1: 'value_1', + field_2: 'value_2', + ... +}) + +/** the commands and the optional params will be processed and stored in the hook state **/ + +// install command +console.log('install command for the selected OS with optionals params', installCommand); +// start command +console.log('start command for the selected OS with optionals params', startCommand); +// optionals params processed +console.log('optionals params processed', optionalParamsParsed); + +``` + +## Types + +### Hook props + +```ts + +export interface IOperationSystem { + name: string; + architecture: string; + extension: string; +} + +interface IUseRegisterCommandsProps { + osDefinitions: IOSDefinition[]; + optionalParamsDefinitions: tOptionalParams; +} +``` + +### Hook output + +```ts + +export interface IOperationSystem { + name: string; + architecture: string; + extension: string; +} + +interface IUseRegisterCommandsOutput { + selectOS: (params: OS) => void; + setOptionalParams: (params: IOptionalParameters) => void; + installCommand: string; + startCommand: string; + optionalParamsParsed: IOptionalParameters | {}; +} +``` + +## Hook with Generic types + +We can pass the types with the OS posibilities options and the optionals params defined. +And the hook will validate and show warning in compilation and development time. + +#### Operating systems types example + +```ts +// global types + +export interface IOptionsParamConfig { + property: string; + getParamCommand: (props: tOptionalParamsCommandProps) => string; +} + +export type tOptionalParams = { + [key in T]: IOptionsParamConfig; +}; + +export interface IOperationSystem { + name: string; + architecture: string; + extension: string; +} + +/// .... + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +import { OSdefintions, paramsDefinitions} from 'path/config/os-definitions'; + + +// pass it to the hook and it will use the types when we are selecting the OS +const { + selectOS, + setOptionalParams, + installCommand, + startCommand, + optionalParamsParsed + } = useRegisterAgentCommands(OSdefintions, paramsDefinitions); + +// when the options are not valid depending on the types defined, the IDE will show a warning +selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'rpm', +}) + +```` + diff --git a/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts b/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts new file mode 100644 index 0000000000..ab0f6727df --- /dev/null +++ b/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts @@ -0,0 +1,237 @@ +import React from 'react'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useRegisterAgentCommands } from './use-register-agent-commands'; +import { + IOSDefinition, + tOptionalParams, +} from '../core/register-commands/types'; + +type tOptionalParamsNames = 'optional1' | 'optional2'; + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; + extension: 'rpm' | 'deb'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; + extension: 'msi'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; + extension: 'pkg'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +const linuxDefinition: IOSDefinition = { + name: 'linux', + options: [ + { + extension: 'deb', + architecture: '32/64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.${props.extension}`, + installCommand: props => `sudo yum install -y ${props.urlPackage}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + extension: 'deb', + architecture: 'x64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}.${props.extension}`, + installCommand: props => + `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + ], +}; + +export const osCommandsDefinitions = [linuxDefinition]; + +/////////////////////////////////////////////////////////////////// +/// Optional parameters definitions +/////////////////////////////////////////////////////////////////// + +export const optionalParamsDefinitions: tOptionalParams = + { + optional1: { + property: 'WAZUH_MANAGER', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + }, + }, + optional2: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + }, + }, + }; + +describe('useRegisterAgentCommands hook', () => { + it('should return installCommand and startCommand null when the hook is initialized', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + expect(hook.result.current.installCommand).toBe(''); + expect(hook.result.current.startCommand).toBe(''); + }); + + it('should return ERROR when get installCommand and the OS received is NOT valid', () => { + const { + result: { + current: { selectOS }, + }, + } = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + try { + act(() => { + selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + }); + } catch (error) { + if (error instanceof Error) + expect(error.message).toContain('No OS option found for'); + } + }); + + it('should change the commands when the OS is selected successfully', async () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { selectOS } = hook.result.current; + const { result } = hook; + + const optionSelected = osCommandsDefinitions + .find(os => os.name === 'linux') + ?.options.find( + item => item.architecture === 'x64' && item.extension === 'deb', + ); + const spyInstall = jest.spyOn(optionSelected!, 'installCommand'); + const spyStart = jest.spyOn(optionSelected!, 'startCommand'); + + act(() => { + selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + }); + expect(result.current.installCommand).not.toBe(''); + expect(result.current.startCommand).not.toBe(''); + expect(spyInstall).toBeCalledTimes(1); + expect(spyStart).toBeCalledTimes(1); + }); + + it('should return commands empty when set optional params and OS is NOT selected', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { setOptionalParams } = hook.result.current; + + act(() => { + setOptionalParams({ + optional1: 'value 1', + optional2: 'value 2', + }); + }); + + expect(hook.result.current.installCommand).toBe(''); + expect(hook.result.current.startCommand).toBe(''); + }); + + it('should return optional params empty when optional params are not added', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { optionalParamsParsed } = hook.result.current; + expect(optionalParamsParsed).toEqual({}); + }); + + it('should return optional params when optional params are added', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { setOptionalParams } = hook.result.current; + const spy1 = jest.spyOn( + optionalParamsDefinitions.optional1, + 'getParamCommand', + ); + const spy2 = jest.spyOn( + optionalParamsDefinitions.optional2, + 'getParamCommand', + ); + act(() => { + setOptionalParams({ + optional1: 'value 1', + optional2: 'value 2', + }); + }); + + expect(spy1).toBeCalledTimes(1); + expect(spy2).toBeCalledTimes(1); + }); + + it('should update the commands when the OS is selected and optional params are added', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { selectOS, setOptionalParams } = hook.result.current; + const optionSelected = osCommandsDefinitions + .find(os => os.name === 'linux') + ?.options.find( + item => item.architecture === 'x64' && item.extension === 'deb', + ); + const spyInstall = jest.spyOn(optionSelected!, 'installCommand'); + const spyStart = jest.spyOn(optionSelected!, 'startCommand'); + + act(() => { + selectOS({ + name: 'linux', + architecture: 'x64', + extension: 'deb', + }); + + setOptionalParams({ + optional1: 'value 1', + optional2: 'value 2', + }); + }); + + expect(hook.result.current.installCommand).not.toBe(''); + expect(hook.result.current.startCommand).not.toBe(''); + expect(spyInstall).toBeCalledTimes(2); + expect(spyStart).toBeCalledTimes(2); + }); +}); diff --git a/public/controllers/register-agent/hooks/use-register-agent-commands.ts b/public/controllers/register-agent/hooks/use-register-agent-commands.ts new file mode 100644 index 0000000000..800c198039 --- /dev/null +++ b/public/controllers/register-agent/hooks/use-register-agent-commands.ts @@ -0,0 +1,109 @@ +import React, { useEffect, useState } from 'react'; +import { CommandGenerator } from '../core/register-commands/command-generator/command-generator'; +import { + IOSDefinition, + IOperationSystem, + IOptionalParameters, + tOptionalParams, +} from '../core/register-commands/types'; +import { version } from '../../../../package.json'; + +interface IUseRegisterCommandsProps { + osDefinitions: IOSDefinition[]; + optionalParamsDefinitions: tOptionalParams; +} + +interface IUseRegisterCommandsOutput { + selectOS: (params: OS) => void; + setOptionalParams: (params: IOptionalParameters) => void; + installCommand: string; + startCommand: string; + optionalParamsParsed: IOptionalParameters | {}; +} + + +/** + * Custom hook that generates install and start commands based on the selected OS and optional parameters. + * + * @template T - The type of the selected OS. + * @param {IUseRegisterCommandsProps} props - The properties to configure the command generator. + * @returns {IUseRegisterCommandsOutput} - An object containing the generated commands and methods to update the selected OS and optional parameters. + */ +export function useRegisterAgentCommands(props: IUseRegisterCommandsProps): IUseRegisterCommandsOutput { + const { osDefinitions, optionalParamsDefinitions } = props; + // command generator settings + const wazuhVersion = version; + const osCommands: IOSDefinition[] = osDefinitions as IOSDefinition[]; + const optionalParams: tOptionalParams = optionalParamsDefinitions as tOptionalParams; + const commandGenerator = new CommandGenerator( + osCommands, + optionalParams, + wazuhVersion, + ); + + const [osSelected, setOsSelected] = useState(null); + const [optionalParamsValues, setOptionalParamsValues] = useState< + IOptionalParameters| {} + >({}); + const [optionalParamsParsed, setOptionalParamsParsed] = useState | {}>({}); + const [installCommand, setInstallCommand] = useState(''); + const [startCommand, setStartCommand] = useState(''); + + + /** + * Generates the install and start commands based on the selected OS and optional parameters. + * If no OS is selected, the method returns early without generating any commands. + * The generated commands are then set as state variables for later use. + */ + const generateCommands = () => { + if (!osSelected) return; + if (osSelected) { + commandGenerator.selectOS(osSelected); + } + if (optionalParamsValues) { + commandGenerator.addOptionalParams( + optionalParamsValues as IOptionalParameters, + ); + } + const installCommand = commandGenerator.getInstallCommand(); + const startCommand = commandGenerator.getStartCommand(); + setInstallCommand(installCommand); + setStartCommand(startCommand); + } + + useEffect(() => { + generateCommands(); + }, [osSelected, optionalParamsValues]); + + + /** + * Sets the selected OS for the command generator and updates the state variables accordingly. + * + * @param {T} params - The selected OS to be set. + * @returns {void} + */ + const selectOS = (params: OS) => { + commandGenerator.selectOS(params); + setOsSelected(params); + }; + + /** + * Sets the optional parameters for the command generator and updates the state variables accordingly. + * + * @param {IOptionalParameters} params - The optional parameters to be set. + * @returns {void} + */ + const setOptionalParams = (params: IOptionalParameters): void => { + commandGenerator.addOptionalParams(params); + setOptionalParamsValues(params); + setOptionalParamsParsed(commandGenerator.getOptionalParamsCommands()); + }; + + return { + selectOS, + setOptionalParams, + installCommand, + startCommand, + optionalParamsParsed + } +}; From 9ada6baa2b1184bcb914c391211a0f5fe662f9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chantal=20Bel=C3=A9n=20kelm?= <99441266+chantal-kelm@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:23:21 -0300 Subject: [PATCH 06/46] Create reusable card for operating systems (#5462) * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * reuse of common form on the card * Card with logic * CheckboxGroup component logic update * CheckboxGroup component logic update * Adding card icons * update checkbox logic, styles, and card styles * clean code * clean code * gitignore Mac files * updating checkbox logic, styles, and card styles * Update os-card.scss * macos card update * undoing merging as it was causing checkboxes not to work * test * file ds_store * file ds_store * file ds_store * remove files DS_store * remove files DS_store --------- Co-authored-by: Maximiliano Ibarra Co-authored-by: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> --- .DS_Store | Bin 8196 -> 0 bytes .gitignore | 5 +- public/assets/images/icons/linux-icon.svg | 3 + public/assets/images/icons/mac-icon.svg | 4 ++ public/assets/images/icons/windows-icon.svg | 13 ++++ public/components/common/form/hooks.test.tsx | 46 ++++++++++--- public/components/common/form/hooks.tsx | 31 +++++---- public/components/common/form/index.tsx | 65 +++++++++++------- .../checkbox-group/checkbox-group.scss | 51 ++++++++++++++ .../checkbox-group/checkbox-group.test.tsx | 55 +++++++++++++++ .../checkbox-group/checkbox-group.tsx | 62 +++++++++++++++++ .../components/os-card/os-card.scss | 50 ++++++++++++++ .../components/os-card/os-card.test.tsx | 17 +++++ .../components/os-card/os-card.tsx | 56 +++++++++++++++ .../container/register-agent.scss | 1 - .../container/register-agent.test.tsx | 18 +++++ .../container/register-agent.tsx | 15 +++- .../utils/register-agent-data.tsx | 25 +++++++ 18 files changed, 467 insertions(+), 50 deletions(-) delete mode 100644 .DS_Store create mode 100644 public/assets/images/icons/linux-icon.svg create mode 100644 public/assets/images/icons/mac-icon.svg create mode 100644 public/assets/images/icons/windows-icon.svg create mode 100644 public/controllers/register-agent/components/checkbox-group/checkbox-group.scss create mode 100644 public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx create mode 100644 public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx create mode 100644 public/controllers/register-agent/components/os-card/os-card.scss create mode 100644 public/controllers/register-agent/components/os-card/os-card.test.tsx create mode 100644 public/controllers/register-agent/components/os-card/os-card.tsx create mode 100644 public/controllers/register-agent/container/register-agent.test.tsx create mode 100644 public/controllers/register-agent/utils/register-agent-data.tsx diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bd241bd50263ce7ea237ae2bf16dba59846221ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM-EPw`82!AJHdVK27l0-tq)6P3G1!4mRVhzPLiLY}m zq#1qYHc&uMv`bCeqZh!H0^6K04j2cF1I7X4fN|h|Z~*UYPS%9?zPf5k?dZY7d;jFljjPTRu97qQJeZJMJsLH|i|j+>XM58}d`z%7P;G0PcbI`;Lw5geMht+A_D1`GKeDBDa@@U8^O0v2c9!=d&J;R_G(j z;ws=3^DVVv=Fp-eSAj!)v^q)0t0%21LpMZ^0V3{Z$1PV+np>rPIJreT@TrL&3hP*~ zqiIDAWA7r8o{shikqq+w2=+Cts%Vvuu@%_T%{05lGmANXiP5#_fZkj;GQPvaLdvf) z`oR4{XIy6ECU}(L&Z7rzcMtvSqMsHVxITW|Zy9A7eV z@aEYQxQaDFyiWU*=9-<#C>Q9H98E~~W$ZE9h@lRE{p{$ZU!PNcRj;&)kLOKJ!LFqh zm{gOD15@e1tcrT625e6L{y&x8n4paV)8c?A?X-6qi1zXaY(8+_Yum^lkU24LtWZi& m$n7{#ZpVRF|1d<|29-5+jbnv4gY@q|1eo`~G~N_b?Z6+4d}LSv diff --git a/.gitignore b/.gitignore index 5ca1c192ff..2cc137cef8 100644 --- a/.gitignore +++ b/.gitignore @@ -81,4 +81,7 @@ cypress/report/ cypress/cookies.json # Customization plugin assets -public/assets/custom/* \ No newline at end of file +public/assets/custom/* + +# Mac files +.DS_Store \ No newline at end of file diff --git a/public/assets/images/icons/linux-icon.svg b/public/assets/images/icons/linux-icon.svg new file mode 100644 index 0000000000..85613a6872 --- /dev/null +++ b/public/assets/images/icons/linux-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/icons/mac-icon.svg b/public/assets/images/icons/mac-icon.svg new file mode 100644 index 0000000000..dbfed2e61f --- /dev/null +++ b/public/assets/images/icons/mac-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/images/icons/windows-icon.svg b/public/assets/images/icons/windows-icon.svg new file mode 100644 index 0000000000..5ef43e4d08 --- /dev/null +++ b/public/assets/images/icons/windows-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/components/common/form/hooks.test.tsx b/public/components/common/form/hooks.test.tsx index c96ebe60b8..283c4809bf 100644 --- a/public/components/common/form/hooks.test.tsx +++ b/public/components/common/form/hooks.test.tsx @@ -1,4 +1,7 @@ +import { fireEvent, render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import { renderHook, act } from '@testing-library/react-hooks'; +import React, { useState } from 'react'; import { useForm } from './hooks'; import { FormConfiguration, IInputForm } from './types'; @@ -174,16 +177,43 @@ describe('[hook] useForm', () => { }); it('[hook] useForm. Verify the hook behavior when receives a custom field type', async () => { - const formFields: FormConfiguration = { - customField: { - type: 'custom', - initialValue: 'default value', - component: (props:IInputForm) => (<>any component), - }, + const CustomComponent = (props: any) => { + const { onChange, field, initialValue } = props; + const [value, setValue] = useState(initialValue || ''); + + const handleOnChange = (e: any) => { + setValue(e.target.value); + onChange(e); }; + return ( + <> + {field} + + + ); + }; + + const formFields: FormConfiguration = { + customField: { + type: 'custom', + initialValue: 'default value', + component: props => CustomComponent(props), + }, + }; + const { result } = renderHook(() => useForm(formFields)); - expect(result.current.fields.customField.component).toBeInstanceOf(Function); - expect(result.current.fields.customField.type).toBe('custom'); + const { container, getByRole } = render( + , + ); + + expect(container).toBeInTheDocument(); + const input = getByRole('textbox'); + expect(input).toHaveValue('default value'); + fireEvent.change(input, { target: { value: 'new value' } }); + expect(result.current.fields.customField.component).toBeInstanceOf( + Function, + ); + expect(result.current.fields.customField.value).toBe('new value'); }); }); diff --git a/public/components/common/form/hooks.tsx b/public/components/common/form/hooks.tsx index 1d2f09e020..be7e1a8fa8 100644 --- a/public/components/common/form/hooks.tsx +++ b/public/components/common/form/hooks.tsx @@ -9,35 +9,36 @@ import { UseFormReturn, } from './types'; - interface IgetValueFromEventType { [key: string]: (event: any) => any; } -const getValueFromEventType: IgetValueFromEventType = { - [EpluginSettingType.switch]: (event: any) => event.target.checked, - [EpluginSettingType.editor]: (value: any) => value, - custom: (event:any) => event.target, - default: (event: any) => event.target.value, -}; - /** * Returns the value of the event according to the type of field * When the type is not found, it returns the value defined in the default key - * - * @param event - * @param type + * + * @param event + * @param type * @returns event value */ function getValueFromEvent( event: any, - type: SettingTypes | CustomSettingType | string, + type: SettingTypes | CustomSettingType, ): any { - - return getValueFromEventType.hasOwnProperty(type) ? getValueFromEventType[type](event) : getValueFromEventType.default(event) + return (getValueFromEventType[type] || getValueFromEventType.default)(event); } - +const getValueFromEventType: IgetValueFromEventType = { + [EpluginSettingType.switch]: (event: any) => event.target.checked, + [EpluginSettingType.editor]: (value: any) => value, + [EpluginSettingType.filepicker]: (value: any) => value, + [EpluginSettingType.select]: (event: any) => event.target.value, + [EpluginSettingType.text]: (event: any) => event.target.value, + [EpluginSettingType.textarea]: (event: any) => event.target.value, + [EpluginSettingType.number]: (event: any) => event.target.value, + custom: (event: any) => event.target.value, + default: (event: any) => event.target.value, +}; export const useForm = (fields: FormConfiguration): UseFormReturn => { const [formFields, setFormFields] = useState<{ diff --git a/public/components/common/form/index.tsx b/public/components/common/form/index.tsx index 0c3fa8a681..0f267589c1 100644 --- a/public/components/common/form/index.tsx +++ b/public/components/common/form/index.tsx @@ -7,6 +7,7 @@ import { InputFormSwitch } from './input_switch'; import { InputFormFilePicker } from './input_filepicker'; import { InputFormTextArea } from './input_text_area'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { OsCard } from '../../../controllers/register-agent/components/os-card/os-card'; import { SettingTypes } from './types'; interface InputFormProps { @@ -15,10 +16,18 @@ interface InputFormProps { onChange: (event: React.ChangeEvent) => void; error?: string; label?: string; - header?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); - footer?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); - preInput?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); - postInput?: React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); + header?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); + footer?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); + preInput?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); + postInput?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); } interface InputFormComponentProps extends InputFormProps { @@ -37,12 +46,13 @@ export const InputForm = ({ postInput, ...rest }: InputFormComponentProps) => { + const ComponentInput = Input[ + type as keyof typeof Input + ] as React.ComponentType; - const ComponentInput = Input[type as keyof typeof Input] as React.ComponentType; - - if(!ComponentInput){ + if (!ComponentInput) { return null; - }; + } const isInvalid = Boolean(error); @@ -55,23 +65,29 @@ export const InputForm = ({ /> ); - return label - ? ( - - <> - {typeof header === 'function' ? header({value, error}) : header} - - {typeof preInput === 'function' ? preInput({value, error}) : preInput} - - {input} - - {typeof postInput === 'function' ? postInput({value, error}) : postInput} - - {typeof footer === 'function' ? footer({value, error}) : footer} - - ) - : input; + if (type === 'custom') { + return ; + } + return label ? ( + + <> + {typeof header === 'function' ? header({ value, error }) : header} + + {typeof preInput === 'function' + ? preInput({ value, error }) + : preInput} + {input} + {typeof postInput === 'function' + ? postInput({ value, error }) + : postInput} + + {typeof footer === 'function' ? footer({ value, error }) : footer} + + + ) : ( + input + ); }; const Input = { @@ -82,4 +98,5 @@ const Input = { select: InputFormSelect, text: InputFormText, textarea: InputFormTextArea, + custom: OsCard, }; diff --git a/public/controllers/register-agent/components/checkbox-group/checkbox-group.scss b/public/controllers/register-agent/components/checkbox-group/checkbox-group.scss new file mode 100644 index 0000000000..89e0e58513 --- /dev/null +++ b/public/controllers/register-agent/components/checkbox-group/checkbox-group.scss @@ -0,0 +1,51 @@ +.checkbox-group-container { + display: flex; + flex-wrap: wrap; + margin-top: 26px; + margin-bottom: 11px; +} + +.checkbox-item { + width: 50%; + display: flex; + flex-direction: row-reverse; + justify-content: center; +} + +.checkbox-group-container.single-architecture { + margin-top: 44px; + display: flex; + justify-content: center; +} + +.checkbox-group-container.double-architecture { + margin-top: 24px; + display: flex; + flex-direction: column; + .checkbox-item:first-child { + margin-bottom: 13px; + } + .checkbox-item { + display: flex; + flex-direction: row-reverse; + justify-content: start; + } +} + +.architecture-label { + margin-left: 8px; + font-style: normal; + font-weight: 400; + font-size: 14px; + color: #343741; +} + +.first-card-four-items { + .checkbox-item:nth-child(n + 3) { + padding-top: 16px; + } +} + +.first-of-row { + padding-right: 17px; +} diff --git a/public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx b/public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx new file mode 100644 index 0000000000..c2fa3c3fb0 --- /dev/null +++ b/public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { CheckboxGroupComponent } from './checkbox-group'; + +describe('CheckboxGroupComponent', () => { + const data = ['Option 1', 'Option 2', 'Option 3']; + const cardIndex = 0; + const selectedOption = 'option-0-0'; + const onOptionChange = jest.fn(); + + test('renders checkbox items with correct labels', () => { + render( + , + ); + + const checkboxItems = screen.getAllByRole('radio'); + expect(checkboxItems).toHaveLength(data.length); + + expect(checkboxItems[0]).toHaveAttribute('id', 'option-0-0'); + expect(checkboxItems[1]).toHaveAttribute('id', 'option-0-1'); + expect(checkboxItems[2]).toHaveAttribute('id', 'option-0-2'); + + expect(checkboxItems[0]).toBeChecked(); + expect(checkboxItems[1]).not.toBeChecked(); + expect(checkboxItems[2]).not.toBeChecked(); + + expect(screen.getByText('Option 1')).toBeInTheDocument(); + expect(screen.getByText('Option 2')).toBeInTheDocument(); + expect(screen.getByText('Option 3')).toBeInTheDocument(); + }); + + test('calls onOptionChange when a checkbox is selected', () => { + render( + , + ); + + const checkboxItems = screen.getAllByRole('radio'); + + fireEvent.click(checkboxItems[1]); + + expect(onOptionChange).toHaveBeenCalledTimes(1); + expect(onOptionChange).toHaveBeenCalledWith('option-0-1'); + }); +}); diff --git a/public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx b/public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx new file mode 100644 index 0000000000..160bcfd66a --- /dev/null +++ b/public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { EuiRadioGroup } from '@elastic/eui'; +import './checkbox-group.scss'; + +interface RegisterAgentData { + icon: string; + title: string; + hr: boolean; + architecture: string[]; +} + +interface Props { + data: string[]; + cardIndex: number; + selectedOption: string | undefined; + onOptionChange: (optionId: string) => void; + onChange: (id: string) => void; +} + +const CheckboxGroupComponent: React.FC = ({ + data, + cardIndex, + selectedOption, + onOptionChange, +}) => { + const handleOptionChange = (optionId: string) => { + onOptionChange(optionId); + }; + + const isSingleArchitecture = data.length === 1; + const isDoubleArchitecture = data.length === 2; + const isFirstCardWithFourItems = cardIndex === 0 && data.length === 4; + + return ( +
+ {data.map((arch, idx) => ( +
+ {arch} + handleOptionChange(id)} + /> +
+ ))} +
+ ); +}; + +export { CheckboxGroupComponent }; +export type { RegisterAgentData }; diff --git a/public/controllers/register-agent/components/os-card/os-card.scss b/public/controllers/register-agent/components/os-card/os-card.scss new file mode 100644 index 0000000000..d4d3b41649 --- /dev/null +++ b/public/controllers/register-agent/components/os-card/os-card.scss @@ -0,0 +1,50 @@ +.card { + height: 183px; +} + +.cardTitle { + display: flex; + align-items: center; + margin-top: 28px; +} + +.cardIcon { + margin-right: 10px; +} + +.euiCard__content .euiCard__titleButton { + text-decoration: none !important; +} + +.cardText { + font-style: normal; + font-weight: 700; + font-size: 18px; + display: flex; + align-items: center; + text-align: center; + letter-spacing: 0.6px; + color: #343741; +} + +.hr { + border: 1px solid #d3dae6; +} + +.cardContent { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.checkboxGroupContainer { + flex-basis: 50%; +} + +.architectureItem { + margin-bottom: 8px; +} + +.last-card { + margin-right: 63px; +} diff --git a/public/controllers/register-agent/components/os-card/os-card.test.tsx b/public/controllers/register-agent/components/os-card/os-card.test.tsx new file mode 100644 index 0000000000..da1a61c120 --- /dev/null +++ b/public/controllers/register-agent/components/os-card/os-card.test.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { OsCard } from './os-card'; + +describe('OsCard', () => { + test('renders three cards with different titles', () => { + render(); + + const cardTitles = screen.getAllByTestId('card-title'); + expect(cardTitles).toHaveLength(3); + + expect(cardTitles[0]).toHaveTextContent('LINUX'); + expect(cardTitles[1]).toHaveTextContent('WINDOWS'); + expect(cardTitles[2]).toHaveTextContent('macOS'); + }); +}); diff --git a/public/controllers/register-agent/components/os-card/os-card.tsx b/public/controllers/register-agent/components/os-card/os-card.tsx new file mode 100644 index 0000000000..d9c0f2f00b --- /dev/null +++ b/public/controllers/register-agent/components/os-card/os-card.tsx @@ -0,0 +1,56 @@ +import React, { useState } from 'react'; +import { + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSpacer, + EuiCheckbox, +} from '@elastic/eui'; +import { REGISTER_AGENT_DATA } from '../../utils/register-agent-data'; +import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; +import './os-card.scss'; + +export const OsCard = () => { + const [selectedOption, setSelectedOption] = useState( + undefined, + ); + + const handleOptionChange = (optionId: string) => { + setSelectedOption(optionId); + }; + + return ( +
+ + {REGISTER_AGENT_DATA.map((data, index) => ( + + + Icon + {data.title} +
+ } + display='plain' + hasBorder + onClick={() => {}} + > + {data.hr &&
} + {/* */} + + + + + ))} + +
+ ); +}; diff --git a/public/controllers/register-agent/container/register-agent.scss b/public/controllers/register-agent/container/register-agent.scss index 6f5dc8ed6c..c127bd0e9c 100644 --- a/public/controllers/register-agent/container/register-agent.scss +++ b/public/controllers/register-agent/container/register-agent.scss @@ -12,7 +12,6 @@ .title { margin-top: 51px; margin-bottom: 51px; - font-family: 'Inter'; font-style: normal; font-weight: 400; font-size: 30px; diff --git a/public/controllers/register-agent/container/register-agent.test.tsx b/public/controllers/register-agent/container/register-agent.test.tsx new file mode 100644 index 0000000000..061960686a --- /dev/null +++ b/public/controllers/register-agent/container/register-agent.test.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { RegisterAgent } from './register-agent'; + +describe('RegisterAgent', () => { + test('renders the component', () => { + render(); + + // Verifica que el título esté presente + const titleElement = screen.getByText('Deploy new agent'); + expect(titleElement).toBeInTheDocument(); + + // Verifica que el componente InputForm esté presente + const component = screen.getByTestId('os-card'); + expect(component).toBeInTheDocument(); + }); +}); diff --git a/public/controllers/register-agent/container/register-agent.tsx b/public/controllers/register-agent/container/register-agent.tsx index 87a780b0e7..5617bf3606 100644 --- a/public/controllers/register-agent/container/register-agent.tsx +++ b/public/controllers/register-agent/container/register-agent.tsx @@ -1,10 +1,23 @@ -import React from 'react'; +import React, { ChangeEvent } from 'react'; +import { InputForm } from '../../../components/common/form'; import './register-agent.scss'; export const RegisterAgent: React.FC = () => { + const handleChange = (event: ChangeEvent) => { + // ver + }; + return (
Deploy new agent
+
); }; diff --git a/public/controllers/register-agent/utils/register-agent-data.tsx b/public/controllers/register-agent/utils/register-agent-data.tsx new file mode 100644 index 0000000000..a32b9464b0 --- /dev/null +++ b/public/controllers/register-agent/utils/register-agent-data.tsx @@ -0,0 +1,25 @@ +import { RegisterAgentData } from '../components/checkbox-group/checkbox-group'; +import LinuxIcon from '../../../../public/assets/images/icons/linux-icon.svg'; +import WindowsIcon from '../../../../public/assets/images/icons/windows-icon.svg'; +import MacIcon from '../../../../public/assets/images/icons/mac-icon.svg'; + +export const REGISTER_AGENT_DATA: RegisterAgentData[] = [ + { + icon: LinuxIcon, + title: 'LINUX', + hr: true, + architecture: ['RPM amd64', 'RPM aarch64', 'DEB amd64', 'DEB aarch64'], + }, + { + icon: WindowsIcon, + title: 'WINDOWS', + hr: true, + architecture: ['MSI 32/64'], + }, + { + icon: MacIcon, + title: 'macOS', + hr: true, + architecture: ['Intel', 'Apple Silicon'], + }, +]; From 10a9d9dbe045ccc18078b64fed10c1914039fde6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chantal=20Bel=C3=A9n=20kelm?= <99441266+chantal-kelm@users.noreply.github.com> Date: Fri, 23 Jun 2023 09:53:25 -0300 Subject: [PATCH 07/46] 5518 inputs logic server address name password and group (#5554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * reuse of common form on the card * Card with logic * CheckboxGroup component logic update * CheckboxGroup component logic update * Adding card icons * update checkbox logic, styles, and card styles * clean code * clean code * gitignore Mac files * updating checkbox logic, styles, and card styles * step component * Passing interfaces to a separate file, updating styles, and component logic * Update interfaces and clean up code * update of folder structure and step logic * tcp, udp, protocols, password, groups, logics * input logic server address name password groups and styles * group input logic * oscards input logic * oscards input logic * styles * regex * styles and settings * styles * various adjustments * cleaning up code and changing some styles * cleaning up code * cleaning code * update password * gitignore * gitignore * correcting validation text in input agent name * correcting validation text in input agent name * corrección de validación de input de nombre del agente * cleaning code * cleaning code * regex that differentiates between FQDN and IP * Use of PLUGIN_VERSION_SHORT * Use of PLUGIN_VERSION_SHORT * link * Revert "Merge branch '4205-redesign-add-agent-page' into 5518-inputs-logic-server-address-name-password-and-group" This reverts commit a4c6fb5d24a482e80f9595a879d141ff2d7fa5bb, reversing changes made to 5a0d2cb0e71972eb8f68b16f035ebc977220379f. * link and revert * characteres valid * correction of styles when bringing changes from parent branch * change tooltip to popover * moving validations to a separate file with their tests * corrections and cleaning of comments * camel case * change in function * type * remove type * fullWidth * type * change * conditional * change label a to Euilink * change label a to Euilink * conditional * delete usePrevious * delete usePrevious * deleted files ds store * test correction and placeholder * show architecture instead of id * removing console css warnings * fixed regex fqdn * fixed regex fqdn * data * changelog * changelog --------- Co-authored-by: Maximiliano Ibarra Co-authored-by: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> --- CHANGELOG.md | 10 + public/components/common/form/hooks.tsx | 2 +- public/components/common/form/index.tsx | 9 +- .../components/common/form/input_select.tsx | 30 ++- public/components/common/form/input_text.tsx | 27 +- public/components/common/form/types.ts | 3 +- public/controllers/agent/index.js | 2 +- .../steps/wz-manager-address.tsx | 6 +- .../components/os-card/os-card.tsx | 56 ----- .../checkbox-group/checkbox-group.scss | 19 +- .../checkbox-group/checkbox-group.test.tsx | 8 +- .../checkbox-group/checkbox-group.tsx | 19 +- .../{ => step-one}/os-card/os-card.scss | 4 + .../{ => step-one}/os-card/os-card.test.tsx | 2 +- .../components/step-one/os-card/os-card.tsx | 73 ++++++ .../components/steps-three/group-input.scss | 8 + .../components/steps-three/group-input.tsx | 96 +++++++ .../container/register-agent.tsx | 23 -- .../register-agent}/register-agent.scss | 6 +- .../register-agent}/register-agent.test.tsx | 4 +- .../register-agent/register-agent.tsx | 238 ++++++++++++++++++ .../containers/steps/steps.scss | 56 +++++ .../register-agent/containers/steps/steps.tsx | 234 +++++++++++++++++ public/controllers/register-agent/index.tsx | 2 +- .../register-agent/interfaces/types.ts | 16 ++ .../services/register-agent-services.tsx | 238 ++++++++++++++++++ .../utils/register-agent-data.tsx | 20 +- .../register-agent/utils/validations.test.tsx | 68 +++++ .../register-agent/utils/validations.tsx | 50 ++++ 29 files changed, 1185 insertions(+), 144 deletions(-) delete mode 100644 public/controllers/register-agent/components/os-card/os-card.tsx rename public/controllers/register-agent/components/{ => step-one}/checkbox-group/checkbox-group.scss (77%) rename public/controllers/register-agent/components/{ => step-one}/checkbox-group/checkbox-group.test.tsx (87%) rename public/controllers/register-agent/components/{ => step-one}/checkbox-group/checkbox-group.tsx (77%) rename public/controllers/register-agent/components/{ => step-one}/os-card/os-card.scss (94%) rename public/controllers/register-agent/components/{ => step-one}/os-card/os-card.test.tsx (90%) create mode 100644 public/controllers/register-agent/components/step-one/os-card/os-card.tsx create mode 100644 public/controllers/register-agent/components/steps-three/group-input.scss create mode 100644 public/controllers/register-agent/components/steps-three/group-input.tsx delete mode 100644 public/controllers/register-agent/container/register-agent.tsx rename public/controllers/register-agent/{container => containers/register-agent}/register-agent.scss (84%) rename public/controllers/register-agent/{container => containers/register-agent}/register-agent.test.tsx (85%) create mode 100644 public/controllers/register-agent/containers/register-agent/register-agent.tsx create mode 100644 public/controllers/register-agent/containers/steps/steps.scss create mode 100644 public/controllers/register-agent/containers/steps/steps.tsx create mode 100644 public/controllers/register-agent/interfaces/types.ts create mode 100644 public/controllers/register-agent/services/register-agent-services.tsx create mode 100644 public/controllers/register-agent/utils/validations.test.tsx create mode 100644 public/controllers/register-agent/utils/validations.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index bb65a829e1..c0d9e0b9fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to the Wazuh app project will be documented in this file. +## Wazuh v4.6.0 - OpenSearch Dashboards 2.6.0 - Revision 4500 + +### Added + +### Changed + +- Changed the deploy a new agent page from step one to step three. [#5554](https://github.com/wazuh/wazuh-kibana-app/pull/5554) [5462](https://github.com/wazuh/wazuh-kibana-app/pull/5462) + +### Fixed + ## Wazuh v4.5.0 - OpenSearch Dashboards 2.6.0 - Revision 4500 ### Added diff --git a/public/components/common/form/hooks.tsx b/public/components/common/form/hooks.tsx index be7e1a8fa8..63ff8fdb72 100644 --- a/public/components/common/form/hooks.tsx +++ b/public/components/common/form/hooks.tsx @@ -103,7 +103,7 @@ export const useForm = (fields: FormConfiguration): UseFormReturn => { Object.entries(enhanceFields as EnhancedFields) .filter(([, { error }]) => error) .map(([fieldKey, { error }]) => [fieldKey, error]), - ) as { [key: string]: string }; + ); function undoChanges() { setFormFields(state => diff --git a/public/components/common/form/index.tsx b/public/components/common/form/index.tsx index 0f267589c1..2b7cb7ec0f 100644 --- a/public/components/common/form/index.tsx +++ b/public/components/common/form/index.tsx @@ -7,10 +7,9 @@ import { InputFormSwitch } from './input_switch'; import { InputFormFilePicker } from './input_filepicker'; import { InputFormTextArea } from './input_text_area'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; -import { OsCard } from '../../../controllers/register-agent/components/os-card/os-card'; import { SettingTypes } from './types'; -interface InputFormProps { +export interface InputFormProps { type: SettingTypes; value: any; onChange: (event: React.ChangeEvent) => void; @@ -65,10 +64,6 @@ export const InputForm = ({ /> ); - if (type === 'custom') { - return ; - } - return label ? ( <> @@ -98,5 +93,5 @@ const Input = { select: InputFormSelect, text: InputFormText, textarea: InputFormTextArea, - custom: OsCard, + custom: ({ component, ...rest }) => component(rest), }; diff --git a/public/components/common/form/input_select.tsx b/public/components/common/form/input_select.tsx index a8f02e99d7..b212f3f068 100644 --- a/public/components/common/form/input_select.tsx +++ b/public/components/common/form/input_select.tsx @@ -2,12 +2,26 @@ import React from 'react'; import { EuiSelect } from '@elastic/eui'; import { IInputFormType } from './types'; -export const InputFormSelect = ({ options, value, onChange }: IInputFormType) => { - return ( - - ) +export const InputFormSelect = ({ + options, + value, + onChange, + placeholder, + selectedOptions, + isDisabled, + isClearable, + dataTestSubj, +}: IInputFormType) => { + return ( + + ); }; diff --git a/public/components/common/form/input_text.tsx b/public/components/common/form/input_text.tsx index feb0d218ee..c8e3d730d4 100644 --- a/public/components/common/form/input_text.tsx +++ b/public/components/common/form/input_text.tsx @@ -1,14 +1,21 @@ import React from 'react'; import { EuiFieldText } from '@elastic/eui'; -import { IInputFormType } from "./types"; +import { IInputFormType } from './types'; -export const InputFormText = ({ value, isInvalid, onChange }: IInputFormType) => { - return ( - - ); +export const InputFormText = ({ + value, + isInvalid, + onChange, + placeholder, + fullWidth, +}: IInputFormType) => { + return ( + + ); }; diff --git a/public/components/common/form/types.ts b/public/components/common/form/types.ts index 737c0c8ab9..301914479c 100644 --- a/public/components/common/form/types.ts +++ b/public/components/common/form/types.ts @@ -19,6 +19,7 @@ export interface IInputForm { } /// use form hook types + export type SettingTypes = | 'text' | 'textarea' @@ -54,7 +55,7 @@ interface EnhancedField { initialValue: any; value: any; changed: boolean; - error: string | null; + error: string | null | undefined; setInputRef: (reference: any) => void; inputRef: any; onChange: (event: any) => void; diff --git a/public/controllers/agent/index.js b/public/controllers/agent/index.js index c9e06604aa..51a445bb00 100644 --- a/public/controllers/agent/index.js +++ b/public/controllers/agent/index.js @@ -11,7 +11,7 @@ */ import { AgentsPreviewController } from './agents-preview'; import { AgentsController } from './agents'; -import { RegisterAgent } from '../register-agent/container/register-agent'; +import { RegisterAgent } from '../../controllers/register-agent/containers/register-agent/register-agent'; import { ExportConfiguration } from './components/export-configuration'; import { AgentsWelcome } from '../../components/common/welcome/agents-welcome'; import { Mitre } from '../../components/overview'; diff --git a/public/controllers/agent/register-agent/steps/wz-manager-address.tsx b/public/controllers/agent/register-agent/steps/wz-manager-address.tsx index 0c46c70676..8bfd679e2f 100644 --- a/public/controllers/agent/register-agent/steps/wz-manager-address.tsx +++ b/public/controllers/agent/register-agent/steps/wz-manager-address.tsx @@ -11,14 +11,14 @@ const WzManagerAddressInput = (props: Props) => { const [value, setValue] = useState(''); useEffect(() => { - if(defaultValue){ + if (defaultValue) { setValue(defaultValue); onChange(defaultValue); - }else{ + } else { setValue(''); onChange(''); } - },[]) + }, []); /** * Handles the change of the selected node IP * @param value diff --git a/public/controllers/register-agent/components/os-card/os-card.tsx b/public/controllers/register-agent/components/os-card/os-card.tsx deleted file mode 100644 index d9c0f2f00b..0000000000 --- a/public/controllers/register-agent/components/os-card/os-card.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React, { useState } from 'react'; -import { - EuiCard, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiSpacer, - EuiCheckbox, -} from '@elastic/eui'; -import { REGISTER_AGENT_DATA } from '../../utils/register-agent-data'; -import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; -import './os-card.scss'; - -export const OsCard = () => { - const [selectedOption, setSelectedOption] = useState( - undefined, - ); - - const handleOptionChange = (optionId: string) => { - setSelectedOption(optionId); - }; - - return ( -
- - {REGISTER_AGENT_DATA.map((data, index) => ( - - - Icon - {data.title} -
- } - display='plain' - hasBorder - onClick={() => {}} - > - {data.hr &&
} - {/* */} - - - - - ))} - -
- ); -}; diff --git a/public/controllers/register-agent/components/checkbox-group/checkbox-group.scss b/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.scss similarity index 77% rename from public/controllers/register-agent/components/checkbox-group/checkbox-group.scss rename to public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.scss index 89e0e58513..ce3cc09745 100644 --- a/public/controllers/register-agent/components/checkbox-group/checkbox-group.scss +++ b/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.scss @@ -1,14 +1,14 @@ .checkbox-group-container { - display: flex; - flex-wrap: wrap; + display: grid; + grid-template-columns: 1fr 1fr; margin-top: 26px; - margin-bottom: 11px; + justify-content: center; } .checkbox-item { - width: 50%; display: flex; flex-direction: row-reverse; + align-items: center; justify-content: center; } @@ -28,7 +28,8 @@ .checkbox-item { display: flex; flex-direction: row-reverse; - justify-content: start; + // justify-content: start; + align-self: baseline; } } @@ -36,16 +37,12 @@ margin-left: 8px; font-style: normal; font-weight: 400; - font-size: 14px; + font-size: 12px; color: #343741; } - .first-card-four-items { .checkbox-item:nth-child(n + 3) { padding-top: 16px; + justify-content: center; } } - -.first-of-row { - padding-right: 17px; -} diff --git a/public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx b/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.test.tsx similarity index 87% rename from public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx rename to public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.test.tsx index c2fa3c3fb0..e2dec80399 100644 --- a/public/controllers/register-agent/components/checkbox-group/checkbox-group.test.tsx +++ b/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { CheckboxGroupComponent } from './checkbox-group'; +import { CheckboxGroupComponent } from '../../step-one/checkbox-group/checkbox-group'; describe('CheckboxGroupComponent', () => { const data = ['Option 1', 'Option 2', 'Option 3']; @@ -50,6 +50,10 @@ describe('CheckboxGroupComponent', () => { fireEvent.click(checkboxItems[1]); expect(onOptionChange).toHaveBeenCalledTimes(1); - expect(onOptionChange).toHaveBeenCalledWith('option-0-1'); + expect(onOptionChange).toHaveBeenCalledWith( + expect.objectContaining({ + target: { value: `option-${cardIndex}-1` }, + }), + ); }); }); diff --git a/public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx b/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.tsx similarity index 77% rename from public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx rename to public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.tsx index 160bcfd66a..09043dea50 100644 --- a/public/controllers/register-agent/components/checkbox-group/checkbox-group.tsx +++ b/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.tsx @@ -2,13 +2,6 @@ import React from 'react'; import { EuiRadioGroup } from '@elastic/eui'; import './checkbox-group.scss'; -interface RegisterAgentData { - icon: string; - title: string; - hr: boolean; - architecture: string[]; -} - interface Props { data: string[]; cardIndex: number; @@ -23,14 +16,9 @@ const CheckboxGroupComponent: React.FC = ({ selectedOption, onOptionChange, }) => { - const handleOptionChange = (optionId: string) => { - onOptionChange(optionId); - }; - const isSingleArchitecture = data.length === 1; const isDoubleArchitecture = data.length === 2; const isFirstCardWithFourItems = cardIndex === 0 && data.length === 4; - return (
= ({ > {arch} handleOptionChange(id)} + onChange={(id: string) => { + onOptionChange({ target: { value: id } }); + }} />
))} @@ -59,4 +49,3 @@ const CheckboxGroupComponent: React.FC = ({ }; export { CheckboxGroupComponent }; -export type { RegisterAgentData }; diff --git a/public/controllers/register-agent/components/os-card/os-card.scss b/public/controllers/register-agent/components/step-one/os-card/os-card.scss similarity index 94% rename from public/controllers/register-agent/components/os-card/os-card.scss rename to public/controllers/register-agent/components/step-one/os-card/os-card.scss index d4d3b41649..364734dc6b 100644 --- a/public/controllers/register-agent/components/os-card/os-card.scss +++ b/public/controllers/register-agent/components/step-one/os-card/os-card.scss @@ -48,3 +48,7 @@ .last-card { margin-right: 63px; } + +.cardsCallOut { + margin-top: 16px; +} diff --git a/public/controllers/register-agent/components/os-card/os-card.test.tsx b/public/controllers/register-agent/components/step-one/os-card/os-card.test.tsx similarity index 90% rename from public/controllers/register-agent/components/os-card/os-card.test.tsx rename to public/controllers/register-agent/components/step-one/os-card/os-card.test.tsx index da1a61c120..ebb927853d 100644 --- a/public/controllers/register-agent/components/os-card/os-card.test.tsx +++ b/public/controllers/register-agent/components/step-one/os-card/os-card.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { OsCard } from './os-card'; +import { OsCard } from '../../step-one/os-card/os-card'; describe('OsCard', () => { test('renders three cards with different titles', () => { diff --git a/public/controllers/register-agent/components/step-one/os-card/os-card.tsx b/public/controllers/register-agent/components/step-one/os-card/os-card.tsx new file mode 100644 index 0000000000..c1db7610a3 --- /dev/null +++ b/public/controllers/register-agent/components/step-one/os-card/os-card.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiCallOut, + EuiLink, + EuiCheckbox, +} from '@elastic/eui'; +import { REGISTER_AGENT_DATA_STEP_ONE } from '../../../utils/register-agent-data'; +import { CheckboxGroupComponent } from '../../step-one/checkbox-group/checkbox-group'; +import './os-card.scss'; +import { webDocumentationLink } from '../../../../../../common/services/web_documentation'; + +interface Props { + setStatusCheck: string; + onChange: React.ChangeEventHandler; +} + +export const OsCard = ({ onChange, value }: Props) => { + return ( +
+ + {REGISTER_AGENT_DATA_STEP_ONE.map((data, index) => ( + + + Icon + {data.title} +
+ } + display='plain' + hasBorder + onClick={() => {}} + className='card' + > + {data.hr &&
} + + + + ))} + + + For additional systems and architectures, please check our{' '} + + steps + + . + + } + > +
+ ); +}; diff --git a/public/controllers/register-agent/components/steps-three/group-input.scss b/public/controllers/register-agent/components/steps-three/group-input.scss new file mode 100644 index 0000000000..e69348c07b --- /dev/null +++ b/public/controllers/register-agent/components/steps-three/group-input.scss @@ -0,0 +1,8 @@ +.groupTitle { + margin-top: '32px'; + flex-direction: 'row'; + font-style: normal; + font-weight: 700; + font-size: 12px; + line-height: 20px; +} diff --git a/public/controllers/register-agent/components/steps-three/group-input.tsx b/public/controllers/register-agent/components/steps-three/group-input.tsx new file mode 100644 index 0000000000..2878738617 --- /dev/null +++ b/public/controllers/register-agent/components/steps-three/group-input.tsx @@ -0,0 +1,96 @@ +import React, { Fragment, useState } from 'react'; +import { + EuiComboBox, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiButtonEmpty, + EuiLink, +} from '@elastic/eui'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; + +const popoverAgentGroup = ( + + Learn about{' '} + + Select a group. + + +); + +const GroupInput = ({ value, options, onChange }) => { + const [isPopoverAgentGroup, setIsPopoverAgentGroup] = useState(false); + + const onButtonAgentGroup = () => + setIsPopoverAgentGroup(isPopoverAgentGroup => !isPopoverAgentGroup); + const closeAgentGroup = () => setIsPopoverAgentGroup(false); + return ( + <> + + + + Select one or more existing groups + + } + isOpen={isPopoverAgentGroup} + closePopover={closeAgentGroup} + anchorPosition='rightCenter' + > + {popoverAgentGroup} + + + + { + onChange({ + target: { value: group }, + }); + }} + isDisabled={!options?.groups.length} + isClearable={true} + data-test-subj='demoComboBox' + data-testid='group-input-combobox' + /> + {!options?.groups.length && ( + <> + + + )} + + ); +}; + +export default GroupInput; diff --git a/public/controllers/register-agent/container/register-agent.tsx b/public/controllers/register-agent/container/register-agent.tsx deleted file mode 100644 index 5617bf3606..0000000000 --- a/public/controllers/register-agent/container/register-agent.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { ChangeEvent } from 'react'; -import { InputForm } from '../../../components/common/form'; -import './register-agent.scss'; - -export const RegisterAgent: React.FC = () => { - const handleChange = (event: ChangeEvent) => { - // ver - }; - - return ( -
-
Deploy new agent
- -
- ); -}; diff --git a/public/controllers/register-agent/container/register-agent.scss b/public/controllers/register-agent/containers/register-agent/register-agent.scss similarity index 84% rename from public/controllers/register-agent/container/register-agent.scss rename to public/controllers/register-agent/containers/register-agent/register-agent.scss index c127bd0e9c..a0d9bd03d7 100644 --- a/public/controllers/register-agent/container/register-agent.scss +++ b/public/controllers/register-agent/containers/register-agent/register-agent.scss @@ -1,6 +1,6 @@ .container { box-sizing: border-box; - height: 1271px; + max-height: 1271px; margin-top: 44px; background: #ffffff; border: 1px solid rgba(52, 55, 65, 0.2); @@ -20,3 +20,7 @@ display: flex; justify-content: center; } +.close { + display: flex; + margin-top: 17px; +} diff --git a/public/controllers/register-agent/container/register-agent.test.tsx b/public/controllers/register-agent/containers/register-agent/register-agent.test.tsx similarity index 85% rename from public/controllers/register-agent/container/register-agent.test.tsx rename to public/controllers/register-agent/containers/register-agent/register-agent.test.tsx index 061960686a..5f4dd452be 100644 --- a/public/controllers/register-agent/container/register-agent.test.tsx +++ b/public/controllers/register-agent/containers/register-agent/register-agent.test.tsx @@ -5,7 +5,9 @@ import { RegisterAgent } from './register-agent'; describe('RegisterAgent', () => { test('renders the component', () => { - render(); + const mockHasAgents = jest.fn(); + + render(); // Verifica que el título esté presente const titleElement = screen.getByText('Deploy new agent'); diff --git a/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/public/controllers/register-agent/containers/register-agent/register-agent.tsx new file mode 100644 index 0000000000..0b89f4f450 --- /dev/null +++ b/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -0,0 +1,238 @@ +import React, { useState, useEffect } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiTitle, + EuiButtonEmpty, + EuiPage, + EuiPageBody, + EuiSpacer, + EuiProgress, +} from '@elastic/eui'; +import { WzRequest } from '../../../../react-services/wz-request'; +import { UI_LOGGER_LEVELS } from '../../../../../common/constants'; +import { UI_ERROR_SEVERITIES } from '../../../../react-services/error-orchestrator/types'; +import { ErrorHandler } from '../../../../react-services/error-management'; +import { getMasterRemoteConfiguration } from '../../../agent/components/register-agent-service'; +import './register-agent.scss'; +import { Steps } from '../steps/steps'; +import { InputForm } from '../../../../components/common/form'; +import { getGroups } from '../../services/register-agent-services'; +import { useForm } from '../../../../components/common/form/hooks'; +import { FormConfiguration } from '../../../../components/common/form/types'; +import { useSelector } from 'react-redux'; +import { withReduxProvider } from '../../../../components/common/hocs'; +import GroupInput from '../../components/steps-three/group-input'; +import { OsCard } from '../../components/step-one/os-card/os-card'; +import { + validateServerAddress, + validateAgentName, +} from '../../utils/validations'; + +export const RegisterAgent = withReduxProvider( + ({ getWazuhVersion, hasAgents, addNewAgent, reload }) => { + const configuration = useSelector( + (state: { appConfig: { data: any } }) => state.appConfig.data, + ); + + const [wazuhVersion, setWazuhVersion] = useState(''); + const [udpProtocol, setUdpProtocol] = useState(false); + const [connectionSecure, setConnectionSecure] = useState( + true, + ); + const [haveUdpProtocol, setHaveUdpProtocol] = useState( + false, + ); + const [haveConnectionSecure, setHaveConnectionSecure] = useState< + boolean | null + >(false); + const [loading, setLoading] = useState(false); + const [wazuhPassword, setWazuhPassword] = useState(''); + const [groups, setGroups] = useState([]); + const [needsPassword, setNeedsPassword] = useState(false); + const [hideTextPassword, setHideTextPassword] = useState( + false, + ); + + const initialFields: FormConfiguration = { + operatingSystemSelection: { + type: 'custom', + initialValue: '', + component: props => { + return ; + }, + options: { + groups, + }, + }, + + //IP: This is a set of four numbers, for example, 192.158.1.38. Each number in the set can range from 0 to 255. Therefore, the full range of IP addresses goes from 0.0.0.0 to 255.255.255.255 + // O ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 + + // FQDN: Maximum of 63 characters per label. + // Can only contain numbers, letters and hyphens (-) + // Labels cannot begin or end with a hyphen + // Currently supports multilingual characters, i.e. letters not included in the English alphabet: e.g. á é í ó ú ü ñ. + // Minimum 3 labels + + serverAddress: { + type: 'text', + initialValue: configuration['enrollment.dns'] || '', + validate: validateServerAddress, + }, + agentName: { + type: 'text', + initialValue: '', + validate: validateAgentName, + }, + + agentGroups: { + type: 'custom', + initialValue: [], + component: props => { + return ; + }, + options: { + groups, + }, + }, + }; + + const form = useForm(initialFields); + + const getRemoteConfig = async () => { + const remoteConfig = await getMasterRemoteConfiguration(); + if (remoteConfig) { + setHaveUdpProtocol(remoteConfig.isUdp); + setHaveConnectionSecure(remoteConfig.haveSecureConnection); + setUdpProtocol(remoteConfig.isUdp); + setConnectionSecure(remoteConfig.haveSecureConnection); + } + }; + + const getAuthInfo = async () => { + try { + const result = await WzRequest.apiReq( + 'GET', + '/agents/000/config/auth/auth', + {}, + ); + return (result.data || {}).data || {}; + } catch (error) { + ErrorHandler.handleError(error); + } + }; + + useEffect(() => { + const fetchData = async () => { + try { + const wazuhVersion = await getWazuhVersion(); + let wazuhPassword = ''; + let hideTextPassword = false; + await getRemoteConfig(); + const authInfo = await getAuthInfo(); + const needsPassword = (authInfo.auth || {}).use_password === 'yes'; + if (needsPassword) { + wazuhPassword = + configuration['enrollment.password'] || + authInfo['authd.pass'] || + ''; + if (wazuhPassword) { + hideTextPassword = true; + } + } + const groups = await getGroups(); + + setNeedsPassword(needsPassword); + setHideTextPassword(hideTextPassword); + setWazuhPassword(wazuhPassword); + setWazuhVersion(wazuhVersion); + setGroups(groups); + setLoading(false); + } catch (error) { + setWazuhVersion(wazuhVersion); + setLoading(false); + const options = { + context: 'RegisterAgent', + level: UI_LOGGER_LEVELS.ERROR, + severity: UI_ERROR_SEVERITIES.BUSINESS, + display: true, + store: false, + error: { + error: error, + message: error.message || error, + title: error.name || error, + }, + }; + ErrorHandler.handleError(error, options); + } + }; + + fetchData(); + }, []); + + const agentGroup = ; + const osCard = ( + + ); + + return ( +
+ + + + + +
+ {hasAgents() ? ( + addNewAgent(false)} + iconType='cross' + > + ) : ( + reload()} + iconType='refresh' + > + Refresh + + )} +
+ + + +

Deploy new agent

+
+
+
+ + {loading ? ( + <> + + + + + + ) : ( + + + + )} +
+
+
+
+
+
+ ); + }, +); diff --git a/public/controllers/register-agent/containers/steps/steps.scss b/public/controllers/register-agent/containers/steps/steps.scss new file mode 100644 index 0000000000..d18ea5c07e --- /dev/null +++ b/public/controllers/register-agent/containers/steps/steps.scss @@ -0,0 +1,56 @@ +.stepTitle { + font-style: normal; + font-weight: 700; + font-size: 16px; + letter-spacing: 0.6px; + color: #343741; + flex-direction: row; +} + +.stepSubtitleServerAddress { + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 24px; + color: #343741; + margin-bottom: 9px; +} + +.stepSubtitle { + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 24px; + color: #343741; + margin-bottom: 20px; +} + +.titleAndIcon { + display: flex; + flex-direction: row; +} + +.warningForAgentName { + margin-top: 10px; +} + +.euiToolTipAnchor { + margin-left: 7px; +} + +.subtitleAgentName { + flex-direction: 'row'; + font-style: 'normal'; + font-weight: 700; + font-size: '12px'; + line-height: '20px'; + color: '#343741'; +} + +.euiStep__titleWrapper { + align-items: center; +} + +.euiButtonEmpty .euiButtonEmpty__content { + padding: 0; +} diff --git a/public/controllers/register-agent/containers/steps/steps.tsx b/public/controllers/register-agent/containers/steps/steps.tsx new file mode 100644 index 0000000000..bf456eff0e --- /dev/null +++ b/public/controllers/register-agent/containers/steps/steps.tsx @@ -0,0 +1,234 @@ +import React, { Fragment, useState } from 'react'; +import { + EuiSteps, + EuiText, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiCallOut, + EuiPopover, + EuiButtonEmpty, + EuiLink, +} from '@elastic/eui'; +import { InputForm } from '../../../../components/common/form'; +import './steps.scss'; +import { + REGISTER_AGENT_DATA_STEP_THREE, + REGISTER_AGENT_DATA_STEP_TWO, +} from '../../utils/register-agent-data'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; + +const popoverServerAddress = ( + + Learn about{' '} + + Server address. + + +); + +const popoverAgentName = ( + + Learn about{' '} + + Assigning an agent name. + + +); + +const warningForAgentName = + 'The agent name must be unique. It can’t be changed once the agent has been enrolled.'; + +export const Steps = ({ + needsPassword, + hideTextPassword, + agentGroup, + form, + osCard, +}) => { + const [isPopoverServerAddress, setIsPopoverServerAddress] = useState(false); + const [isPopoverAgentName, setIsPopoverAgentName] = useState(false); + + const onButtonServerAddress = () => + setIsPopoverServerAddress( + isPopoverServerAddress => !isPopoverServerAddress, + ); + const closeServerAddress = () => setIsPopoverServerAddress(false); + + const onButtonAgentName = () => + setIsPopoverAgentName(isPopoverAgentName => !isPopoverAgentName); + const closeAgentName = () => setIsPopoverAgentName(false); + + const firstSetOfSteps = [ + { + title: ( + +

Select the package to download and install on your system:

+
+ ), + children: osCard, + status: form.fields.operatingSystemSelection.value + ? 'complete' + : 'current', + }, + { + title: ( + + + + Server address + + } + isOpen={isPopoverServerAddress} + closePopover={closeServerAddress} + anchorPosition='rightCenter' + > + {popoverServerAddress} + + + + ), + children: ( + + + {REGISTER_AGENT_DATA_STEP_TWO.map((data, index) => ( + + + {data.subtitle} + + + ))} + + } + fullWidth={false} + placeholder='Server address' + /> + + ), + status: !form.fields.operatingSystemSelection.value + ? 'disabled' + : !form.fields.serverAddress.value && + form.fields.operatingSystemSelection.value + ? 'current' + : form.fields.operatingSystemSelection.value && + form.fields.serverAddress.value + ? 'complete' + : '', + }, + ...(!(!needsPassword || hideTextPassword) + ? [ + { + title: ( + +

Wazuh password

+
+ ), + children: ( + + { + 'No ha establecido una contraseña. Se le asigno una por defecto' + } + + ), + }, + ] + : []), + { + title: ( + +

Optional settings

+
+ ), + children: ( + + + {REGISTER_AGENT_DATA_STEP_THREE.map((data, index) => ( + + {data.subtitle} + + ))} + + + + + + Assign an agent name + + } + isOpen={isPopoverAgentName} + closePopover={closeAgentName} + anchorPosition='rightCenter' + > + {popoverAgentName} + + + + + } + placeholder='Agent name' + /> + + {agentGroup} + + ), + status: + !form.fields.operatingSystemSelection.value || + !form.fields.serverAddress.value + ? 'disabled' + : form.fields.serverAddress.value !== '' + ? 'current' + : form.fields.agentGroups.value.length > 0 + ? 'complete' + : '', + }, + ]; + + return ; +}; diff --git a/public/controllers/register-agent/index.tsx b/public/controllers/register-agent/index.tsx index d516e34a58..146589950a 100644 --- a/public/controllers/register-agent/index.tsx +++ b/public/controllers/register-agent/index.tsx @@ -1 +1 @@ -export { RegisterAgent } from './container/register-agent'; +export { RegisterAgent } from './containers/register-agent/register-agent'; diff --git a/public/controllers/register-agent/interfaces/types.ts b/public/controllers/register-agent/interfaces/types.ts new file mode 100644 index 0000000000..06de5db134 --- /dev/null +++ b/public/controllers/register-agent/interfaces/types.ts @@ -0,0 +1,16 @@ +interface RegisterAgentData { + icon: string; + title: string; + hr: boolean; + architecture: string[]; +} + +interface CheckboxGroupComponentProps { + data: string[]; + cardIndex: number; + selectedOption: string | undefined; + onOptionChange: (optionId: string) => void; + onChange: (id: string) => void; +} + +export type { RegisterAgentData, CheckboxGroupComponentProps }; diff --git a/public/controllers/register-agent/services/register-agent-services.tsx b/public/controllers/register-agent/services/register-agent-services.tsx new file mode 100644 index 0000000000..262afedd64 --- /dev/null +++ b/public/controllers/register-agent/services/register-agent-services.tsx @@ -0,0 +1,238 @@ +import { WzRequest } from '../../../react-services/wz-request'; +import { ServerAddressOptions } from '../register-agent/steps'; + +type Protocol = 'TCP' | 'UDP'; + +type RemoteItem = { + connection: 'syslog' | 'secure'; + ipv6: 'yes' | 'no'; + protocol: Protocol[]; + allowed_ips?: string[]; + queue_size?: string; +}; + +type RemoteConfig = { + name: string; + isUdp: boolean | null; + haveSecureConnection: boolean | null; +}; + +/** + * Get the cluster status + */ +export const clusterStatusResponse = async (): Promise => { + const clusterStatus = await WzRequest.apiReq('GET', '/cluster/status', {}); + if ( + clusterStatus.data.data.enabled === 'yes' && + clusterStatus.data.data.running === 'yes' + ) { + // Cluster mode + return true; + } else { + // Manager mode + return false; + } +}; + +/** + * Get the remote configuration from api + */ +async function getRemoteConfiguration(nodeName: string): Promise { + let config: RemoteConfig = { + name: nodeName, + isUdp: false, + haveSecureConnection: false, + }; + + try { + const clusterStatus = await clusterStatusResponse(); + let result; + if (clusterStatus) { + result = await WzRequest.apiReq( + 'GET', + `/cluster/${nodeName}/configuration/request/remote`, + {}, + ); + } else { + result = await WzRequest.apiReq( + 'GET', + '/manager/configuration/request/remote', + {}, + ); + } + const items = ((result.data || {}).data || {}).affected_items || []; + const remote = items[0]?.remote; + if (remote) { + const remoteFiltered = remote.filter((item: RemoteItem) => { + return item.connection === 'secure'; + }); + + remoteFiltered.length > 0 + ? (config.haveSecureConnection = true) + : (config.haveSecureConnection = false); + + let protocolsAvailable: Protocol[] = []; + remote.forEach((item: RemoteItem) => { + // get all protocols available + item.protocol.forEach(protocol => { + protocolsAvailable = protocolsAvailable.concat(protocol); + }); + }); + + config.isUdp = + getRemoteProtocol(protocolsAvailable) === 'UDP' ? true : false; + } + return config; + } catch (error) { + return config; + } +} + +/** + * Get the remote protocol available from list of protocols + * @param protocols + */ +function getRemoteProtocol(protocols: Protocol[]) { + if (protocols.length === 1) { + return protocols[0]; + } else { + return !protocols.includes('TCP') ? 'UDP' : 'TCP'; + } +} + +/** + * Get the remote configuration from nodes registered in the cluster and decide the protocol to setting up in deploy agent param + * @param nodeSelected + * @param defaultServerAddress + */ +async function getConnectionConfig( + nodeSelected: ServerAddressOptions, + defaultServerAddress?: string, +) { + const nodeName = nodeSelected?.label; + const nodeIp = nodeSelected?.value; + if (!defaultServerAddress) { + if (nodeSelected.nodetype !== 'custom') { + const remoteConfig = await getRemoteConfiguration(nodeName); + return { + serverAddress: nodeIp, + udpProtocol: remoteConfig.isUdp, + connectionSecure: remoteConfig.haveSecureConnection, + }; + } else { + return { + serverAddress: nodeName, + udpProtocol: false, + connectionSecure: true, + }; + } + } else { + return { + serverAddress: defaultServerAddress, + udpProtocol: false, + connectionSecure: true, + }; + } +} + +type NodeItem = { + name: string; + ip: string; + type: string; +}; + +type NodeResponse = { + data: { + data: { + affected_items: NodeItem[]; + }; + }; +}; + +/** + * Get the list of the cluster nodes and parse it into a list of options + */ +export const getNodeIPs = async (): Promise => { + return await WzRequest.apiReq('GET', '/cluster/nodes', {}); +}; + +/** + * Get the list of the manager and parse it into a list of options + */ +export const getManagerNode = async (): Promise => { + const managerNode = await WzRequest.apiReq('GET', '/manager/api/config', {}); + return ( + managerNode?.data?.data?.affected_items?.map(item => ({ + label: item.node_name, + value: item.node_api_config.host, + nodetype: 'master', + })) || [] + ); +}; + +/** + * Parse the nodes list from the API response to a format that can be used by the EuiComboBox + * @param nodes + */ +export const parseNodesInOptions = ( + nodes: NodeResponse, +): ServerAddressOptions[] => { + return nodes.data.data.affected_items.map((item: NodeItem) => ({ + label: item.name, + value: item.ip, + nodetype: item.type, + })); +}; + +/** + * Get the list of the cluster nodes from API and parse it into a list of options + */ +export const fetchClusterNodesOptions = async (): Promise< + ServerAddressOptions[] +> => { + const clusterStatus = await clusterStatusResponse(); + if (clusterStatus) { + // Cluster mode + // Get the cluster nodes + const nodes = await getNodeIPs(); + return parseNodesInOptions(nodes); + } else { + // Manager mode + // Get the manager node + return await getManagerNode(); + } +}; + +/** + * Get the master node data from the list of cluster nodes + * @param nodeIps + */ +export const getMasterNode = ( + nodeIps: ServerAddressOptions[], +): ServerAddressOptions[] => { + return nodeIps.filter(nodeIp => nodeIp.nodetype === 'master'); +}; + +/** + * Get the remote configuration from manager + * This function get the config from manager mode or cluster mode + */ +export const getMasterRemoteConfiguration = async () => { + const nodes = await fetchClusterNodesOptions(); + const masterNode = getMasterNode(nodes); + return await getRemoteConfiguration(masterNode[0].label); +}; + +export { getConnectionConfig, getRemoteConfiguration }; + +export const getGroups = async () => { + try { + const result = await WzRequest.apiReq('GET', '/groups', {}); + return result.data.data.affected_items.map(item => ({ + label: item.name, + id: item.name, + })); + } catch (error) { + throw new Error(error); + } +}; diff --git a/public/controllers/register-agent/utils/register-agent-data.tsx b/public/controllers/register-agent/utils/register-agent-data.tsx index a32b9464b0..c0e4e79a94 100644 --- a/public/controllers/register-agent/utils/register-agent-data.tsx +++ b/public/controllers/register-agent/utils/register-agent-data.tsx @@ -1,9 +1,9 @@ -import { RegisterAgentData } from '../components/checkbox-group/checkbox-group'; +import { RegisterAgentData } from '../interfaces/types'; import LinuxIcon from '../../../../public/assets/images/icons/linux-icon.svg'; import WindowsIcon from '../../../../public/assets/images/icons/windows-icon.svg'; import MacIcon from '../../../../public/assets/images/icons/mac-icon.svg'; -export const REGISTER_AGENT_DATA: RegisterAgentData[] = [ +export const REGISTER_AGENT_DATA_STEP_ONE: RegisterAgentData[] = [ { icon: LinuxIcon, title: 'LINUX', @@ -23,3 +23,19 @@ export const REGISTER_AGENT_DATA: RegisterAgentData[] = [ architecture: ['Intel', 'Apple Silicon'], }, ]; + +export const REGISTER_AGENT_DATA_STEP_TWO = [ + { + title: 'Server address', + subtitle: + 'This is the address the agent uses to communicate with the Wazuh server. Enter an IP address or a fully qualified domain name (FDQN).', + }, +]; + +export const REGISTER_AGENT_DATA_STEP_THREE = [ + { + title: 'Optional settings', + subtitle: + 'The deployment sets the endpoint hostname as the agent name by default. Optionally, you can set your own name in the field below.', + }, +]; diff --git a/public/controllers/register-agent/utils/validations.test.tsx b/public/controllers/register-agent/utils/validations.test.tsx new file mode 100644 index 0000000000..e51dd972fd --- /dev/null +++ b/public/controllers/register-agent/utils/validations.test.tsx @@ -0,0 +1,68 @@ +import { validateServerAddress, validateAgentName } from './validations'; + +describe('Validations', () => { + it('should return undefined for an empty value', () => { + const result = validateServerAddress(''); + expect(result).toBeUndefined(); + }); + + it('should return undefined for a valid FQDN', () => { + const validFQDN = 'example.fqdn.valid'; + const result = validateServerAddress(validFQDN); + expect(result).toBeUndefined(); + }); + + it('should return undefined for a valid IP', () => { + const validIP = '192.168.1.1'; + const result = validateServerAddress(validIP); + expect(result).toBeUndefined(); + }); + + it('should return an error message for an invalid FQDN', () => { + const invalidFQDN = 'example.fqdn'; + const result = validateServerAddress(invalidFQDN); + expect(result).toBe( + 'Each label must have a letter or number at the beginning. The maximum length is 63 characters.', + ); + }); + + test('should return an error message for an invalid IP', () => { + const invalidIP = '999.999.999.999.999'; + const result = validateServerAddress(invalidIP); + expect(result).toBe('Not a valid IP'); + }); + + test('should return undefined for an empty value', () => { + const emptyValue = ''; + const result = validateAgentName(emptyValue); + expect(result).toBeUndefined(); + }); + + test('should return an error message for invalid format and length', () => { + const invalidAgentName = '?'; + const result = validateAgentName(invalidAgentName); + expect(result).toBe( + 'The minimum length is 2 characters. The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"', + ); + }); + + test('should return an error message for invalid format', () => { + const invalidAgentName = 'agent$name'; + const result = validateAgentName(invalidAgentName); + expect(result).toBe( + 'The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"', + ); + }); + + test('should return an error message for invalid length', () => { + const invalidAgentName = 'a'; + const result = validateAgentName(invalidAgentName); + expect(result).toBe('The minimum length is 2 characters.'); + }); + + test('should return an empty string for a valid agent name', () => { + const validAgentName = 'agent_name'; + const result = validateAgentName(validAgentName); + expect(result).toBe(''); + }); +}); diff --git a/public/controllers/register-agent/utils/validations.tsx b/public/controllers/register-agent/utils/validations.tsx new file mode 100644 index 0000000000..6b5a17978f --- /dev/null +++ b/public/controllers/register-agent/utils/validations.tsx @@ -0,0 +1,50 @@ +export const validateServerAddress = value => { + const isFQDN = + /^(?!-)(?!.*--)[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*(?:\.[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*){2,}$/; + + const isIP = + /^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})$/; + const numbersAndPoints = /^[0-9.]+$/; + const areLettersNumbersAndColons = /^[a-zA-Z0-9:]+$/; + const letters = /[a-zA-Z]/; + const isFQDNFormatValid = isFQDN.test(value); + const isIPFormatValid = isIP.test(value); + const areNumbersAndPoints = numbersAndPoints.test(value); + const hasLetters = letters.test(value); + const hasPoints = value.includes('.'); + + let validation = undefined; + if (value.length === 0) { + return validation; + } else if (isFQDNFormatValid && value !== '') { + return validation; // FQDN valid + } else if (isIPFormatValid && value !== '') { + return validation; // IP valid + } else if (hasPoints && hasLetters && !isFQDNFormatValid) { + return (validation = + 'Each label must have a letter or number at the beginning. The maximum length is 63 characters.'); // FQDN invalid + } else if ( + (areNumbersAndPoints || areLettersNumbersAndColons) && + !isIPFormatValid + ) { + return (validation = 'Not a valid IP'); // IP invalid + } +}; + +export const validateAgentName = value => { + if (value.length === 0) { + return undefined; + } + const regex = /^[A-Za-z.\-_,]+$/; + + const isLengthValid = value.length >= 2 && value.length <= 63; + const isFormatValid = regex.test(value); + if (!isFormatValid && !isLengthValid) { + return 'The minimum length is 2 characters. The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"'; + } else if (!isLengthValid) { + return 'The minimum length is 2 characters.'; + } else if (!isFormatValid) { + return 'The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"'; + } + return ''; +}; From 11a462d890466bb2d0dcd1943fe0331db4e655c9 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:45:02 -0300 Subject: [PATCH 08/46] [Redesign add agent] Integration commands generator with UI (#5593) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * reuse of common form on the card * Card with logic * CheckboxGroup component logic update * CheckboxGroup component logic update * Adding card icons * update checkbox logic, styles, and card styles * clean code * clean code * gitignore Mac files * updating checkbox logic, styles, and card styles * step component * Passing interfaces to a separate file, updating styles, and component logic * Update interfaces and clean up code * update of folder structure and step logic * tcp, udp, protocols, password, groups, logics * input logic server address name password groups and styles * group input logic * oscards input logic * oscards input logic * styles * regex * styles and settings * styles * various adjustments * cleaning up code and changing some styles * cleaning up code * cleaning code * update password * gitignore * gitignore * correcting validation text in input agent name * correcting validation text in input agent name * corrección de validación de input de nombre del agente * cleaning code * cleaning code * regex that differentiates between FQDN and IP * Use of PLUGIN_VERSION_SHORT * Use of PLUGIN_VERSION_SHORT * link * Revert "Merge branch '4205-redesign-add-agent-page' into 5518-inputs-logic-server-address-name-password-and-group" This reverts commit a4c6fb5d24a482e80f9595a879d141ff2d7fa5bb, reversing changes made to 5a0d2cb0e71972eb8f68b16f035ebc977220379f. * link and revert * characteres valid * correction of styles when bringing changes from parent branch * change tooltip to popover * moving validations to a separate file with their tests * corrections and cleaning of comments * camel case * change in function * type * remove type * fullWidth * type * change * conditional * change label a to Euilink * change label a to Euilink * conditional * delete usePrevious * delete usePrevious * deleted files ds store * test correction and placeholder * show architecture instead of id * Add register agent form values parser * Remove extension on operating system type * Add command sections with form values * Create new components for steps inputs * Fix some types * Renamed some options * Move commands config inside core folder * Fix server address error message display * Create methods to get form steps status * Allow select more than group * Hide agent group param when is empty * Fix steps form statuses * Remove break lines in commands * Add white space in error messages * Fix steps form status * Added new command component white custom copy and language * Fixed step form status --------- Co-authored-by: chantal.kelm Co-authored-by: Chantal Belén kelm <99441266+chantal-kelm@users.noreply.github.com> --- public/components/common/form/types.ts | 3 +- .../command-output/command-output.tsx | 65 ++++ .../group-input.scss | 0 .../group-input.tsx | 0 .../optionals-inputs/optionals-inputs.tsx | 102 ++++++ .../checkbox-group/checkbox-group.scss | 0 .../checkbox-group/checkbox-group.test.tsx | 0 .../checkbox-group/checkbox-group.tsx | 0 .../os-card/os-card.scss | 0 .../os-card/os-card.test.tsx | 0 .../os-card/os-card.tsx | 7 +- .../server-address/server-address-input.tsx | 35 ++ .../server-address/server-address-title.tsx | 53 +++ .../config/os-commands-definitions.ts | 148 --------- .../register-agent/register-agent.scss | 4 +- .../register-agent/register-agent.tsx | 47 ++- .../register-agent/containers/steps/steps.tsx | 307 ++++++++---------- .../core/config/os-commands-definitions.ts | 194 +++++++++++ .../core/register-commands/README.md | 13 - .../command-generator.test.ts | 20 +- .../command-generator/command-generator.ts | 12 +- .../register-commands/exceptions/index.ts | 27 +- .../get-install-command.service.test.ts | 6 - .../services/get-install-command.service.ts | 5 +- .../search-os-definitions.service.test.ts | 25 +- .../services/search-os-definitions.service.ts | 10 +- .../core/register-commands/types.ts | 5 +- .../register-agent/hooks/README.md | 8 - .../hooks/use-register-agent-commands.test.ts | 16 +- .../register-agent/interfaces/types.ts | 6 +- .../services/register-agent-services.tsx | 78 ++++- .../register-agent-steps-status-services.tsx | 145 +++++++++ .../utils/register-agent-data.tsx | 6 +- .../register-agent/utils/validations.tsx | 12 +- 34 files changed, 862 insertions(+), 497 deletions(-) create mode 100644 public/controllers/register-agent/components/command-output/command-output.tsx rename public/controllers/register-agent/components/{steps-three => group-input}/group-input.scss (100%) rename public/controllers/register-agent/components/{steps-three => group-input}/group-input.tsx (100%) create mode 100644 public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx rename public/controllers/register-agent/components/{step-one => os-selector}/checkbox-group/checkbox-group.scss (100%) rename public/controllers/register-agent/components/{step-one => os-selector}/checkbox-group/checkbox-group.test.tsx (100%) rename public/controllers/register-agent/components/{step-one => os-selector}/checkbox-group/checkbox-group.tsx (100%) rename public/controllers/register-agent/components/{step-one => os-selector}/os-card/os-card.scss (100%) rename public/controllers/register-agent/components/{step-one => os-selector}/os-card/os-card.test.tsx (100%) rename public/controllers/register-agent/components/{step-one => os-selector}/os-card/os-card.tsx (89%) create mode 100644 public/controllers/register-agent/components/server-address/server-address-input.tsx create mode 100644 public/controllers/register-agent/components/server-address/server-address-title.tsx delete mode 100644 public/controllers/register-agent/config/os-commands-definitions.ts create mode 100644 public/controllers/register-agent/core/config/os-commands-definitions.ts create mode 100644 public/controllers/register-agent/services/register-agent-steps-status-services.tsx diff --git a/public/components/common/form/types.ts b/public/components/common/form/types.ts index 301914479c..762f73962f 100644 --- a/public/components/common/form/types.ts +++ b/public/components/common/form/types.ts @@ -70,8 +70,9 @@ interface EnhancedCustomField extends EnhancedField { component: (props: any) => JSX.Element; } +export type EnhancedFieldConfiguration = EnhancedDefaultField | EnhancedCustomField; export interface EnhancedFields { - [key: string]: EnhancedDefaultField | EnhancedCustomField; + [key: string]: EnhancedFieldConfiguration; } export interface UseFormReturn { diff --git a/public/controllers/register-agent/components/command-output/command-output.tsx b/public/controllers/register-agent/components/command-output/command-output.tsx new file mode 100644 index 0000000000..88830a66c7 --- /dev/null +++ b/public/controllers/register-agent/components/command-output/command-output.tsx @@ -0,0 +1,65 @@ +import { + EuiCodeBlock, + EuiCopy, + EuiIcon, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import React, { Fragment, useState } from 'react'; + +interface ICommandSectionProps { + commandText: string; + showCommand: boolean; + onCopy: () => void; + os?: 'WINDOWS' | string; +} + +export default function CommandOutput(props: ICommandSectionProps) { + const { commandText, showCommand, onCopy, os } = props; + const getHighlightCodeLanguage = (os: 'WINDOWS' | string) => { + if (os.toLowerCase() === 'windows') { + return 'powershell'; + } else { + return 'bash'; + } + }; + + const [language, setLanguage] = useState(getHighlightCodeLanguage(os || '')); + + const onHandleCopy = (command: any) => { + onCopy && onCopy(); + return command + }; + + const [commandToCopy, setCommandToCopy] = useState(commandText); + + return ( + + + +
+ + {showCommand ? commandText : ''} + + {showCommand && ( + + {commandToCopy => ( +
onHandleCopy(commandToCopy)}> +

+ Copy command +

+
+ )} +
+ )} +
+ +
+
+ ); +} diff --git a/public/controllers/register-agent/components/steps-three/group-input.scss b/public/controllers/register-agent/components/group-input/group-input.scss similarity index 100% rename from public/controllers/register-agent/components/steps-three/group-input.scss rename to public/controllers/register-agent/components/group-input/group-input.scss diff --git a/public/controllers/register-agent/components/steps-three/group-input.tsx b/public/controllers/register-agent/components/group-input/group-input.tsx similarity index 100% rename from public/controllers/register-agent/components/steps-three/group-input.tsx rename to public/controllers/register-agent/components/group-input/group-input.tsx diff --git a/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx b/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx new file mode 100644 index 0000000000..0a7560c3a8 --- /dev/null +++ b/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx @@ -0,0 +1,102 @@ +import React, { Fragment, useState } from 'react'; +import { UseFormReturn } from '../../../../components/common/form/types'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPopover, + EuiButtonEmpty, + EuiCallOut, + EuiLink, +} from '@elastic/eui'; +import { InputForm } from '../../../../components/common/form'; +import { OPTIONAL_PARAMETERS_TEXT } from '../../utils/register-agent-data'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +interface OptionalsInputsProps { + formFields: UseFormReturn['fields']; +} + +const OptionalsInputs = (props: OptionalsInputsProps) => { + const { formFields } = props; + const [isPopoverAgentName, setIsPopoverAgentName] = useState(false); + const onButtonAgentName = () => + setIsPopoverAgentName(isPopoverAgentName => !isPopoverAgentName); + const closeAgentName = () => setIsPopoverAgentName(false); + + const popoverAgentName = ( + + Learn about{' '} + + Assigning an agent name. + + + ); + + const warningForAgentName = + 'The agent name must be unique. It can’t be changed once the agent has been enrolled.'; + return ( + + + {OPTIONAL_PARAMETERS_TEXT.map((data, index) => ( + + {data.subtitle} + + ))} + + + + + + Assign an agent name + + } + isOpen={isPopoverAgentName} + closePopover={closeAgentName} + anchorPosition='rightCenter' + > + {popoverAgentName} + + + + + } + placeholder='Agent name' + /> + + + + ); +}; + +export default OptionalsInputs; diff --git a/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.scss b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss similarity index 100% rename from public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.scss rename to public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss diff --git a/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.test.tsx b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx similarity index 100% rename from public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.test.tsx rename to public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx diff --git a/public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.tsx b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx similarity index 100% rename from public/controllers/register-agent/components/step-one/checkbox-group/checkbox-group.tsx rename to public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx diff --git a/public/controllers/register-agent/components/step-one/os-card/os-card.scss b/public/controllers/register-agent/components/os-selector/os-card/os-card.scss similarity index 100% rename from public/controllers/register-agent/components/step-one/os-card/os-card.scss rename to public/controllers/register-agent/components/os-selector/os-card/os-card.scss diff --git a/public/controllers/register-agent/components/step-one/os-card/os-card.test.tsx b/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx similarity index 100% rename from public/controllers/register-agent/components/step-one/os-card/os-card.test.tsx rename to public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx diff --git a/public/controllers/register-agent/components/step-one/os-card/os-card.tsx b/public/controllers/register-agent/components/os-selector/os-card/os-card.tsx similarity index 89% rename from public/controllers/register-agent/components/step-one/os-card/os-card.tsx rename to public/controllers/register-agent/components/os-selector/os-card/os-card.tsx index c1db7610a3..18b08a2e7e 100644 --- a/public/controllers/register-agent/components/step-one/os-card/os-card.tsx +++ b/public/controllers/register-agent/components/os-selector/os-card/os-card.tsx @@ -7,21 +7,22 @@ import { EuiLink, EuiCheckbox, } from '@elastic/eui'; -import { REGISTER_AGENT_DATA_STEP_ONE } from '../../../utils/register-agent-data'; -import { CheckboxGroupComponent } from '../../step-one/checkbox-group/checkbox-group'; +import { OPERATING_SYSTEMS_OPTIONS } from '../../../utils/register-agent-data'; +import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; import './os-card.scss'; import { webDocumentationLink } from '../../../../../../common/services/web_documentation'; interface Props { setStatusCheck: string; onChange: React.ChangeEventHandler; + value: any; } export const OsCard = ({ onChange, value }: Props) => { return (
- {REGISTER_AGENT_DATA_STEP_ONE.map((data, index) => ( + {OPERATING_SYSTEMS_OPTIONS.map((data, index) => ( { + const { formField } = props; + + return ( + + + {SERVER_ADDRESS_TEXTS.map((data, index) => ( + + + {data.subtitle} + + + ))} + + } + fullWidth={false} + placeholder='Server address' + /> + + ); +}; + +export default ServerAddressInput; diff --git a/public/controllers/register-agent/components/server-address/server-address-title.tsx b/public/controllers/register-agent/components/server-address/server-address-title.tsx new file mode 100644 index 0000000000..76475c5a6d --- /dev/null +++ b/public/controllers/register-agent/components/server-address/server-address-title.tsx @@ -0,0 +1,53 @@ +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiLink, EuiPopover } from "@elastic/eui" +import React, { useState } from "react"; +import { webDocumentationLink } from "../../../../../common/services/web_documentation"; +import { PLUGIN_VERSION_SHORT } from "../../../../../common/constants"; + +const ServerAddressTitle = () => { + const [isPopoverServerAddress, setIsPopoverServerAddress] = useState(false); + const closeServerAddress = () => setIsPopoverServerAddress(false); + const onButtonServerAddress = () => + setIsPopoverServerAddress( + isPopoverServerAddress => !isPopoverServerAddress, + ); + + const popoverServerAddress = ( + + Learn about{' '} + + Server address. + + + ); + + return ( + + + Server address + + } + isOpen={isPopoverServerAddress} + closePopover={closeServerAddress} + anchorPosition='rightCenter' + > + {popoverServerAddress} + + + ) +} + +export default ServerAddressTitle; \ No newline at end of file diff --git a/public/controllers/register-agent/config/os-commands-definitions.ts b/public/controllers/register-agent/config/os-commands-definitions.ts deleted file mode 100644 index dc69156571..0000000000 --- a/public/controllers/register-agent/config/os-commands-definitions.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { IOSDefinition, tOptionalParams } from '../core/register-commands/types'; - -// Defined OS combinations -export interface ILinuxOSTypes { - name: 'linux'; - architecture: 'x64' | 'x86'; - extension: 'rpm' | 'deb'; -} -export interface IWindowsOSTypes { - name: 'windows'; - architecture: 'x86'; - extension: 'msi'; -} - -export interface IMacOSTypes { - name: 'mac'; - architecture: '32/64'; - extension: 'pkg'; -} - -export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; - - -export type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; - -/////////////////////////////////////////////////////////////////// -/// Operating system commands definitions -/////////////////////////////////////////////////////////////////// - -const linuxDefinition: IOSDefinition = { - name: 'linux', - options: [ - { - extension: 'deb', - architecture: '32/64', - urlPackage: props => - `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.${props.extension}`, - installCommand: props => - `sudo yum install -y ${props.urlPackage}`, - startCommand: props => `sudo systemctl start wazuh-agent`, - }, - { - extension: 'deb', - architecture: 'x64', - urlPackage: props => - `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}.${props.extension}`, - installCommand: props => - `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, - startCommand: props => `sudo systemctl start wazuh-agent`, - }, - { - extension: 'rpm', - architecture: '32/64', - urlPackage: props => - `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.${props.extension}`, - installCommand: props => - `sudo yum install -y ${props.urlPackage}`, - startCommand: props => `sudo systemctl start wazuh-agent`, - }, - { - extension: 'deb', - architecture: 'x64', - urlPackage: props => - `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64.${props.extension}`, - installCommand: props => - `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, - startCommand: props => `sudo systemctl start wazuh-agent`, - }, - ], -}; - -const windowsDefinition: IOSDefinition = { - name: 'windows', - options: [ - { - extension: 'msi', - architecture: 'x86', - urlPackage: props => - `https://packages.wazuh.com/4.x/windows/wazuh-agent-${props.wazuhVersion}-1.${props.extension}`, - installCommand: props => - `Invoke-WebRequest -Uri ${props.urlPackage} -OutFile \${env.tmp}\\wazuh-agent.${props.extension}; msiexec.exe /i \${env.tmp}\\wazuh-agent.${props.extension} /q`, - startCommand: props => `Start-Service -Name wazuh-agent`, - }, - ], -}; - -const macDefinition: IOSDefinition = { - name: 'mac', - options: [ - { - extension: 'pkg', - architecture: '32/64', - urlPackage: props => - `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1.${props.extension}`, - installCommand: props => - `mac -so wazuh-agent.pkg ${props.urlPackage} && sudo launchctl setenv && sudo installer -pkg ./wazuh-agent.pkg -target /`, - startCommand: props => `sudo /Library/Ossec/bin/wazuh-control start`, - }, - ], -}; - -export const osCommandsDefinitions = [ - linuxDefinition, - windowsDefinition, - macDefinition, -]; - -/////////////////////////////////////////////////////////////////// -/// Optional parameters definitions -/////////////////////////////////////////////////////////////////// - -export const optionalParamsDefinitions: tOptionalParams = { - server_address: { - property: 'WAZUH_MANAGER', - getParamCommand: props => { - const { property, value } = props; - return `${property}=${value}`; - } - }, - agent_name: { - property: 'WAZUH_AGENT_NAME', - getParamCommand: props => { - const { property, value } = props; - return `${property}=${value}`; - } - }, - protocol: { - property: 'WAZUH_MANAGER_PROTOCOL', - getParamCommand: props => { - const { property, value } = props; - return `${property}=${value}`; - } - }, - agent_group: { - property: 'WAZUH_AGENT_GROUP', - getParamCommand: props => { - const { property, value } = props; - return `${property}=${value}`; - } - }, - wazuh_password: { - property: 'WAZUH_PASSWORD', - getParamCommand: props => { - const { property, value } = props; - return `${property}=${value}`; - } - } -} diff --git a/public/controllers/register-agent/containers/register-agent/register-agent.scss b/public/controllers/register-agent/containers/register-agent/register-agent.scss index a0d9bd03d7..6c32c5a91a 100644 --- a/public/controllers/register-agent/containers/register-agent/register-agent.scss +++ b/public/controllers/register-agent/containers/register-agent/register-agent.scss @@ -1,6 +1,6 @@ .container { box-sizing: border-box; - max-height: 1271px; + min-height: 1271px; margin-top: 44px; background: #ffffff; border: 1px solid rgba(52, 55, 65, 0.2); @@ -23,4 +23,4 @@ .close { display: flex; margin-top: 17px; -} +} \ No newline at end of file diff --git a/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/public/controllers/register-agent/containers/register-agent/register-agent.tsx index 0b89f4f450..9bdb61396a 100644 --- a/public/controllers/register-agent/containers/register-agent/register-agent.tsx +++ b/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -9,6 +9,7 @@ import { EuiPageBody, EuiSpacer, EuiProgress, + EuiButton, } from '@elastic/eui'; import { WzRequest } from '../../../../react-services/wz-request'; import { UI_LOGGER_LEVELS } from '../../../../../common/constants'; @@ -23,24 +24,27 @@ import { useForm } from '../../../../components/common/form/hooks'; import { FormConfiguration } from '../../../../components/common/form/types'; import { useSelector } from 'react-redux'; import { withReduxProvider } from '../../../../components/common/hocs'; -import GroupInput from '../../components/steps-three/group-input'; -import { OsCard } from '../../components/step-one/os-card/os-card'; +import GroupInput from '../../components/group-input/group-input'; +import { OsCard } from '../../components/os-selector/os-card/os-card'; import { validateServerAddress, validateAgentName, } from '../../utils/validations'; +interface IRegisterAgentProps { + getWazuhVersion: () => Promise; + hasAgents: () => Promise; + addNewAgent: (agent: any) => Promise; + reload: () => void; +} + export const RegisterAgent = withReduxProvider( - ({ getWazuhVersion, hasAgents, addNewAgent, reload }) => { + ({ getWazuhVersion, hasAgents, addNewAgent, reload }: IRegisterAgentProps) => { const configuration = useSelector( (state: { appConfig: { data: any } }) => state.appConfig.data, ); const [wazuhVersion, setWazuhVersion] = useState(''); - const [udpProtocol, setUdpProtocol] = useState(false); - const [connectionSecure, setConnectionSecure] = useState( - true, - ); const [haveUdpProtocol, setHaveUdpProtocol] = useState( false, ); @@ -50,8 +54,8 @@ export const RegisterAgent = withReduxProvider( const [loading, setLoading] = useState(false); const [wazuhPassword, setWazuhPassword] = useState(''); const [groups, setGroups] = useState([]); - const [needsPassword, setNeedsPassword] = useState(false); - const [hideTextPassword, setHideTextPassword] = useState( + const [needsPassword, setNeedsPassword] = useState(false); + const [hideTextPassword, setHideTextPassword] = useState( false, ); @@ -66,16 +70,6 @@ export const RegisterAgent = withReduxProvider( groups, }, }, - - //IP: This is a set of four numbers, for example, 192.158.1.38. Each number in the set can range from 0 to 255. Therefore, the full range of IP addresses goes from 0.0.0.0 to 255.255.255.255 - // O ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 - - // FQDN: Maximum of 63 characters per label. - // Can only contain numbers, letters and hyphens (-) - // Labels cannot begin or end with a hyphen - // Currently supports multilingual characters, i.e. letters not included in the English alphabet: e.g. á é í ó ú ü ñ. - // Minimum 3 labels - serverAddress: { type: 'text', initialValue: configuration['enrollment.dns'] || '', @@ -106,8 +100,6 @@ export const RegisterAgent = withReduxProvider( if (remoteConfig) { setHaveUdpProtocol(remoteConfig.isUdp); setHaveConnectionSecure(remoteConfig.haveSecureConnection); - setUdpProtocol(remoteConfig.isUdp); - setConnectionSecure(remoteConfig.haveSecureConnection); } }; @@ -143,7 +135,6 @@ export const RegisterAgent = withReduxProvider( } } const groups = await getGroups(); - setNeedsPassword(needsPassword); setHideTextPassword(hideTextPassword); setWazuhPassword(wazuhPassword); @@ -172,7 +163,6 @@ export const RegisterAgent = withReduxProvider( fetchData(); }, []); - const agentGroup = ; const osCard = ( ); @@ -222,11 +212,20 @@ export const RegisterAgent = withReduxProvider( form={form} needsPassword={needsPassword} hideTextPassword={hideTextPassword} - agentGroup={agentGroup} osCard={osCard} + connection={{ + isSecure: haveConnectionSecure ? true : false, + isUDP: haveUdpProtocol ? true : false, + }} + wazuhPassword={wazuhPassword} /> )} + + + reload()}>Close + + diff --git a/public/controllers/register-agent/containers/steps/steps.tsx b/public/controllers/register-agent/containers/steps/steps.tsx index bf456eff0e..95e14d2144 100644 --- a/public/controllers/register-agent/containers/steps/steps.tsx +++ b/public/controllers/register-agent/containers/steps/steps.tsx @@ -1,80 +1,105 @@ -import React, { Fragment, useState } from 'react'; -import { - EuiSteps, - EuiText, - EuiTitle, - EuiFlexGroup, - EuiFlexItem, - EuiCallOut, - EuiPopover, - EuiButtonEmpty, - EuiLink, -} from '@elastic/eui'; -import { InputForm } from '../../../../components/common/form'; +import React, { Fragment, useEffect, useState } from 'react'; +import { EuiSteps, EuiTitle } from '@elastic/eui'; import './steps.scss'; +import { OPERATING_SYSTEMS_OPTIONS } from '../../utils/register-agent-data'; import { - REGISTER_AGENT_DATA_STEP_THREE, - REGISTER_AGENT_DATA_STEP_TWO, -} from '../../utils/register-agent-data'; -import { webDocumentationLink } from '../../../../../common/services/web_documentation'; -import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; - -const popoverServerAddress = ( - - Learn about{' '} - - Server address. - - -); + IParseRegisterFormValues, + getRegisterAgentFormValues, + parseRegisterAgentFormValues, +} from '../../services/register-agent-services'; -const popoverAgentName = ( - - Learn about{' '} - - Assigning an agent name. - - -); +import { useRegisterAgentCommands } from '../../hooks/use-register-agent-commands'; +import { + osCommandsDefinitions, + optionalParamsDefinitions, + tOperatingSystem, + tOptionalParameters, +} from '../../core/config/os-commands-definitions'; +import { UseFormReturn } from '../../../../components/common/form/types'; +import CommandOutput from '../../components/command-output/command-output'; +import ServerAddressTitle from '../../components/server-address/server-address-title'; +import ServerAddressInput from '../../components/server-address/server-address-input'; +import OptionalsInputs from '../../components/optionals-inputs/optionals-inputs'; +import { getAgentCommandsStepStatus, tFormStepsStatus, getOSSelectorStepStatus, getServerAddressStepStatus, getOptionalParameterStepStatus, showCommandsSections } from '../../services/register-agent-steps-status-services'; -const warningForAgentName = - 'The agent name must be unique. It can’t be changed once the agent has been enrolled.'; +interface IStepsProps { + needsPassword: boolean; + hideTextPassword: boolean; + form: UseFormReturn; + osCard: React.ReactElement; + connection: { + isUDP: boolean; + isSecure: boolean; + }; + wazuhPassword: string; +} export const Steps = ({ needsPassword, hideTextPassword, - agentGroup, form, osCard, -}) => { - const [isPopoverServerAddress, setIsPopoverServerAddress] = useState(false); - const [isPopoverAgentName, setIsPopoverAgentName] = useState(false); + connection, + wazuhPassword, +}: IStepsProps) => { + const [registerAgentFormValues, setRegisterAgentFormValues] = + useState({ + operatingSystem: { + name: '', + architecture: '', + }, + optionalParams: { + agentGroups: '', + agentName: '', + serverAddress: '', + wazuhPassword, + }, + }); - const onButtonServerAddress = () => - setIsPopoverServerAddress( - isPopoverServerAddress => !isPopoverServerAddress, + useEffect(() => { + // get form values and parse them divided in OS and optional params + const registerAgentFormValuesParsed = parseRegisterAgentFormValues( + getRegisterAgentFormValues(form), + OPERATING_SYSTEMS_OPTIONS, ); - const closeServerAddress = () => setIsPopoverServerAddress(false); + setRegisterAgentFormValues(registerAgentFormValuesParsed); + setInstallCommandStepStatus(getAgentCommandsStepStatus(form.fields, installCommandWasCopied)) + setStartCommandStepStatus(getAgentCommandsStepStatus(form.fields, startCommandWasCopied)) + }, [form.fields]); + + const { installCommand, startCommand, selectOS, setOptionalParams } = + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }); + + // install - start commands step state + const [installCommandWasCopied, setInstallCommandWasCopied] = useState(false); + const [installCommandStepStatus, setInstallCommandStepStatus] = useState(getAgentCommandsStepStatus(form.fields, false)) + const [startCommandWasCopied, setStartCommandWasCopied] = useState(false); + const [startCommandStepStatus, setStartCommandStepStatus] = useState(getAgentCommandsStepStatus(form.fields, false)) + + useEffect(() => { + if ( + registerAgentFormValues.operatingSystem.name !== '' && + registerAgentFormValues.operatingSystem.architecture !== '' + ) { + selectOS(registerAgentFormValues.operatingSystem as tOperatingSystem); + } + setOptionalParams(registerAgentFormValues.optionalParams); + setInstallCommandWasCopied(false); + setStartCommandWasCopied(false); + }, [registerAgentFormValues]); + + useEffect(() => { + setInstallCommandStepStatus(getAgentCommandsStepStatus(form.fields, installCommandWasCopied)) + }, [installCommandWasCopied]) - const onButtonAgentName = () => - setIsPopoverAgentName(isPopoverAgentName => !isPopoverAgentName); - const closeAgentName = () => setIsPopoverAgentName(false); + useEffect(() => { + setStartCommandStepStatus(getAgentCommandsStepStatus(form.fields, startCommandWasCopied)) + }, [startCommandWasCopied]) - const firstSetOfSteps = [ + const registerAgentFormSteps = [ { title: ( @@ -82,62 +107,12 @@ export const Steps = ({ ), children: osCard, - status: form.fields.operatingSystemSelection.value - ? 'complete' - : 'current', + status: getOSSelectorStepStatus(form.fields), }, { - title: ( - - - - Server address - - } - isOpen={isPopoverServerAddress} - closePopover={closeServerAddress} - anchorPosition='rightCenter' - > - {popoverServerAddress} - - - - ), - children: ( - - - {REGISTER_AGENT_DATA_STEP_TWO.map((data, index) => ( - - - {data.subtitle} - - - ))} - - } - fullWidth={false} - placeholder='Server address' - /> - - ), - status: !form.fields.operatingSystemSelection.value - ? 'disabled' - : !form.fields.serverAddress.value && - form.fields.operatingSystemSelection.value - ? 'current' - : form.fields.operatingSystemSelection.value && - form.fields.serverAddress.value - ? 'complete' - : '', + title: , + children: , + status: getServerAddressStepStatus(form.fields), }, ...(!(!needsPassword || hideTextPassword) ? [ @@ -163,72 +138,44 @@ export const Steps = ({

Optional settings

), + children: , + status: getOptionalParameterStepStatus(form.fields, installCommandWasCopied, startCommandWasCopied) + }, + { + title: ( + +

+ Run the following commands to download and install the Wazuh agent: +

+
+ ), + children: ( + setInstallCommandWasCopied(true)} + /> + ), + status: installCommandStepStatus, + }, + { + title: ( + +

Start the Wazuh agent:

+
+ ), children: ( - - - {REGISTER_AGENT_DATA_STEP_THREE.map((data, index) => ( - - {data.subtitle} - - ))} - - - - - - Assign an agent name - - } - isOpen={isPopoverAgentName} - closePopover={closeAgentName} - anchorPosition='rightCenter' - > - {popoverAgentName} - - - - - } - placeholder='Agent name' - /> - - {agentGroup} - + setStartCommandWasCopied(true)} + /> ), - status: - !form.fields.operatingSystemSelection.value || - !form.fields.serverAddress.value - ? 'disabled' - : form.fields.serverAddress.value !== '' - ? 'current' - : form.fields.agentGroups.value.length > 0 - ? 'complete' - : '', + status: startCommandStepStatus, }, ]; - return ; + return ; }; diff --git a/public/controllers/register-agent/core/config/os-commands-definitions.ts b/public/controllers/register-agent/core/config/os-commands-definitions.ts new file mode 100644 index 0000000000..73a370f917 --- /dev/null +++ b/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -0,0 +1,194 @@ +import { IOSDefinition, tOptionalParams } from '../register-commands/types'; + +// Defined OS combinations + +/** Linux options **/ +export interface ILinuxAMDRPM { + name: 'LINUX'; + architecture: 'RPM amd64'; +} + +export interface ILinuxAARCHRPM { + name: 'LINUX'; + architecture: 'RPM aarch64'; +} + +export interface ILinuxAMDDEB { + name: 'LINUX'; + architecture: 'DEB amd64'; +} + +export interface ILinuxAARCHDEB { + name: 'LINUX'; + architecture: 'DEB aarch64'; +} + +type ILinuxOSTypes = + | ILinuxAMDRPM + | ILinuxAARCHRPM + | ILinuxAMDDEB + | ILinuxAARCHDEB; + +/** Windows options **/ +export interface IWindowsOSTypes { + name: 'WINDOWS'; + architecture: 'MSI 32/64'; +} + +/** MacOS options **/ +export interface IMacOSIntel { + name: 'macOS'; + architecture: 'Intel'; +} + +export interface IMacOSApple { + name: 'macOS'; + architecture: 'Apple Silicon'; +} + +type IMacOSTypes = IMacOSApple | IMacOSIntel; + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +export type tOptionalParameters = + | 'serverAddress' + | 'agentName' + | 'agentGroups' + | 'wazuhPassword'; + +/////////////////////////////////////////////////////////////////// +/// Operating system commands definitions +/////////////////////////////////////////////////////////////////// + +const linuxDefinition: IOSDefinition = { + name: 'LINUX', + options: [ + { + architecture: 'DEB amd64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64`, + installCommand: props => + `sudo yum install -y ${props.urlPackage} ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + architecture: 'DEB aarch64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}`, + installCommand: props => + `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + architecture: 'RPM amd64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64`, + installCommand: props => + `sudo yum install -y ${props.urlPackage} ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + architecture: 'RPM aarch64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64`, + installCommand: props => + `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + ], +}; + +const windowsDefinition: IOSDefinition = { + name: 'WINDOWS', + options: [ + { + architecture: 'MSI 32/64', + urlPackage: props => + `https://packages.wazuh.com/4.x/windows/wazuh-agent-${props.wazuhVersion}-1`, + installCommand: props => + `Invoke-WebRequest -Uri ${ + props.urlPackage + } -OutFile \${env.tmp}\\wazuh-agent; msiexec.exe /i \${env.tmp}\\wazuh-agent /q ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `Start-Service -Name wazuh-agent`, + }, + ], +}; + +const macDefinition: IOSDefinition = { + name: 'macOS', + options: [ + { + architecture: 'Intel', + urlPackage: props => + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1`, + installCommand: props => + `mac -so wazuh-agent.pkg ${ + props.urlPackage + } && sudo launchctl setenv && sudo installer -pkg ./wazuh-agent.pkg -target / + ${props.optionals?.serverAddress || ''} + ${props.optionals?.agentName || ''} + ${props.optionals?.agentGroups || ''} + ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `sudo /Library/Ossec/bin/wazuh-control start`, + }, + { + architecture: 'Apple Silicon', + urlPackage: props => + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1`, + installCommand: props => + `mac -so wazuh-agent.pkg ${ + props.urlPackage + } && sudo launchctl setenv && sudo installer -pkg ./wazuh-agent.pkg -target + ${props.optionals?.serverAddress || ''} + ${props.optionals?.agentName || ''} + ${props.optionals?.agentGroups || ''} + ${props.optionals?.wazuhPassword || ''}`, + startCommand: props => `sudo /Library/Ossec/bin/wazuh-control start`, + }, + ], +}; + +export const osCommandsDefinitions = [ + linuxDefinition, + windowsDefinition, + macDefinition, +]; + +/////////////////////////////////////////////////////////////////// +/// Optional parameters definitions +/////////////////////////////////////////////////////////////////// + +export const optionalParamsDefinitions: tOptionalParams = { + serverAddress: { + property: 'WAZUH_MANAGER', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, + agentName: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, + agentGroups: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: props => { + const { property, value } = props; + let parsedValue = value; + if (Array.isArray(value)) { + parsedValue = value.length > 0 ? value.join(',') : ''; + } + return parsedValue ? `${property}='${parsedValue}'` : ''; + }, + }, + wazuhPassword: { + property: 'WAZUH_PASSWORD', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, +}; diff --git a/public/controllers/register-agent/core/register-commands/README.md b/public/controllers/register-agent/core/register-commands/README.md index d53288e940..02e7600561 100644 --- a/public/controllers/register-agent/core/register-commands/README.md +++ b/public/controllers/register-agent/core/register-commands/README.md @@ -48,7 +48,6 @@ export type tOptionalParams = { export interface IOperationSystem { name: string; architecture: string; - extension: string; } /// .... @@ -56,18 +55,15 @@ export interface IOperationSystem { interface ILinuxOSTypes { name: 'linux'; architecture: 'x64' | 'x86'; - extension: 'rpm' | 'deb'; } interface IWindowsOSTypes { name: 'windows'; architecture: 'x86'; - extension: 'msi'; } interface IMacOSTypes { name: 'mac'; architecture: '32/64'; - extension: 'pkg'; } type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; // add the necessary OS options @@ -80,7 +76,6 @@ export interface IOSDefinition { - extension: OS['extension']; architecture: OS['architecture']; urlPackage: (props: tOSEntryProps) => string; installCommand: (props: tOSEntryProps & { urlPackage: string }) => string; @@ -99,14 +94,12 @@ const osDefinitions: IOSDefinition[] = [{ name: 'linux', options: [ { - extension: 'rpm', architecture: 'amd64', urlPackage: props => 'add url package', installCommand: props => 'add install command', startCommand: props => `add start command`, }, { - extension: 'deb', architecture: 'amd64', urlPackage: props => 'add url package', installCommand: props => 'add install command', @@ -118,7 +111,6 @@ const osDefinitions: IOSDefinition[] = [{ name: 'windows', options: [ { - extension: 'msi', architecture: '32/64', urlPackage: props => 'add url package', installCommand: props => 'add install command', @@ -237,7 +229,6 @@ const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters) commandGenerator.selectOS({ name: 'linux', architecture: 'amd64', - extension: 'rpm' }); // get install command @@ -262,7 +253,6 @@ const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters) commandGenerator.selectOS({ name: 'linux', architecture: 'amd64', - extension: 'rpm' }); // get start command @@ -284,7 +274,6 @@ const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters) commandGenerator.selectOS({ name: 'linux', architecture: 'amd64', - extension: 'rpm' }); const urlPackage = commandGenerator.getUrlPackage(); @@ -305,7 +294,6 @@ const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters) commandGenerator.selectOS({ name: 'linux', architecture: 'amd64', - extension: 'rpm' }); // specify to the command generator the optional parameters that we want to use @@ -330,7 +318,6 @@ export interface ICommandsResponse { wazuhVersion: string; os: string; architecture: string; - extension: string; url_package: string; install_command: string; start_command: string; diff --git a/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts index 71fd77ebd3..ae9af6d24e 100644 --- a/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts +++ b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts @@ -1,7 +1,6 @@ import { CommandGenerator } from './command-generator'; import { IOSDefinition, - IOperationSystem, IOptionalParameters, tOptionalParams, } from '../types'; @@ -14,18 +13,15 @@ const mockedCommandsResponse = jest.fn().mockReturnValue(mockedCommandValue); export interface ILinuxOSTypes { name: 'linux'; architecture: 'x64' | 'x86'; - extension: 'rpm' | 'deb'; } export interface IWindowsOSTypes { name: 'windows'; architecture: 'x86'; - extension: 'msi'; } export interface IMacOSTypes { name: 'mac'; architecture: '32/64'; - extension: 'pkg'; } export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; @@ -41,14 +37,12 @@ const osDefinitions: IOSDefinition[] = [ options: [ { architecture: 'x64', - extension: 'deb', installCommand: mockedCommandsResponse, startCommand: mockedCommandsResponse, urlPackage: mockedCommandsResponse, }, { - architecture: 'x64', - extension: 'msi', + architecture: 'x86', installCommand: mockedCommandsResponse, startCommand: mockedCommandsResponse, urlPackage: mockedCommandsResponse, @@ -107,7 +101,6 @@ describe('Command Generator', () => { commandGenerator.selectOS({ name: 'linux', architecture: 'x64', - extension: 'deb', }); commandGenerator.addOptionalParams(optionalValues); const command = commandGenerator.getInstallCommand(); @@ -123,7 +116,6 @@ describe('Command Generator', () => { commandGenerator.selectOS({ name: 'linux', architecture: 'x64', - extension: 'deb', }); commandGenerator.addOptionalParams(optionalValues); const command = commandGenerator.getStartCommand(); @@ -139,14 +131,12 @@ describe('Command Generator', () => { commandGenerator.selectOS({ name: 'linux', architecture: 'x64', - extension: 'deb', }); commandGenerator.addOptionalParams(optionalValues); const commands = commandGenerator.getAllCommands(); expect(commands).toEqual({ os: 'linux', architecture: 'x64', - extension: 'deb', wazuhVersion: '4.4', install_command: mockedCommandValue, start_command: mockedCommandValue, @@ -165,7 +155,6 @@ describe('Command Generator', () => { const selectedOs: tOperatingSystem = { name: 'linux', architecture: 'x64', - extension: 'deb', }; commandGenerator.selectOS(selectedOs); @@ -182,7 +171,6 @@ describe('Command Generator', () => { expect(commands).toEqual({ os: selectedOs.name, architecture: selectedOs.architecture, - extension: selectedOs.extension, wazuhVersion: '4.4', install_command: mockedCommandValue, start_command: mockedCommandValue, @@ -219,14 +207,12 @@ describe('Command Generator', () => { options: [ { architecture: 'x64', - extension: 'deb', installCommand: mockedCommandsResponse, startCommand: mockedCommandsResponse, urlPackage: mockedCommandsResponse, }, { architecture: 'x64', - extension: 'deb', installCommand: mockedCommandsResponse, startCommand: mockedCommandsResponse, urlPackage: mockedCommandsResponse, @@ -250,7 +236,6 @@ describe('Command Generator', () => { options: [ { architecture: 'x64', - extension: 'deb', installCommand: mockedCommandsResponse, startCommand: mockedCommandsResponse, urlPackage: mockedCommandsResponse, @@ -262,7 +247,6 @@ describe('Command Generator', () => { options: [ { architecture: 'x64', - extension: 'deb', installCommand: mockedCommandsResponse, startCommand: mockedCommandsResponse, urlPackage: mockedCommandsResponse, @@ -340,7 +324,6 @@ describe('Command Generator', () => { const selectedOs: tOperatingSystem = { name: 'linux', architecture: 'x64', - extension: 'deb', }; commandGenerator.selectOS(selectedOs); @@ -373,7 +356,6 @@ describe('Command Generator', () => { const selectedOs: tOperatingSystem = { name: 'linux', architecture: 'x64', - extension: 'deb', }; commandGenerator.selectOS(selectedOs); diff --git a/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts index 8dd6b69889..6974478eac 100644 --- a/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts +++ b/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts @@ -14,7 +14,7 @@ import { validateOSDefinitionsDuplicated, } from '../services/search-os-definitions.service'; import { OptionalParametersManager } from '../optional-parameters-manager/optional-parameters-manager'; -import { NoArchitectureSelectedException, NoExtensionSelectedException, NoOSSelectedException, WazuhVersionUndefinedException } from '../exceptions'; +import { NoArchitectureSelectedException, NoOSSelectedException, WazuhVersionUndefinedException } from '../exceptions'; import { version } from '../../../../../../package.json'; export class CommandGenerator implements ICommandGenerator { @@ -71,21 +71,17 @@ export class CommandGenerator { - const { name, architecture, extension } = params; + const { name, architecture } = params; if (!name) { throw new NoOSSelectedException(); } if (!architecture) { throw new NoArchitectureSelectedException(); } - if (!extension) { - throw new NoExtensionSelectedException(); - } const option = searchOSDefinitions(this.osDefinitions, { name, architecture, - extension, }); return option; } @@ -102,7 +98,6 @@ export class CommandGenerator, @@ -140,7 +134,6 @@ export class CommandGenerator, }); @@ -160,7 +153,6 @@ export class CommandGenerator = { architecture: 'x64', - extension: 'deb', installCommand: props => 'install command mocked', startCommand: props => 'start command mocked', urlPackage: props => 'https://package-url.com', @@ -63,7 +59,6 @@ describe('getInstallCommandByOS', () => { // @ts-ignore const osDefinition: IOSCommandsDefinition = { architecture: 'x64', - extension: 'deb', startCommand: props => 'start command mocked', urlPackage: props => 'https://package-url.com', }; @@ -90,7 +85,6 @@ describe('getInstallCommandByOS', () => { const mockedInstall = jest.fn(); const validOsDefinition: IOSCommandsDefinition = { architecture: 'x64', - extension: 'deb', installCommand: mockedInstall, startCommand: props => 'start command mocked', urlPackage: props => 'https://package-url.com', diff --git a/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts b/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts index d66e75c8c0..c8fabc3ebf 100644 --- a/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts +++ b/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts @@ -16,11 +16,11 @@ import { IOSCommandsDefinition, IOperationSystem, IOptionalParameters } from ".. export function getInstallCommandByOS(osDefinition: IOSCommandsDefinition, packageUrl: string, version: string, osName: string, optionals?: IOptionalParameters) { if (!osDefinition.installCommand) { - throw new NoInstallCommandDefinitionException(osName, osDefinition.architecture, osDefinition.extension); + throw new NoInstallCommandDefinitionException(osName, osDefinition.architecture); } if(!packageUrl || packageUrl === ''){ - throw new NoPackageURLDefinitionException(osName, osDefinition.architecture, osDefinition.extension); + throw new NoPackageURLDefinitionException(osName, osDefinition.architecture); } if(!version || version === ''){ @@ -32,7 +32,6 @@ export function getInstallCommandByOS[] name: 'linux', options: [ { - extension: 'deb', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, @@ -51,7 +46,6 @@ const validOSDefinitions: IOSDefinition[] name: 'windows', options: [ { - extension: 'msi', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, @@ -67,7 +61,6 @@ describe('search OS definitions services', () => { const result = searchOSDefinitions(validOSDefinitions, { name: 'linux', architecture: 'x64', - extension: 'deb', }); expect(result).toMatchObject(validOSDefinitions[0].options[0]); }); @@ -78,20 +71,10 @@ describe('search OS definitions services', () => { // @ts-ignore name: 'invalid-os', architecture: 'x64', - extension: 'deb', }), ).toThrow(NoOSOptionFoundException); }); - - it('should throw an error if the OS name is found but the architecture is not found', () => { - expect(() => - searchOSDefinitions(validOSDefinitions, { - name: 'linux', - architecture: 'invalid-architecture', - extension: 'deb', - }), - ).toThrow(NoOptionFoundException); - }); + }); describe('validateOSDefinitionsDuplicated', () => { @@ -101,7 +84,6 @@ describe('search OS definitions services', () => { name: 'linux', options: [ { - extension: 'deb', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, @@ -113,7 +95,6 @@ describe('search OS definitions services', () => { name: 'windows', options: [ { - extension: 'msi', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, @@ -133,7 +114,6 @@ describe('search OS definitions services', () => { name: 'linux', options: [ { - extension: 'deb', architecture: 'x64', // @ts-ignore packageManager: 'aix', @@ -162,7 +142,6 @@ describe('search OS definitions services', () => { name: 'linux', options: [ { - extension: 'deb', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, @@ -174,14 +153,12 @@ describe('search OS definitions services', () => { name: 'linux', options: [ { - extension: 'deb', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, urlPackage: mockedUrlPackage, }, { - extension: 'deb', architecture: 'x64', installCommand: mockedInstallCommand, startCommand: mockedStartCommand, diff --git a/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts b/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts index fcd5509681..0ceefada65 100644 --- a/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts +++ b/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts @@ -14,13 +14,12 @@ import { IOSDefinition, IOperationSystem } from '../types'; * @param params - The operation system parameters to match against. * @returns The matching OS definition option. * @throws NoOSOptionFoundException - If no matching OS definition is found. - * @throws NoOptionFoundException - If no matching OS definition option is found. */ export function searchOSDefinitions( osDefinitions: IOSDefinition[], params: IOperationSystem, ){ - const { name, architecture, extension } = params; + const { name, architecture } = params; const osDefinition = osDefinitions.find(os => os.name === name); if (!osDefinition) { @@ -29,11 +28,11 @@ export function searchOSDefinitions - option.architecture === architecture && option.extension === extension, + option.architecture === architecture, ); if (!osDefinitionOption) { - throw new NoOptionFoundException(name, architecture, extension); + throw new NoOptionFoundException(name, architecture); } return osDefinitionOption; @@ -72,11 +71,10 @@ export function validateOSDefinitionHasDuplicatedOptions(); for (const option of osDefinition.options) { - let ext_arch_manager = `${option.extension}_${option.architecture}`; + let ext_arch_manager = `${option.architecture}`; if (options.has(ext_arch_manager)) { throw new DuplicatedOSOptionException( osDefinition.name, - option.extension, option.architecture, ); } diff --git a/public/controllers/register-agent/core/register-commands/types.ts b/public/controllers/register-agent/core/register-commands/types.ts index efd36433ff..ebce4b3dfd 100644 --- a/public/controllers/register-agent/core/register-commands/types.ts +++ b/public/controllers/register-agent/core/register-commands/types.ts @@ -4,7 +4,6 @@ export interface IOperationSystem { name: string; architecture: string; - extension: string; } export type IOptionalParameters = { @@ -28,7 +27,6 @@ interface IOptionalParamsWithValues { type tOSEntryProps = IOSProps & IOptionalParamsWithValues; export interface IOSCommandsDefinition { - extension: OS['extension']; architecture: OS['architecture']; urlPackage: (props: tOSEntryProps) => string; installCommand: (props: tOSEntryProps & { urlPackage: string }) => string; @@ -60,7 +58,7 @@ export type tOptionalParams = { }; export interface IOptionalParamInput { - value: string; + value: any; name: T; } export interface IOptionalParametersManager { @@ -90,7 +88,6 @@ export interface ICommandsResponse { wazuhVersion: string; os: string; architecture: string; - extension: string; url_package: string; install_command: string; start_command: string; diff --git a/public/controllers/register-agent/hooks/README.md b/public/controllers/register-agent/hooks/README.md index 0b06f1b93e..d3ec96adc1 100644 --- a/public/controllers/register-agent/hooks/README.md +++ b/public/controllers/register-agent/hooks/README.md @@ -46,7 +46,6 @@ const { selectOS({ name: 'name-OS', architecture: 'architecture-OS', - extension: 'extension-OS', }) // add optionals params depending on the specified optional parameters in the hook configuration @@ -76,7 +75,6 @@ console.log('optionals params processed', optionalParamsParsed); export interface IOperationSystem { name: string; architecture: string; - extension: string; } interface IUseRegisterCommandsProps { @@ -92,7 +90,6 @@ interface IUseRegisterCommandsProps { @@ -126,7 +123,6 @@ export type tOptionalParams = { export interface IOperationSystem { name: string; architecture: string; - extension: string; } /// .... @@ -134,18 +130,15 @@ export interface IOperationSystem { export interface ILinuxOSTypes { name: 'linux'; architecture: 'x64' | 'x86'; - extension: 'rpm' | 'deb'; } export interface IWindowsOSTypes { name: 'windows'; architecture: 'x86'; - extension: 'msi'; } export interface IMacOSTypes { name: 'mac'; architecture: '32/64'; - extension: 'pkg'; } export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; @@ -168,7 +161,6 @@ const { selectOS({ name: 'linux', architecture: 'x64', - extension: 'rpm', }) ```` diff --git a/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts b/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts index ab0f6727df..20a4de7b32 100644 --- a/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts +++ b/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts @@ -11,18 +11,15 @@ type tOptionalParamsNames = 'optional1' | 'optional2'; export interface ILinuxOSTypes { name: 'linux'; architecture: 'x64' | 'x86'; - extension: 'rpm' | 'deb'; } export interface IWindowsOSTypes { name: 'windows'; architecture: 'x86'; - extension: 'msi'; } export interface IMacOSTypes { name: 'mac'; architecture: '32/64'; - extension: 'pkg'; } export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; @@ -31,18 +28,16 @@ const linuxDefinition: IOSDefinition = { name: 'linux', options: [ { - extension: 'deb', architecture: '32/64', urlPackage: props => - `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.${props.extension}`, + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64`, installCommand: props => `sudo yum install -y ${props.urlPackage}`, startCommand: props => `sudo systemctl start wazuh-agent`, }, { - extension: 'deb', architecture: 'x64', urlPackage: props => - `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}.${props.extension}`, + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}`, installCommand: props => `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, startCommand: props => `sudo systemctl start wazuh-agent`, @@ -102,7 +97,6 @@ describe('useRegisterAgentCommands hook', () => { selectOS({ name: 'linux', architecture: 'x64', - extension: 'deb', }); }); } catch (error) { @@ -124,7 +118,7 @@ describe('useRegisterAgentCommands hook', () => { const optionSelected = osCommandsDefinitions .find(os => os.name === 'linux') ?.options.find( - item => item.architecture === 'x64' && item.extension === 'deb', + item => item.architecture === 'x64', ); const spyInstall = jest.spyOn(optionSelected!, 'installCommand'); const spyStart = jest.spyOn(optionSelected!, 'startCommand'); @@ -133,7 +127,6 @@ describe('useRegisterAgentCommands hook', () => { selectOS({ name: 'linux', architecture: 'x64', - extension: 'deb', }); }); expect(result.current.installCommand).not.toBe(''); @@ -211,7 +204,7 @@ describe('useRegisterAgentCommands hook', () => { const optionSelected = osCommandsDefinitions .find(os => os.name === 'linux') ?.options.find( - item => item.architecture === 'x64' && item.extension === 'deb', + item => item.architecture === 'x64', ); const spyInstall = jest.spyOn(optionSelected!, 'installCommand'); const spyStart = jest.spyOn(optionSelected!, 'startCommand'); @@ -220,7 +213,6 @@ describe('useRegisterAgentCommands hook', () => { selectOS({ name: 'linux', architecture: 'x64', - extension: 'deb', }); setOptionalParams({ diff --git a/public/controllers/register-agent/interfaces/types.ts b/public/controllers/register-agent/interfaces/types.ts index 06de5db134..f9fe6c02fc 100644 --- a/public/controllers/register-agent/interfaces/types.ts +++ b/public/controllers/register-agent/interfaces/types.ts @@ -1,8 +1,10 @@ +import { tOperatingSystem } from '../config/os-commands-definitions'; + interface RegisterAgentData { icon: string; - title: string; + title: tOperatingSystem['name']; hr: boolean; - architecture: string[]; + architecture: tOperatingSystem['architecture'][] } interface CheckboxGroupComponentProps { diff --git a/public/controllers/register-agent/services/register-agent-services.tsx b/public/controllers/register-agent/services/register-agent-services.tsx index 262afedd64..ed32979bab 100644 --- a/public/controllers/register-agent/services/register-agent-services.tsx +++ b/public/controllers/register-agent/services/register-agent-services.tsx @@ -1,5 +1,10 @@ +import { UseFormReturn } from '../../../components/common/form/types'; import { WzRequest } from '../../../react-services/wz-request'; -import { ServerAddressOptions } from '../register-agent/steps'; +import { + tOperatingSystem, + tOptionalParameters, +} from '../core/config/os-commands-definitions'; +import { RegisterAgentData } from '../interfaces/types'; type Protocol = 'TCP' | 'UDP'; @@ -106,7 +111,7 @@ function getRemoteProtocol(protocols: Protocol[]) { * @param defaultServerAddress */ async function getConnectionConfig( - nodeSelected: ServerAddressOptions, + nodeSelected: any, defaultServerAddress?: string, ) { const nodeName = nodeSelected?.label; @@ -174,9 +179,7 @@ export const getManagerNode = async (): Promise => { * Parse the nodes list from the API response to a format that can be used by the EuiComboBox * @param nodes */ -export const parseNodesInOptions = ( - nodes: NodeResponse, -): ServerAddressOptions[] => { +export const parseNodesInOptions = (nodes: NodeResponse): any[] => { return nodes.data.data.affected_items.map((item: NodeItem) => ({ label: item.name, value: item.ip, @@ -187,9 +190,7 @@ export const parseNodesInOptions = ( /** * Get the list of the cluster nodes from API and parse it into a list of options */ -export const fetchClusterNodesOptions = async (): Promise< - ServerAddressOptions[] -> => { +export const fetchClusterNodesOptions = async (): Promise => { const clusterStatus = await clusterStatusResponse(); if (clusterStatus) { // Cluster mode @@ -207,9 +208,7 @@ export const fetchClusterNodesOptions = async (): Promise< * Get the master node data from the list of cluster nodes * @param nodeIps */ -export const getMasterNode = ( - nodeIps: ServerAddressOptions[], -): ServerAddressOptions[] => { +export const getMasterNode = (nodeIps: any[]): any[] => { return nodeIps.filter(nodeIp => nodeIp.nodetype === 'master'); }; @@ -236,3 +235,60 @@ export const getGroups = async () => { throw new Error(error); } }; + +export const getRegisterAgentFormValues = (form: UseFormReturn) => { + // return the values form the formFields and the value property + return Object.keys(form.fields).map(key => { + return { + name: key, + value: form.fields[key].value, + }; + }); +}; + +export interface IParseRegisterFormValues { + operatingSystem: { + name: tOperatingSystem['name'] | ''; + architecture: tOperatingSystem['architecture'] | ''; + }; + // optionalParams is an object that their key is defined in tOptionalParameters and value must be string + optionalParams: { + [FIELD in tOptionalParameters]: any; + }; +} + +export const parseRegisterAgentFormValues = ( + formValues: { name: keyof UseFormReturn['fields']; value: any }[], + OSOptionsDefined: RegisterAgentData[], +) => { + // return the values form the formFields and the value property + const parsedForm = { + operatingSystem: { + architecture: '', + name: '', + }, + optionalParams: {}, + } as IParseRegisterFormValues; + formValues.forEach(field => { + if (field.name === 'operatingSystemSelection') { + // search the architecture defined in architecture array and get the os name defined in title array in the same index + const operatingSystem = OSOptionsDefined.find(os => + os.architecture.includes(field.value), + ); + if (operatingSystem) { + parsedForm.operatingSystem = { + name: operatingSystem.title, + architecture: field.value, + }; + } + } else { + if (field.name === 'agentGroups') { + parsedForm.optionalParams[field.name as any] = field.value.map(item => item.id) + } else { + parsedForm.optionalParams[field.name as any] = field.value; + } + } + }); + + return parsedForm; +}; \ No newline at end of file diff --git a/public/controllers/register-agent/services/register-agent-steps-status-services.tsx b/public/controllers/register-agent/services/register-agent-steps-status-services.tsx new file mode 100644 index 0000000000..7065d38290 --- /dev/null +++ b/public/controllers/register-agent/services/register-agent-steps-status-services.tsx @@ -0,0 +1,145 @@ +import { EuiStepStatus } from '@elastic/eui'; +import { UseFormReturn } from '../../../components/common/form/types'; + +const fieldsHaveErrors = ( + fieldsToCheck: string[], + formFields: UseFormReturn['fields'], +) => { + if (!fieldsToCheck) { + return true; + } + // check if the fieldsToCheck array NOT exists in formFields and get the field doesn't exists + if (!fieldsToCheck.every(key => formFields[key])) { + throw Error('fields to check are not defined in formFields'); + } + + const haveError = fieldsToCheck.some(key => { + return formFields[key]?.error; + }); + return haveError; +}; + +const anyFieldIsComplete = ( + fieldsToCheck: string[], + formFields: UseFormReturn['fields'], +) => { + if (!fieldsToCheck) { + return true; + } + // check if the fieldsToCheck array NOT exists in formFields and get the field doesn't exists + if (!fieldsToCheck.every(key => formFields[key])) { + throw Error('fields to check are not defined in formFields'); + } + + if (fieldsHaveErrors(fieldsToCheck, formFields)) { + return false; + } + + if (fieldsAreEmpty(fieldsToCheck, formFields)) { + return false; + } + + return true; +}; + +const fieldsAreEmpty = ( + fieldsToCheck: string[], + formFields: UseFormReturn['fields'], +) => { + if (!fieldsToCheck) { + return true; + } + // check if the fieldsToCheck array NOT exists in formFields and get the field doesn't exists + if (!fieldsToCheck.every(key => formFields[key])) { + throw Error('fields to check are not defined in formFields'); + } + + const notEmpty = fieldsToCheck.some(key => { + return formFields[key]?.value?.length > 0; + }); + return !notEmpty; +}; + +export const showCommandsSections = ( + formFields: UseFormReturn['fields'], +): boolean => { + if ( + !formFields.operatingSystemSelection.value || + formFields.serverAddress.value === '' || + formFields.serverAddress.error + ) { + return false; + } else if ( + formFields.serverAddress.value === '' && + formFields.agentName.value === '' + ) { + return true; + } else if (!fieldsHaveErrors(['agentGroups', 'agentName'], formFields)) { + return true; + } else { + return false; + } +}; + +/******** Form Steps status getters ********/ + +export type tFormStepsStatus = EuiStepStatus | 'current' | 'disabled' | ''; + +export const getOSSelectorStepStatus = ( + formFields: UseFormReturn['fields'], +): tFormStepsStatus => { + return formFields.operatingSystemSelection.value ? 'complete' : 'current'; +}; + +export const getAgentCommandsStepStatus = ( + formFields: UseFormReturn['fields'], + wasCopied: boolean, +): tFormStepsStatus | 'disabled' => { + if (!showCommandsSections(formFields)) { + return 'disabled'; + } else if (showCommandsSections(formFields) && wasCopied) { + return 'complete'; + } else { + return 'current'; + } +}; + +export const getServerAddressStepStatus = ( + formFields: UseFormReturn['fields'], +): tFormStepsStatus => { + if ( + !formFields.operatingSystemSelection.value || + formFields.operatingSystemSelection.error + ) { + return 'disabled'; + } else if ( + !formFields.serverAddress.value || + formFields.serverAddress.error + ) { + return 'current'; + } else { + return 'complete'; + } +}; + +export const getOptionalParameterStepStatus = ( + formFields: UseFormReturn['fields'], + installCommandWasCopied: boolean, +): tFormStepsStatus => { + // when previous step are not complete + if ( + !formFields.operatingSystemSelection.value || + formFields.operatingSystemSelection.error || + !formFields.serverAddress.value || + formFields.serverAddress.error + ) { + return 'disabled'; + } else if ( + installCommandWasCopied || + anyFieldIsComplete(['agentName', 'agentGroups'], formFields) + ) { + return 'complete'; + } else { + return 'current'; + } +}; diff --git a/public/controllers/register-agent/utils/register-agent-data.tsx b/public/controllers/register-agent/utils/register-agent-data.tsx index c0e4e79a94..44a7f52619 100644 --- a/public/controllers/register-agent/utils/register-agent-data.tsx +++ b/public/controllers/register-agent/utils/register-agent-data.tsx @@ -3,7 +3,7 @@ import LinuxIcon from '../../../../public/assets/images/icons/linux-icon.svg'; import WindowsIcon from '../../../../public/assets/images/icons/windows-icon.svg'; import MacIcon from '../../../../public/assets/images/icons/mac-icon.svg'; -export const REGISTER_AGENT_DATA_STEP_ONE: RegisterAgentData[] = [ +export const OPERATING_SYSTEMS_OPTIONS: RegisterAgentData[] = [ { icon: LinuxIcon, title: 'LINUX', @@ -24,7 +24,7 @@ export const REGISTER_AGENT_DATA_STEP_ONE: RegisterAgentData[] = [ }, ]; -export const REGISTER_AGENT_DATA_STEP_TWO = [ +export const SERVER_ADDRESS_TEXTS = [ { title: 'Server address', subtitle: @@ -32,7 +32,7 @@ export const REGISTER_AGENT_DATA_STEP_TWO = [ }, ]; -export const REGISTER_AGENT_DATA_STEP_THREE = [ +export const OPTIONAL_PARAMETERS_TEXT = [ { title: 'Optional settings', subtitle: diff --git a/public/controllers/register-agent/utils/validations.tsx b/public/controllers/register-agent/utils/validations.tsx index 6b5a17978f..c3eab3de31 100644 --- a/public/controllers/register-agent/utils/validations.tsx +++ b/public/controllers/register-agent/utils/validations.tsx @@ -1,4 +1,12 @@ -export const validateServerAddress = value => { +//IP: This is a set of four numbers, for example, 192.158.1.38. Each number in the set can range from 0 to 255. Therefore, the full range of IP addresses goes from 0.0.0.0 to 255.255.255.255 +// O ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 + +// FQDN: Maximum of 63 characters per label. +// Can only contain numbers, letters and hyphens (-) +// Labels cannot begin or end with a hyphen +// Currently supports multilingual characters, i.e. letters not included in the English alphabet: e.g. á é í ó ú ü ñ. +// Minimum 3 labels +export const validateServerAddress = (value: any) => { const isFQDN = /^(?!-)(?!.*--)[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*(?:\.[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*){2,}$/; @@ -31,7 +39,7 @@ export const validateServerAddress = value => { } }; -export const validateAgentName = value => { +export const validateAgentName = (value: any) => { if (value.length === 0) { return undefined; } From 1c31e45cc4d36df101f28920b7d85a5608b29038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chantal=20Bel=C3=A9n=20kelm?= <99441266+chantal-kelm@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:46:45 -0300 Subject: [PATCH 09/46] [Redesign add agent] Dark mode (#5620) * remove custom color styles to make the elastic dark mode work by default on the agent registration page * add development for images to have dark mode in the section deploy a new agent * changelog about dark mode * Cleaning console.log from assets file * Adding suggested style modifications in the agent registration section * add a style hint so that text cannot be selected on cards * add suggested changes to the styles in the register an agent section * correction added to the word wizard * added coding enhancements in the agent registration section * adding an enhancement to eliminate the console error * adding an enhancement to eliminate the console error --- CHANGELOG.md | 3 + .../assets/images/themes/dark/linux-icon.svg | 3 + public/assets/images/themes/dark/mac-icon.svg | 4 + .../images/themes/dark/windows-icon.svg | 13 ++ .../assets/images/themes/light/linux-icon.svg | 3 + .../assets/images/themes/light/mac-icon.svg | 4 + .../images/themes/light/windows-icon.svg | 13 ++ .../components/group-input/group-input.tsx | 1 - .../optionals-inputs/optionals-inputs.tsx | 1 - .../checkbox-group/checkbox-group.scss | 6 +- .../checkbox-group/checkbox-group.tsx | 4 +- .../os-selector/os-card/os-card.scss | 7 +- .../os-selector/os-card/os-card.tsx | 1 - .../register-agent/register-agent.scss | 11 +- .../register-agent/register-agent.tsx | 37 +++- .../containers/steps/steps.scss | 3 - .../register-agent/containers/steps/steps.tsx | 75 ++++---- .../utils/register-agent-data.tsx | 18 +- public/styles/theme/dark/index.dark.scss | 174 ++++++++++-------- public/utils/assets.ts | 3 +- 20 files changed, 236 insertions(+), 148 deletions(-) create mode 100644 public/assets/images/themes/dark/linux-icon.svg create mode 100644 public/assets/images/themes/dark/mac-icon.svg create mode 100644 public/assets/images/themes/dark/windows-icon.svg create mode 100644 public/assets/images/themes/light/linux-icon.svg create mode 100644 public/assets/images/themes/light/mac-icon.svg create mode 100644 public/assets/images/themes/light/windows-icon.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d9e0b9fc..c45b8f981a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,12 @@ All notable changes to the Wazuh app project will be documented in this file. ### Added +- Added development so that the images of the new agent deployment page also have dark mode. [5620](https://github.com/wazuh/wazuh-kibana-app/pull/5620) + ### Changed - Changed the deploy a new agent page from step one to step three. [#5554](https://github.com/wazuh/wazuh-kibana-app/pull/5554) [5462](https://github.com/wazuh/wazuh-kibana-app/pull/5462) +- Removed the custom colors that did not allow to activate the default dark mode of elastic. [5620](https://github.com/wazuh/wazuh-kibana-app/pull/5620) ### Fixed diff --git a/public/assets/images/themes/dark/linux-icon.svg b/public/assets/images/themes/dark/linux-icon.svg new file mode 100644 index 0000000000..c76c7d6328 --- /dev/null +++ b/public/assets/images/themes/dark/linux-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/themes/dark/mac-icon.svg b/public/assets/images/themes/dark/mac-icon.svg new file mode 100644 index 0000000000..2eae996a06 --- /dev/null +++ b/public/assets/images/themes/dark/mac-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/images/themes/dark/windows-icon.svg b/public/assets/images/themes/dark/windows-icon.svg new file mode 100644 index 0000000000..74d5b551f8 --- /dev/null +++ b/public/assets/images/themes/dark/windows-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/assets/images/themes/light/linux-icon.svg b/public/assets/images/themes/light/linux-icon.svg new file mode 100644 index 0000000000..85613a6872 --- /dev/null +++ b/public/assets/images/themes/light/linux-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/themes/light/mac-icon.svg b/public/assets/images/themes/light/mac-icon.svg new file mode 100644 index 0000000000..dbfed2e61f --- /dev/null +++ b/public/assets/images/themes/light/mac-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/images/themes/light/windows-icon.svg b/public/assets/images/themes/light/windows-icon.svg new file mode 100644 index 0000000000..5ef43e4d08 --- /dev/null +++ b/public/assets/images/themes/light/windows-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/controllers/register-agent/components/group-input/group-input.tsx b/public/controllers/register-agent/components/group-input/group-input.tsx index 2878738617..68f436252c 100644 --- a/public/controllers/register-agent/components/group-input/group-input.tsx +++ b/public/controllers/register-agent/components/group-input/group-input.tsx @@ -50,7 +50,6 @@ const GroupInput = ({ value, options, onChange }) => { fontWeight: 700, fontSize: '12px', lineHeight: '20px', - color: '#343741', }} > Select one or more existing groups diff --git a/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx b/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx index 0a7560c3a8..28c91f69af 100644 --- a/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx +++ b/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx @@ -70,7 +70,6 @@ const OptionalsInputs = (props: OptionalsInputsProps) => { fontWeight: 700, fontSize: '12px', lineHeight: '20px', - color: '#343741', }} > Assign an agent name diff --git a/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss index ce3cc09745..b8b985d165 100644 --- a/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss +++ b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss @@ -9,7 +9,7 @@ display: flex; flex-direction: row-reverse; align-items: center; - justify-content: center; + justify-content: left; } .checkbox-group-container.single-architecture { @@ -28,7 +28,7 @@ .checkbox-item { display: flex; flex-direction: row-reverse; - // justify-content: start; + justify-content: left; align-self: baseline; } } @@ -38,11 +38,9 @@ font-style: normal; font-weight: 400; font-size: 12px; - color: #343741; } .first-card-four-items { .checkbox-item:nth-child(n + 3) { padding-top: 16px; - justify-content: center; } } diff --git a/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx index 09043dea50..461e6e0943 100644 --- a/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx +++ b/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx @@ -34,7 +34,9 @@ const CheckboxGroupComponent: React.FC = ({ idx === 0 || idx === 2 ? ' first-of-row' : '' }`} > - {arch} + { } display='plain' hasBorder - onClick={() => {}} className='card' > {data.hr &&
} diff --git a/public/controllers/register-agent/containers/register-agent/register-agent.scss b/public/controllers/register-agent/containers/register-agent/register-agent.scss index 6c32c5a91a..f51d4384e0 100644 --- a/public/controllers/register-agent/containers/register-agent/register-agent.scss +++ b/public/controllers/register-agent/containers/register-agent/register-agent.scss @@ -1,4 +1,4 @@ -.container { +.register-agent-wizard-container { box-sizing: border-box; min-height: 1271px; margin-top: 44px; @@ -9,18 +9,19 @@ padding-right: 63px; } -.title { +.register-agent-wizard-title { margin-top: 51px; margin-bottom: 51px; font-style: normal; font-weight: 400; font-size: 30px; line-height: 36px; - color: #343741; display: flex; justify-content: center; } -.close { + +.register-agent-wizard-close { display: flex; margin-top: 17px; -} \ No newline at end of file + float: right; +} diff --git a/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/public/controllers/register-agent/containers/register-agent/register-agent.tsx index 9bdb61396a..f9d1fa499e 100644 --- a/public/controllers/register-agent/containers/register-agent/register-agent.tsx +++ b/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -39,7 +39,12 @@ interface IRegisterAgentProps { } export const RegisterAgent = withReduxProvider( - ({ getWazuhVersion, hasAgents, addNewAgent, reload }: IRegisterAgentProps) => { + ({ + getWazuhVersion, + hasAgents, + addNewAgent, + reload, + }: IRegisterAgentProps) => { const configuration = useSelector( (state: { appConfig: { data: any } }) => state.appConfig.data, ); @@ -55,9 +60,7 @@ export const RegisterAgent = withReduxProvider( const [wazuhPassword, setWazuhPassword] = useState(''); const [groups, setGroups] = useState([]); const [needsPassword, setNeedsPassword] = useState(false); - const [hideTextPassword, setHideTextPassword] = useState( - false, - ); + const [hideTextPassword, setHideTextPassword] = useState(false); const initialFields: FormConfiguration = { operatingSystemSelection: { @@ -173,14 +176,16 @@ export const RegisterAgent = withReduxProvider( - -
+ +
{hasAgents() ? ( addNewAgent(false)} iconType='cross' - > + > + Close + ) : ( -

Deploy new agent

+

+ Deploy new agent +

@@ -221,9 +228,19 @@ export const RegisterAgent = withReduxProvider( /> )} - + - reload()}>Close + reload()} + > + Close + diff --git a/public/controllers/register-agent/containers/steps/steps.scss b/public/controllers/register-agent/containers/steps/steps.scss index d18ea5c07e..b6ef8d579a 100644 --- a/public/controllers/register-agent/containers/steps/steps.scss +++ b/public/controllers/register-agent/containers/steps/steps.scss @@ -3,7 +3,6 @@ font-weight: 700; font-size: 16px; letter-spacing: 0.6px; - color: #343741; flex-direction: row; } @@ -12,7 +11,6 @@ font-weight: 400; font-size: 14px; line-height: 24px; - color: #343741; margin-bottom: 9px; } @@ -21,7 +19,6 @@ font-weight: 400; font-size: 14px; line-height: 24px; - color: #343741; margin-bottom: 20px; } diff --git a/public/controllers/register-agent/containers/steps/steps.tsx b/public/controllers/register-agent/containers/steps/steps.tsx index 95e14d2144..5e8b06f120 100644 --- a/public/controllers/register-agent/containers/steps/steps.tsx +++ b/public/controllers/register-agent/containers/steps/steps.tsx @@ -1,5 +1,5 @@ import React, { Fragment, useEffect, useState } from 'react'; -import { EuiSteps, EuiTitle } from '@elastic/eui'; +import { EuiSteps } from '@elastic/eui'; import './steps.scss'; import { OPERATING_SYSTEMS_OPTIONS } from '../../utils/register-agent-data'; import { @@ -20,7 +20,14 @@ import CommandOutput from '../../components/command-output/command-output'; import ServerAddressTitle from '../../components/server-address/server-address-title'; import ServerAddressInput from '../../components/server-address/server-address-input'; import OptionalsInputs from '../../components/optionals-inputs/optionals-inputs'; -import { getAgentCommandsStepStatus, tFormStepsStatus, getOSSelectorStepStatus, getServerAddressStepStatus, getOptionalParameterStepStatus, showCommandsSections } from '../../services/register-agent-steps-status-services'; +import { + getAgentCommandsStepStatus, + tFormStepsStatus, + getOSSelectorStepStatus, + getServerAddressStepStatus, + getOptionalParameterStepStatus, + showCommandsSections, +} from '../../services/register-agent-steps-status-services'; interface IStepsProps { needsPassword: boolean; @@ -63,8 +70,12 @@ export const Steps = ({ OPERATING_SYSTEMS_OPTIONS, ); setRegisterAgentFormValues(registerAgentFormValuesParsed); - setInstallCommandStepStatus(getAgentCommandsStepStatus(form.fields, installCommandWasCopied)) - setStartCommandStepStatus(getAgentCommandsStepStatus(form.fields, startCommandWasCopied)) + setInstallCommandStepStatus( + getAgentCommandsStepStatus(form.fields, installCommandWasCopied), + ); + setStartCommandStepStatus( + getAgentCommandsStepStatus(form.fields, startCommandWasCopied), + ); }, [form.fields]); const { installCommand, startCommand, selectOS, setOptionalParams } = @@ -75,9 +86,11 @@ export const Steps = ({ // install - start commands step state const [installCommandWasCopied, setInstallCommandWasCopied] = useState(false); - const [installCommandStepStatus, setInstallCommandStepStatus] = useState(getAgentCommandsStepStatus(form.fields, false)) + const [installCommandStepStatus, setInstallCommandStepStatus] = + useState(getAgentCommandsStepStatus(form.fields, false)); const [startCommandWasCopied, setStartCommandWasCopied] = useState(false); - const [startCommandStepStatus, setStartCommandStepStatus] = useState(getAgentCommandsStepStatus(form.fields, false)) + const [startCommandStepStatus, setStartCommandStepStatus] = + useState(getAgentCommandsStepStatus(form.fields, false)); useEffect(() => { if ( @@ -92,19 +105,23 @@ export const Steps = ({ }, [registerAgentFormValues]); useEffect(() => { - setInstallCommandStepStatus(getAgentCommandsStepStatus(form.fields, installCommandWasCopied)) - }, [installCommandWasCopied]) + setInstallCommandStepStatus( + getAgentCommandsStepStatus(form.fields, installCommandWasCopied), + ); + }, [installCommandWasCopied]); useEffect(() => { - setStartCommandStepStatus(getAgentCommandsStepStatus(form.fields, startCommandWasCopied)) - }, [startCommandWasCopied]) + setStartCommandStepStatus( + getAgentCommandsStepStatus(form.fields, startCommandWasCopied), + ); + }, [startCommandWasCopied]); const registerAgentFormSteps = [ { title: ( - -

Select the package to download and install on your system:

-
+ + Select the package to download and install on your system: + ), children: osCard, status: getOSSelectorStepStatus(form.fields), @@ -117,11 +134,7 @@ export const Steps = ({ ...(!(!needsPassword || hideTextPassword) ? [ { - title: ( - -

Wazuh password

-
- ), + title: Wazuh password, children: ( { @@ -133,21 +146,19 @@ export const Steps = ({ ] : []), { - title: ( - -

Optional settings

-
- ), + title: Optional settings, children: , - status: getOptionalParameterStepStatus(form.fields, installCommandWasCopied, startCommandWasCopied) + status: getOptionalParameterStepStatus( + form.fields, + installCommandWasCopied, + startCommandWasCopied, + ), }, { title: ( - -

- Run the following commands to download and install the Wazuh agent: -

-
+ + Run the following commands to download and install the Wazuh agent: + ), children: ( -

Start the Wazuh agent:

- - ), + title: Start the Wazuh agent:, children: ( tr { color: #dfe5ef; } -#wz-search-filter-bar-input{ +#wz-search-filter-bar-input { box-shadow: none; } -.kuiLocalSearchInput, .kuiLocalSearchInput:focus { +.kuiLocalSearchInput, +.kuiLocalSearchInput:focus { border: 1px solid #343741 !important; background: #16171c; color: #dfe5ef; } .wzMultipleSelector .panel-primary { - border: 1px solid #343741!important; + border: 1px solid #343741 !important; -webkit-box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important; box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important; border-radius: 2px; @@ -239,11 +243,12 @@ table thead > tr { border-left: 1px dashed #343741; } -md-dialog.md-default-theme.md-content-overflow .md-actions, -md-dialog.md-content-overflow .md-actions, -md-dialog.md-default-theme.md-content-overflow md-dialog-actions, -md-dialog.md-content-overflow md-dialog-actions, -md-divider.md-default-theme, md-divider { +md-dialog.md-default-theme.md-content-overflow .md-actions, +md-dialog.md-content-overflow .md-actions, +md-dialog.md-default-theme.md-content-overflow md-dialog-actions, +md-dialog.md-content-overflow md-dialog-actions, +md-divider.md-default-theme, +md-divider { border-top-color: rgb(52, 55, 65); } @@ -272,18 +277,18 @@ md-divider.md-default-theme, md-divider { background-color: #0b4462; } -.CodeMirror-hints{ +.CodeMirror-hints { background-color: #16171c !important; border-color: #000; - color: #dfe5ef!important; + color: #dfe5ef !important; } -.CodeMirror-hint{ - color: #dfe5ef!important; +.CodeMirror-hint { + color: #dfe5ef !important; } -.CodeMirror-hint:hover{ - background-color: #25262E; +.CodeMirror-hint:hover { + background-color: #25262e; } .wz-input-text { @@ -293,7 +298,7 @@ md-divider.md-default-theme, md-divider { } .wz-menu { - box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3)!important; + box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3) !important; } .wz-menu-select { @@ -309,29 +314,36 @@ md-divider.md-default-theme, md-divider { } .extraHeader { - border-bottom: 1px solid #2e2f34!important; + border-bottom: 1px solid #2e2f34 !important; } -.wzMultipleSelectorAdding{ - background-color: #037200!important; +.wzMultipleSelectorAdding { + background-color: #037200 !important; } -.wzMultipleSelectorRemoving{ - background-color: #990000!important; +.wzMultipleSelectorRemoving { + background-color: #990000 !important; } -.wzMultipleSelectorSelect{ +.wzMultipleSelectorSelect { background-color: #16171c; border: 1px solid rgb(52, 55, 65); } -.wz-button, .wz-button-groups, .refresh-agents-btn { - background-color: #1BA9F5 !important; - border-color: #1BA9F5 !important; +.wz-button, +.wz-button-groups, +.refresh-agents-btn { + background-color: #1ba9f5 !important; + border-color: #1ba9f5 !important; color: #000 !important; } -.wz-button-groups.active, .wz-button-groups:not([disabled]):hover, .wz-button.active, .wz-button:not([disabled]):hover, .wz-button-flat:not([disabled]):hover, .refresh-agents-btn:hover { +.wz-button-groups.active, +.wz-button-groups:not([disabled]):hover, +.wz-button.active, +.wz-button:not([disabled]):hover, +.wz-button-flat:not([disabled]):hover, +.refresh-agents-btn:hover { background-color: #0a9dec !important; border-color: #0a9dec !important; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 2px 2px -1px rgba(0, 0, 0, 0.3) !important; @@ -339,77 +351,78 @@ md-divider.md-default-theme, md-divider { } .kuiButton--hollow:hover { - color: #006E8A !important; + color: #006e8a !important; text-decoration: underline !important; } - .wz-menu-select { - filter: invert(0) !important; + filter: invert(0) !important; } -.logtest{ - border-left: 1px solid #343741!important; - box-shadow: -2px 0px 2px -1px rgba(0, 0, 0, 0.3)!important; +.logtest { + border-left: 1px solid #343741 !important; + box-shadow: -2px 0px 2px -1px rgba(0, 0, 0, 0.3) !important; background: #1a1b20; z-index: 10; } .wz-menu-left-side { - border-right: 1px solid #343741!important; - background: #1d1e24!important; + border-right: 1px solid #343741 !important; + background: #1d1e24 !important; } .wz-menu-sections { background: #1a1b20; } - -.wz-module-header-agent, .wz-module-header-nav { - border-bottom: 1px solid #343741!important; - background: #1d1e24!important; +.wz-module-header-agent, +.wz-module-header-nav { + border-bottom: 1px solid #343741 !important; + background: #1d1e24 !important; } .wz-welcome-page-agent-info { - box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), 0 1px 5px -2px rgba(0, 0, 0, 0.3)!important; - background: #1d1e24!important; + box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), + 0 1px 5px -2px rgba(0, 0, 0, 0.3) !important; + background: #1d1e24 !important; } -.wz-welcome-page-agent-info .wz-welcome-page-agent-info-details{ - background: #1a1b20!important; - border-bottom: 1px solid #343741!important; +.wz-welcome-page-agent-info .wz-welcome-page-agent-info-details { + background: #1a1b20 !important; + border-bottom: 1px solid #343741 !important; } .details-row { - background: #16171c!important; - border-top: 1px solid #343741!important; + background: #16171c !important; + border-top: 1px solid #343741 !important; } -.wz-inventory{ +.wz-inventory { .detail-tooltip { background-color: #16171c; } } .flyout-body .euiAccordion { - border-bottom: 1px solid #343741!important; + border-bottom: 1px solid #343741 !important; } .module-discover-table .euiTableRow.euiTableRow-isExpandedRow .euiTableRowCell { - background: #1d1e24!important; + background: #1d1e24 !important; } .module-discover-table .euiTableRow-isExpandedRow .euiTableCellContent { - background: #1d1e24!important; + background: #1d1e24 !important; } -.wz-search-bar > div > div > div.euiComboBox__inputWrap{ - background: #16171c!important; - box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.2), 0 3px 2px -2px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(255, 255, 255, 0.1)!important; +.wz-search-bar > div > div > div.euiComboBox__inputWrap { + background: #16171c !important; + box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.2), + 0 3px 2px -2px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(255, 255, 255, 0.1) !important; } .euiComboBoxPlaceholder { - color: #DFE5EF!important; + color: #dfe5ef !important; } svg .legend text { @@ -418,7 +431,7 @@ svg .legend text { /* welcome-agent */ -.wz-welcome-page-agent-tabs{ +.wz-welcome-page-agent-tabs { padding: 12px 16px 1px 10px; min-height: 54px; border-bottom: 1px solid #343741; @@ -436,15 +449,16 @@ svg .legend text { .wz-menu-agent-info { background-color: #1a1b20; - border-bottom: 1px solid #343741!important; + border-bottom: 1px solid #343741 !important; } .flyout-row { border: none; } -.application .euiAccordion, .flyout-body .euiAccordion { - border-bottom: 1px solid #343741!important; +.application .euiAccordion, +.flyout-body .euiAccordion { + border-bottom: 1px solid #343741 !important; } .sidepanel-infoBtnStyle { diff --git a/public/utils/assets.ts b/public/utils/assets.ts index 12d02c6029..771139a85a 100644 --- a/public/utils/assets.ts +++ b/public/utils/assets.ts @@ -1,7 +1,8 @@ import { ASSETS_BASE_URL_PREFIX } from '../../common/constants'; import { getUiSettings } from '../kibana-services'; -export const getAssetURL = (assetURL: string) => `${ASSETS_BASE_URL_PREFIX}${assetURL}`; +export const getAssetURL = (assetURL: string) => + `${ASSETS_BASE_URL_PREFIX}${assetURL}`; export const getThemeAssetURL = (asset: string, theme?: string) => { theme = theme || (getUiSettings()?.get('theme:darkMode') ? 'dark' : 'light'); From 2fc1dc5203fcc958a664b8b5ebe85ae9b012b394 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:21:27 -0300 Subject: [PATCH 10/46] [Redesign add agent] Add and validate register agent commands (#5622) * Add show/hide password in command component * Add protocol and password types * Add more step status methods * Add os commands service * Resolve strings replacements in command component * Change macos packages name by arch * Add \n to the macos params * Fixed parsed macos params inside echo * Add -e in mac os install command * Remove sudo from macos install command with echo * Add sudo to linux before optional params * Fix PR review comments --- .../command-output/command-output.tsx | 55 ++++++-- .../register-agent/register-agent.tsx | 18 +-- .../register-agent/containers/steps/steps.tsx | 63 +++++---- .../core/config/os-commands-definitions.ts | 76 +++++------ .../core/register-commands/types.ts | 5 +- .../register-agent-os-commands-services.tsx | 129 ++++++++++++++++++ .../services/register-agent-services.tsx | 3 +- .../register-agent-steps-status-services.tsx | 16 +++ 8 files changed, 271 insertions(+), 94 deletions(-) create mode 100644 public/controllers/register-agent/services/register-agent-os-commands-services.tsx diff --git a/public/controllers/register-agent/components/command-output/command-output.tsx b/public/controllers/register-agent/components/command-output/command-output.tsx index 88830a66c7..dafb26d4b5 100644 --- a/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/public/controllers/register-agent/components/command-output/command-output.tsx @@ -3,19 +3,22 @@ import { EuiCopy, EuiIcon, EuiSpacer, + EuiSwitch, + EuiSwitchEvent, EuiText, } from '@elastic/eui'; -import React, { Fragment, useState } from 'react'; +import React, { Fragment, useEffect, useState } from 'react'; interface ICommandSectionProps { commandText: string; showCommand: boolean; onCopy: () => void; os?: 'WINDOWS' | string; + password?: string; } export default function CommandOutput(props: ICommandSectionProps) { - const { commandText, showCommand, onCopy, os } = props; + const { commandText, showCommand, onCopy, os, password } = props; const getHighlightCodeLanguage = (os: 'WINDOWS' | string) => { if (os.toLowerCase() === 'windows') { return 'powershell'; @@ -23,15 +26,45 @@ export default function CommandOutput(props: ICommandSectionProps) { return 'bash'; } }; - - const [language, setLanguage] = useState(getHighlightCodeLanguage(os || '')); + const [havePassword, setHavePassword] = useState(false); + const [showPassword, setShowPassword] = useState(false); const onHandleCopy = (command: any) => { onCopy && onCopy(); - return command + return command; // the return is needed to avoid a bug in EuiCopy }; - const [commandToCopy, setCommandToCopy] = useState(commandText); + const [commandToShow, setCommandToShow] = useState(commandText); + + useEffect(() => { + if (password) { + setHavePassword(true); + osdfucatePassword(password); + } else { + setHavePassword(false); + setCommandToShow(commandText); + } + }, [password, commandText, showPassword]) + + const osdfucatePassword = (password: string) => { + if(!password) return; + if(!commandText) return; + + if(showPassword){ + setCommandToShow(commandText); + }else{ + // search password in commandText and replace with * for every character + const findPassword = commandText.search(password); + if (findPassword > -1) { + let command = commandText; + setCommandToShow(command.replace(/WAZUH_REGISTRATION_PASSWORD='([^']+)'/,`WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`)); + } + } + } + + const onChangeShowPassword = (event: EuiSwitchEvent) => { + setShowPassword(event.target.checked); + } return ( @@ -44,12 +77,15 @@ export default function CommandOutput(props: ICommandSectionProps) { }} language={getHighlightCodeLanguage(os || '')} > - {showCommand ? commandText : ''} + {showCommand ? commandToShow : ''} {showCommand && ( - {commandToCopy => ( -
onHandleCopy(commandToCopy)}> + {copy => ( +
onHandleCopy(copy())} + >

Copy command

@@ -59,6 +95,7 @@ export default function CommandOutput(props: ICommandSectionProps) { )}
+ {showCommand && havePassword ? : null} ); diff --git a/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/public/controllers/register-agent/containers/register-agent/register-agent.tsx index f9d1fa499e..8ae23213cd 100644 --- a/public/controllers/register-agent/containers/register-agent/register-agent.tsx +++ b/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -48,19 +48,14 @@ export const RegisterAgent = withReduxProvider( const configuration = useSelector( (state: { appConfig: { data: any } }) => state.appConfig.data, ); - const [wazuhVersion, setWazuhVersion] = useState(''); const [haveUdpProtocol, setHaveUdpProtocol] = useState( false, ); - const [haveConnectionSecure, setHaveConnectionSecure] = useState< - boolean | null - >(false); const [loading, setLoading] = useState(false); const [wazuhPassword, setWazuhPassword] = useState(''); const [groups, setGroups] = useState([]); const [needsPassword, setNeedsPassword] = useState(false); - const [hideTextPassword, setHideTextPassword] = useState(false); const initialFields: FormConfiguration = { operatingSystemSelection: { @@ -102,7 +97,6 @@ export const RegisterAgent = withReduxProvider( const remoteConfig = await getMasterRemoteConfiguration(); if (remoteConfig) { setHaveUdpProtocol(remoteConfig.isUdp); - setHaveConnectionSecure(remoteConfig.haveSecureConnection); } }; @@ -123,23 +117,19 @@ export const RegisterAgent = withReduxProvider( const fetchData = async () => { try { const wazuhVersion = await getWazuhVersion(); - let wazuhPassword = ''; - let hideTextPassword = false; await getRemoteConfig(); const authInfo = await getAuthInfo(); + // get wazuh password configuration + let wazuhPassword = ''; const needsPassword = (authInfo.auth || {}).use_password === 'yes'; if (needsPassword) { wazuhPassword = configuration['enrollment.password'] || authInfo['authd.pass'] || ''; - if (wazuhPassword) { - hideTextPassword = true; - } } const groups = await getGroups(); setNeedsPassword(needsPassword); - setHideTextPassword(hideTextPassword); setWazuhPassword(wazuhPassword); setWazuhVersion(wazuhVersion); setGroups(groups); @@ -218,13 +208,11 @@ export const RegisterAgent = withReduxProvider( )} diff --git a/public/controllers/register-agent/containers/steps/steps.tsx b/public/controllers/register-agent/containers/steps/steps.tsx index 5e8b06f120..da865cf9de 100644 --- a/public/controllers/register-agent/containers/steps/steps.tsx +++ b/public/controllers/register-agent/containers/steps/steps.tsx @@ -1,5 +1,5 @@ import React, { Fragment, useEffect, useState } from 'react'; -import { EuiSteps } from '@elastic/eui'; +import { EuiCallOut, EuiLink, EuiSteps, EuiTitle } from '@elastic/eui'; import './steps.scss'; import { OPERATING_SYSTEMS_OPTIONS } from '../../utils/register-agent-data'; import { @@ -27,47 +27,49 @@ import { getServerAddressStepStatus, getOptionalParameterStepStatus, showCommandsSections, + getPasswordStepStatus, } from '../../services/register-agent-steps-status-services'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; interface IStepsProps { needsPassword: boolean; - hideTextPassword: boolean; form: UseFormReturn; osCard: React.ReactElement; connection: { isUDP: boolean; - isSecure: boolean; }; wazuhPassword: string; } export const Steps = ({ needsPassword, - hideTextPassword, form, osCard, connection, wazuhPassword, }: IStepsProps) => { + const initialParsedFormValues = { + operatingSystem: { + name: '', + architecture: '', + }, + optionalParams: { + agentGroups: '', + agentName: '', + serverAddress: '', + wazuhPassword, + protocol: connection.isUDP ? 'UDP' : '', + }, + } as IParseRegisterFormValues; const [registerAgentFormValues, setRegisterAgentFormValues] = - useState({ - operatingSystem: { - name: '', - architecture: '', - }, - optionalParams: { - agentGroups: '', - agentName: '', - serverAddress: '', - wazuhPassword, - }, - }); + useState(initialParsedFormValues); useEffect(() => { // get form values and parse them divided in OS and optional params const registerAgentFormValuesParsed = parseRegisterAgentFormValues( getRegisterAgentFormValues(form), OPERATING_SYSTEMS_OPTIONS, + initialParsedFormValues, ); setRegisterAgentFormValues(registerAgentFormValuesParsed); setInstallCommandStepStatus( @@ -99,7 +101,7 @@ export const Steps = ({ ) { selectOS(registerAgentFormValues.operatingSystem as tOperatingSystem); } - setOptionalParams(registerAgentFormValues.optionalParams); + setOptionalParams({ ...registerAgentFormValues.optionalParams }); setInstallCommandWasCopied(false); setStartCommandWasCopied(false); }, [registerAgentFormValues]); @@ -131,17 +133,32 @@ export const Steps = ({ children: , status: getServerAddressStepStatus(form.fields), }, - ...(!(!needsPassword || hideTextPassword) + ...(needsPassword && !wazuhPassword ? [ { title: Wazuh password, children: ( - - { - 'No ha establecido una contraseña. Se le asigno una por defecto' + + The Wazuh password is required but wasn't defined. Please check our{' '} + + steps + + } - + iconType='iInCircle' + className='warningForAgentName' + /> ), + status: getPasswordStepStatus(form.fields), }, ] : []), @@ -151,7 +168,6 @@ export const Steps = ({ status: getOptionalParameterStepStatus( form.fields, installCommandWasCopied, - startCommandWasCopied, ), }, { @@ -166,6 +182,7 @@ export const Steps = ({ showCommand={showCommandsSections(form.fields)} os={registerAgentFormValues.operatingSystem.name} onCopy={() => setInstallCommandWasCopied(true)} + password={registerAgentFormValues.optionalParams.wazuhPassword} /> ), status: installCommandStepStatus, diff --git a/public/controllers/register-agent/core/config/os-commands-definitions.ts b/public/controllers/register-agent/core/config/os-commands-definitions.ts index 73a370f917..562b487e1a 100644 --- a/public/controllers/register-agent/core/config/os-commands-definitions.ts +++ b/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -1,3 +1,4 @@ +import { getLinuxDEBInstallCommand, getLinuxRPMInstallCommand, getLinuxStartCommand, getMacOsInstallCommand, getMacosStartCommand, getWindowsInstallCommand, getWindowsStartCommand } from '../../services/register-agent-os-commands-services'; import { IOSDefinition, tOptionalParams } from '../register-commands/types'; // Defined OS combinations @@ -54,7 +55,8 @@ export type tOptionalParameters = | 'serverAddress' | 'agentName' | 'agentGroups' - | 'wazuhPassword'; + | 'wazuhPassword' + | 'protocol'; /////////////////////////////////////////////////////////////////// /// Operating system commands definitions @@ -66,34 +68,30 @@ const linuxDefinition: IOSDefinition = { { architecture: 'DEB amd64', urlPackage: props => - `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64`, - installCommand: props => - `sudo yum install -y ${props.urlPackage} ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `sudo systemctl start wazuh-agent`, + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64.deb`, + installCommand: props => getLinuxDEBInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), }, { architecture: 'DEB aarch64', urlPackage: props => - `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}`, - installCommand: props => - `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `sudo systemctl start wazuh-agent`, + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_aarch64.deb`, + installCommand: props => getLinuxDEBInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), }, { architecture: 'RPM amd64', urlPackage: props => - `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64`, - installCommand: props => - `sudo yum install -y ${props.urlPackage} ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `sudo systemctl start wazuh-agent`, + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.rpm`, + installCommand: props => getLinuxRPMInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), }, { architecture: 'RPM aarch64', urlPackage: props => - `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64`, - installCommand: props => - `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `sudo systemctl start wazuh-agent`, + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.aarch64.rpm`, + installCommand: props => getLinuxRPMInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), }, ], }; @@ -104,12 +102,9 @@ const windowsDefinition: IOSDefinition = { { architecture: 'MSI 32/64', urlPackage: props => - `https://packages.wazuh.com/4.x/windows/wazuh-agent-${props.wazuhVersion}-1`, - installCommand: props => - `Invoke-WebRequest -Uri ${ - props.urlPackage - } -OutFile \${env.tmp}\\wazuh-agent; msiexec.exe /i \${env.tmp}\\wazuh-agent /q ${props.optionals?.serverAddress || ''} ${props.optionals?.agentName || ''} ${props.optionals?.agentGroups || ''} ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `Start-Service -Name wazuh-agent`, + `https://packages.wazuh.com/4.x/windows/wazuh-agent-${props.wazuhVersion}-1.msi`, + installCommand: props => getWindowsInstallCommand(props), + startCommand: props => getWindowsStartCommand(props), }, ], }; @@ -120,30 +115,16 @@ const macDefinition: IOSDefinition = { { architecture: 'Intel', urlPackage: props => - `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1`, - installCommand: props => - `mac -so wazuh-agent.pkg ${ - props.urlPackage - } && sudo launchctl setenv && sudo installer -pkg ./wazuh-agent.pkg -target / - ${props.optionals?.serverAddress || ''} - ${props.optionals?.agentName || ''} - ${props.optionals?.agentGroups || ''} - ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `sudo /Library/Ossec/bin/wazuh-control start`, + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1.intel64.pkg`, + installCommand: props => getMacOsInstallCommand(props), + startCommand: props => getMacosStartCommand(props), }, { architecture: 'Apple Silicon', urlPackage: props => - `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1`, - installCommand: props => - `mac -so wazuh-agent.pkg ${ - props.urlPackage - } && sudo launchctl setenv && sudo installer -pkg ./wazuh-agent.pkg -target - ${props.optionals?.serverAddress || ''} - ${props.optionals?.agentName || ''} - ${props.optionals?.agentGroups || ''} - ${props.optionals?.wazuhPassword || ''}`, - startCommand: props => `sudo /Library/Ossec/bin/wazuh-control start`, + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1.arm64.pkg`, + installCommand: props => getMacOsInstallCommand(props), + startCommand: props => getMacosStartCommand(props), }, ], }; @@ -184,8 +165,15 @@ export const optionalParamsDefinitions: tOptionalParams = { return parsedValue ? `${property}='${parsedValue}'` : ''; }, }, + protocol: { + property: 'WAZUH_PROTOCOL', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, wazuhPassword: { - property: 'WAZUH_PASSWORD', + property: 'WAZUH_REGISTRATION_PASSWORD', getParamCommand: props => { const { property, value } = props; return value !== '' ? `${property}='${value}'` : ''; diff --git a/public/controllers/register-agent/core/register-commands/types.ts b/public/controllers/register-agent/core/register-commands/types.ts index ebce4b3dfd..243477a999 100644 --- a/public/controllers/register-agent/core/register-commands/types.ts +++ b/public/controllers/register-agent/core/register-commands/types.ts @@ -24,12 +24,13 @@ interface IOptionalParamsWithValues { } -type tOSEntryProps = IOSProps & IOptionalParamsWithValues; +export type tOSEntryProps = IOSProps & IOptionalParamsWithValues; +export type tOSEntryInstallCommand = tOSEntryProps & { urlPackage: string }; export interface IOSCommandsDefinition { architecture: OS['architecture']; urlPackage: (props: tOSEntryProps) => string; - installCommand: (props: tOSEntryProps & { urlPackage: string }) => string; + installCommand: (props: tOSEntryInstallCommand) => string; startCommand: (props: tOSEntryProps) => string; } diff --git a/public/controllers/register-agent/services/register-agent-os-commands-services.tsx b/public/controllers/register-agent/services/register-agent-os-commands-services.tsx new file mode 100644 index 0000000000..818a91945a --- /dev/null +++ b/public/controllers/register-agent/services/register-agent-os-commands-services.tsx @@ -0,0 +1,129 @@ +import { tOptionalParameters } from '../core/config/os-commands-definitions'; +import { + IOptionalParameters, + tOSEntryInstallCommand, + tOSEntryProps, +} from '../core/register-commands/types'; +import { tOperatingSystem } from '../hooks/use-register-agent-commands.test'; + +const getAllOptionals = ( + optionals: IOptionalParameters, + osName?: tOperatingSystem['name'], +) => { + // create paramNameOrderList, which is an array of the keys of optionals add interface + const paramNameOrderList: (keyof IOptionalParameters)[] = + ['serverAddress', 'wazuhPassword', 'agentGroups', 'agentName', 'protocol']; + + if (!optionals) return ''; + let paramsText = Object.entries(paramNameOrderList).reduce( + (acc, [key, value]) => { + if (optionals[value]) { + acc += `${optionals[value]} `; + } + return acc; + }, + '', + ); + + if (osName && osName.toLowerCase() === 'windows' && optionals.serverAddress) { + // when os is windows we must to add wazuh registration server with server address + paramsText = + paramsText + `WAZUH_REGISTRATION_SERVER=${optionals.serverAddress.replace('WAZUH_MANAGER=','')} `; + } + + return paramsText; +}; + +const getAllOptionalsMacos = ( + optionals: IOptionalParameters +) => { + // create paramNameOrderList, which is an array of the keys of optionals add interface + const paramNameOrderList: (keyof IOptionalParameters)[] = + ['serverAddress', 'wazuhPassword', 'agentGroups', 'agentName', 'protocol']; + + if (!optionals) return ''; + return Object.entries(paramNameOrderList).reduce( + (acc, [key, value]) => { + if (optionals[value]) { + acc += `${optionals[value]}\\n`; + } + return acc; + }, + '', + ); +}; + +/******* Linux *******/ + + +// Install command +export const getLinuxRPMInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage } = props; + return `sudo ${ + optionals && getAllOptionals(optionals) + }yum install -y ${urlPackage}`; +}; + +export const getLinuxDEBInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage } = props; + return `curl -so wazuh-agent.deb ${urlPackage} && sudo ${ + optionals && getAllOptionals(optionals) + }dpkg -i ./wazuh-agent.deb`; +}; + +// Start command +export const getLinuxStartCommand = ( + _props: tOSEntryProps, +) => { + return `sudo systemctl daemon-reload\nsudo systemctl enable wazuh-agent\nsudo systemctl start wazuh-agent`; +}; + +/******** Windows ********/ + +export const getWindowsInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage, name } = props; + return `Invoke-WebRequest -Uri ${urlPackage} -OutFile \${env.tmp}\\wazuh-agent; msiexec.exe /i \${env.tmp}\\wazuh-agent /q ${ + optionals && getAllOptionals(optionals, name) + }`; +}; + +export const getWindowsStartCommand = ( + _props: tOSEntryProps, +) => { + return `NET START WazuhSvc`; +}; + +/******** MacOS ********/ + +export const getMacOsInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage } = props; + // Set macOS installation script with environment variables + const optionalsText = optionals && getAllOptionalsMacos(optionals); + const macOSInstallationOptions = `${optionalsText}` + .replace(/\' ([a-zA-Z])/g, "' && $1") // Separate environment variables with && + .replace(/\"/g, '\\"') // Escape double quotes + .trim(); + + // If no variables are set, the echo will be empty + const macOSInstallationSetEnvVariablesScript = macOSInstallationOptions + ? `echo -e "${macOSInstallationOptions}" > /tmp/wazuh_envs && ` + : ``; + + // Merge environment variables with installation script + const macOSInstallationScript = `curl -so wazuh-agent.pkg ${urlPackage} && ${macOSInstallationSetEnvVariablesScript}sudo installer -pkg ./wazuh-agent.pkg -target /`; + return macOSInstallationScript; +}; + +export const getMacosStartCommand = ( + _props: tOSEntryProps, +) => { + return `sudo /Library/Ossec/bin/wazuh-control start`; +}; diff --git a/public/controllers/register-agent/services/register-agent-services.tsx b/public/controllers/register-agent/services/register-agent-services.tsx index ed32979bab..8200224bb2 100644 --- a/public/controllers/register-agent/services/register-agent-services.tsx +++ b/public/controllers/register-agent/services/register-agent-services.tsx @@ -260,9 +260,10 @@ export interface IParseRegisterFormValues { export const parseRegisterAgentFormValues = ( formValues: { name: keyof UseFormReturn['fields']; value: any }[], OSOptionsDefined: RegisterAgentData[], + initialValues?: IParseRegisterFormValues ) => { // return the values form the formFields and the value property - const parsedForm = { + const parsedForm = initialValues || { operatingSystem: { architecture: '', name: '', diff --git a/public/controllers/register-agent/services/register-agent-steps-status-services.tsx b/public/controllers/register-agent/services/register-agent-steps-status-services.tsx index 7065d38290..90e7ca64ae 100644 --- a/public/controllers/register-agent/services/register-agent-steps-status-services.tsx +++ b/public/controllers/register-agent/services/register-agent-steps-status-services.tsx @@ -143,3 +143,19 @@ export const getOptionalParameterStepStatus = ( return 'current'; } }; + + +export const getPasswordStepStatus = ( + formFields: UseFormReturn['fields'], +): tFormStepsStatus => { + if ( + !formFields.operatingSystemSelection.value || + formFields.operatingSystemSelection.error || + !formFields.serverAddress.value || + formFields.serverAddress.error + ) { + return 'disabled'; + }else{ + return 'complete'; + } +} \ No newline at end of file From b92c8c5220620483b4321a76b024ae81f966fc31 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 3 Jul 2023 10:34:09 -0300 Subject: [PATCH 11/46] Fixed imports in tests --- .../checkbox-group/checkbox-group.test.tsx | 2 +- .../os-selector/os-card/os-card.test.tsx | 2 +- .../register-agent/register-agent.test.tsx | 20 ------------------- 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.test.tsx diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx index e2dec80399..7a9e9e79a2 100644 --- a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx +++ b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { CheckboxGroupComponent } from '../../step-one/checkbox-group/checkbox-group'; +import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; describe('CheckboxGroupComponent', () => { const data = ['Option 1', 'Option 2', 'Option 3']; diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx index ebb927853d..f8f379e20c 100644 --- a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx +++ b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; -import { OsCard } from '../../step-one/os-card/os-card'; +import { OsCard } from '../os-card/os-card'; describe('OsCard', () => { test('renders three cards with different titles', () => { diff --git a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.test.tsx b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.test.tsx deleted file mode 100644 index 5f4dd452be..0000000000 --- a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.test.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom/extend-expect'; -import { RegisterAgent } from './register-agent'; - -describe('RegisterAgent', () => { - test('renders the component', () => { - const mockHasAgents = jest.fn(); - - render(); - - // Verifica que el título esté presente - const titleElement = screen.getByText('Deploy new agent'); - expect(titleElement).toBeInTheDocument(); - - // Verifica que el componente InputForm esté presente - const component = screen.getByTestId('os-card'); - expect(component).toBeInTheDocument(); - }); -}); From 537fb6870db3438b3f791d256dbe9fd2b1a68857 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 3 Jul 2023 10:57:59 -0300 Subject: [PATCH 12/46] Fix components unit tests --- .../checkbox-group/checkbox-group.test.tsx | 8 +++--- .../os-selector/os-card/os-card.test.tsx | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx index 7a9e9e79a2..d42289ff98 100644 --- a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx +++ b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx @@ -22,9 +22,9 @@ describe('CheckboxGroupComponent', () => { const checkboxItems = screen.getAllByRole('radio'); expect(checkboxItems).toHaveLength(data.length); - expect(checkboxItems[0]).toHaveAttribute('id', 'option-0-0'); - expect(checkboxItems[1]).toHaveAttribute('id', 'option-0-1'); - expect(checkboxItems[2]).toHaveAttribute('id', 'option-0-2'); + expect(checkboxItems[0]).toHaveAttribute('id', 'Option 1'); + expect(checkboxItems[1]).toHaveAttribute('id', 'Option 2'); + expect(checkboxItems[2]).toHaveAttribute('id', 'Option 3'); expect(checkboxItems[0]).toBeChecked(); expect(checkboxItems[1]).not.toBeChecked(); @@ -52,7 +52,7 @@ describe('CheckboxGroupComponent', () => { expect(onOptionChange).toHaveBeenCalledTimes(1); expect(onOptionChange).toHaveBeenCalledWith( expect.objectContaining({ - target: { value: `option-${cardIndex}-1` }, + target: { value: `Option ${cardIndex}` }, }), ); }); diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx index f8f379e20c..59b0657948 100644 --- a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx +++ b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx @@ -3,6 +3,31 @@ import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { OsCard } from '../os-card/os-card'; +jest.mock('../../../../../kibana-services', () => ({ + ...(jest.requireActual('../../../../../kibana-services') as object), + getHttp: jest.fn().mockReturnValue({ + basePath: { + get: () => { + return 'http://localhost:5601'; + }, + prepend: (url) => { + return `http://localhost:5601${url}`; + }, + }, + }), + getCookies: jest.fn().mockReturnValue({ + set: (name, value, options) => { + return true; + }, + get: () => { + return '{}'; + }, + remove: () => { + return; + }, + }), +})); + describe('OsCard', () => { test('renders three cards with different titles', () => { render(); From 7617c96fadfd74506aad24d570701bdb03d974a4 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 3 Jul 2023 11:23:40 -0300 Subject: [PATCH 13/46] Fix unit test checkbox group component --- .../os-selector/checkbox-group/checkbox-group.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx index d42289ff98..186fc0d240 100644 --- a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx +++ b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx @@ -6,7 +6,7 @@ import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; describe('CheckboxGroupComponent', () => { const data = ['Option 1', 'Option 2', 'Option 3']; const cardIndex = 0; - const selectedOption = 'option-0-0'; + const selectedOption = 'Option 1'; const onOptionChange = jest.fn(); test('renders checkbox items with correct labels', () => { @@ -52,7 +52,7 @@ describe('CheckboxGroupComponent', () => { expect(onOptionChange).toHaveBeenCalledTimes(1); expect(onOptionChange).toHaveBeenCalledWith( expect.objectContaining({ - target: { value: `Option ${cardIndex}` }, + target: { value: `Option 2` }, }), ); }); From 3d187b170d54294d6e054425ae25228326fa4d14 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 3 Jul 2023 11:59:34 -0300 Subject: [PATCH 14/46] Fix os card unit test with mock uiSettings --- .../components/os-selector/os-card/os-card.test.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx index 59b0657948..d01a27c141 100644 --- a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx +++ b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx @@ -26,6 +26,11 @@ jest.mock('../../../../../kibana-services', () => ({ return; }, }), + getUiSettings: jest.fn().mockReturnValue({ + get: (name) => { + return true; + }, + }), })); describe('OsCard', () => { From bade293e280bf78657a636eac0ff02abf85e845d Mon Sep 17 00:00:00 2001 From: "chantal.kelm" Date: Tue, 4 Jul 2023 12:00:58 -0300 Subject: [PATCH 15/46] modify the fqdn regex because it interferes with an ipv4 instance --- .../public/controllers/register-agent/utils/validations.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/main/public/controllers/register-agent/utils/validations.tsx b/plugins/main/public/controllers/register-agent/utils/validations.tsx index c3eab3de31..00fb93410f 100644 --- a/plugins/main/public/controllers/register-agent/utils/validations.tsx +++ b/plugins/main/public/controllers/register-agent/utils/validations.tsx @@ -8,7 +8,7 @@ // Minimum 3 labels export const validateServerAddress = (value: any) => { const isFQDN = - /^(?!-)(?!.*--)[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*(?:\.[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*){2,}$/; + /^(?!-)(?!.*--)(?!.*\d$)[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*(?:\.[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*){2,}$/; const isIP = /^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})$/; From d0b89f445532dcfa9477f658da6616be11cf4ac6 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> Date: Fri, 7 Jul 2023 07:00:39 -0300 Subject: [PATCH 16/46] [Redesign add page] Add form status callout message (#5634) * Add form status manager and unit tests * Add empty and invalid fields messages * Hide commands code block when exists warning messages * Fix fields names in warning messages * Updated CHANGELOG --- CHANGELOG.md | 2 + .../register-agent/containers/steps/steps.tsx | 85 ++++++++-- .../services/form-status-manager.test.tsx | 145 ++++++++++++++++++ .../services/form-status-manager.tsx | 116 ++++++++++++++ .../register-agent-steps-status-services.tsx | 77 +++++++--- 5 files changed, 392 insertions(+), 33 deletions(-) create mode 100644 plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx create mode 100644 plugins/main/public/controllers/register-agent/services/form-status-manager.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e32db3b0..276f72d427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ All notable changes to the Wazuh app project will be documented in this file. - Added new CLI to generate API data from specification file [#5519](https://github.com/wazuh/wazuh-kibana-app/pull/5519) - Added specific RBAC permissions to Security section [#5551](https://github.com/wazuh/wazuh-kibana-app/pull/5551) - Added development so that the images of the new agent deployment page also have dark mode. [#5620](https://github.com/wazuh/wazuh-kibana-app/pull/5620) +- Added register agent form status callout message [#5634](https://github.com/wazuh/wazuh-kibana-app/pull/5634) + ### Changed diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx index da865cf9de..26140391e9 100644 --- a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx @@ -28,6 +28,10 @@ import { getOptionalParameterStepStatus, showCommandsSections, getPasswordStepStatus, + getIncompleteSteps, + getInvalidFields, + tFormFieldsLabel, + tFormStepsLabel, } from '../../services/register-agent-steps-status-services'; import { webDocumentationLink } from '../../../../../common/services/web_documentation'; @@ -61,9 +65,17 @@ export const Steps = ({ protocol: connection.isUDP ? 'UDP' : '', }, } as IParseRegisterFormValues; + const [missingStepsName, setMissingStepsName] = useState( + [], + ); + const [invalidFieldsName, setInvalidFieldsName] = useState< + tFormFieldsLabel[] + >([]); const [registerAgentFormValues, setRegisterAgentFormValues] = useState(initialParsedFormValues); + const FORM_MESSAGE_CONJUNTION = ' and '; + useEffect(() => { // get form values and parse them divided in OS and optional params const registerAgentFormValuesParsed = parseRegisterAgentFormValues( @@ -78,6 +90,8 @@ export const Steps = ({ setStartCommandStepStatus( getAgentCommandsStepStatus(form.fields, startCommandWasCopied), ); + setMissingStepsName(getIncompleteSteps(form.fields) || []); + setInvalidFieldsName(getInvalidFields(form.fields) || []); }, [form.fields]); const { installCommand, startCommand, selectOS, setOptionalParams } = @@ -142,7 +156,8 @@ export const Steps = ({ color='warning' title={ - The Wazuh password is required but wasn't defined. Please check our{' '} + The Wazuh password is required but wasn't defined. Please + check our{' '} ), children: ( - setInstallCommandWasCopied(true)} - password={registerAgentFormValues.optionalParams.wazuhPassword} - /> + <> + {missingStepsName?.length ? ( + + ) : null} + {invalidFieldsName?.length ? ( + + ) : null} + {!missingStepsName?.length && !invalidFieldsName?.length ? ( + setInstallCommandWasCopied(true)} + password={registerAgentFormValues.optionalParams.wazuhPassword} + /> + ) : null} + ), status: installCommandStepStatus, }, { title: Start the Wazuh agent:, children: ( - setStartCommandWasCopied(true)} - /> + <> + {missingStepsName?.length ? ( + + ) : null} + {invalidFieldsName?.length ? ( + + ) : null} + {!missingStepsName?.length && !invalidFieldsName?.length ? ( + setStartCommandWasCopied(true)} + /> + ) : null} + ), status: startCommandStepStatus, }, diff --git a/plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx b/plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx new file mode 100644 index 0000000000..db512362f5 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx @@ -0,0 +1,145 @@ +import { + EnhancedFieldConfiguration, + UseFormReturn, +} from '../../../components/common/form/types'; +import { + FormStepsDependencies, + RegisterAgentFormStatusManager, +} from './form-status-manager'; + +const defaultFormFieldData: EnhancedFieldConfiguration = { + changed: true, + value: 'value1', + error: '', + currentValue: '', + initialValue: '', + type: 'text', + onChange: () => { + console.log('onChange'); + }, + setInputRef: () => { + console.log('setInputRef'); + }, + inputRef: null, +}; + +const formFieldsDefault: UseFormReturn['fields'] = { + field1: { + ...defaultFormFieldData, + value: '', + error: null, + }, + field2: { + ...defaultFormFieldData, + value: '', + error: 'error message', + }, + field3: { + ...defaultFormFieldData, + value: 'value valid', + error: null, + }, +}; + +describe('RegisterAgentFormStatusManager', () => { + it('should create a instance', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + }); + + it('should return the form status', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + const formStatus = registerAgentFormStatusManager.getFormStatus(); + expect(formStatus).toEqual({ + field1: 'empty', + field2: 'invalid', + field3: 'complete', + }); + }); + + it('should return the field status', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + const fieldStatus = registerAgentFormStatusManager.getFieldStatus('field1'); + expect(fieldStatus).toEqual('empty'); + }); + + it('should return error if fieldname not found', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + expect(() => + registerAgentFormStatusManager.getFieldStatus('field4'), + ).toThrowError('Fieldname not found'); + }); + + it('should return a INVALID when the step have an error', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1', 'field2'], + step2: ['field3'], + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getStepStatus('step1')).toEqual( + 'invalid', + ); + }); + + it('should return COMPLETE when the step have no errors and is not empty', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1', 'field2'], + step2: ['field3'], + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getStepStatus('step2')).toEqual( + 'complete', + ); + }); + + it('should return EMPTY when the step all fields empty', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1'], + step2: [ 'field2', + 'field3' ], + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getStepStatus('step1')).toEqual( + 'empty', + ); + }); + + it('should return all the steps status', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1'], + step2: [ 'field2', + 'field3' ], + step3: ['field3'] + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getFormStepsStatus()).toEqual({ + step1: 'empty', + step2: 'invalid', + step3: 'complete' + }); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/services/form-status-manager.tsx b/plugins/main/public/controllers/register-agent/services/form-status-manager.tsx new file mode 100644 index 0000000000..a250e2d413 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/form-status-manager.tsx @@ -0,0 +1,116 @@ +import { UseFormReturn } from '../../../components/common/form/types'; + +type FieldStatus = 'invalid' | 'empty' | 'complete'; +type FormStatus = { + [key: string]: FieldStatus; +}; + +type FormFields = UseFormReturn['fields']; +type FormFieldName = keyof FormFields; + +export type FormStepsDependencies = { + [key: string]: FormFieldName[]; +}; + +type FormStepsStatus = { + [key: string]: FieldStatus; +}; + +interface FormFieldsStatusManager { + getFieldStatus: (fieldname: FormFieldName) => FieldStatus; + getFormStatus: () => FormStatus; + getStepStatus: (stepName: string) => FieldStatus; + getFormStepsStatus: () => FormStepsStatus; +} + +export class RegisterAgentFormStatusManager implements FormFieldsStatusManager { + constructor( + private formFields: FormFields, + private formSteps?: FormStepsDependencies, + ) {} + + getFieldStatus = (fieldname: FormFieldName): FieldStatus => { + const field = this.formFields[fieldname]; + if (!field) { + throw Error('Fieldname not found'); + } + + if (field.error) { + return 'invalid'; + } + + if (field.value?.length === 0) { + return 'empty'; + } + + return 'complete'; + }; + + getFormStatus = (): FormStatus => { + const fieldNames = Object.keys(this.formFields); + const formStatus: FormStatus | object = {}; + + fieldNames.forEach((fieldName: string) => { + formStatus[fieldName] = this.getFieldStatus(fieldName); + }); + + return formStatus as FormStatus; + }; + + getStepStatus = (stepName: string): FieldStatus => { + if (!this.formSteps) { + throw Error('Form steps not defined'); + } + const stepFields = this.formSteps[stepName]; + if (!stepFields) { + throw Error('Step name not found'); + } + + const formStepStatus: FormStepsStatus | object = {}; + stepFields.forEach((fieldName: FormFieldName) => { + formStepStatus[fieldName] = this.getFieldStatus(fieldName); + }); + + const stepStatus = Object.values(formStepStatus); + + // if any is invalid + if (stepStatus.includes('invalid')) { + return 'invalid'; + } else if (stepStatus.includes('empty')) { + // if all are empty + return 'empty'; + } else { + // if all are complete + return 'complete'; + } + }; + + getFormStepsStatus = (): FormStepsStatus => { + if (!this.formSteps) { + throw Error('Form steps not defined'); + } + + const formStepsStatus: FormStepsStatus | object = {}; + Object.keys(this.formSteps).forEach((stepName: string) => { + formStepsStatus[stepName] = this.getStepStatus(stepName); + }); + + return formStepsStatus as FormStepsStatus; + }; + + getIncompleteSteps = (): string[] => { + const formStepsStatus = this.getFormStepsStatus(); + const notCompleteSteps = Object.entries(formStepsStatus).filter( + ([ _, status ]) => status === 'empty', + ); + return notCompleteSteps.map(( [ stepName, _]) => stepName); + }; + + getInvalidFields = (): string[] => { + const formStatus = this.getFormStatus(); + const invalidFields = Object.entries(formStatus).filter( + ([ _, status ]) => status === 'invalid', + ); + return invalidFields.map(([ fieldName, _ ]) => fieldName); + } +} diff --git a/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx b/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx index 90e7ca64ae..0136b0ec2b 100644 --- a/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx +++ b/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx @@ -1,5 +1,9 @@ import { EuiStepStatus } from '@elastic/eui'; import { UseFormReturn } from '../../../components/common/form/types'; +import { + FormStepsDependencies, + RegisterAgentFormStatusManager, +} from './form-status-manager'; const fieldsHaveErrors = ( fieldsToCheck: string[], @@ -19,7 +23,7 @@ const fieldsHaveErrors = ( return haveError; }; -const anyFieldIsComplete = ( +const fieldsAreEmpty = ( fieldsToCheck: string[], formFields: UseFormReturn['fields'], ) => { @@ -31,18 +35,13 @@ const anyFieldIsComplete = ( throw Error('fields to check are not defined in formFields'); } - if (fieldsHaveErrors(fieldsToCheck, formFields)) { - return false; - } - - if (fieldsAreEmpty(fieldsToCheck, formFields)) { - return false; - } - - return true; + const notEmpty = fieldsToCheck.some(key => { + return formFields[key]?.value?.length > 0; + }); + return !notEmpty; }; -const fieldsAreEmpty = ( +const anyFieldIsComplete = ( fieldsToCheck: string[], formFields: UseFormReturn['fields'], ) => { @@ -54,12 +53,18 @@ const fieldsAreEmpty = ( throw Error('fields to check are not defined in formFields'); } - const notEmpty = fieldsToCheck.some(key => { - return formFields[key]?.value?.length > 0; - }); - return !notEmpty; + if (fieldsHaveErrors(fieldsToCheck, formFields)) { + return false; + } + + if (fieldsAreEmpty(fieldsToCheck, formFields)) { + return false; + } + + return true; }; + export const showCommandsSections = ( formFields: UseFormReturn['fields'], ): boolean => { @@ -144,8 +149,7 @@ export const getOptionalParameterStepStatus = ( } }; - -export const getPasswordStepStatus = ( +export const getPasswordStepStatus = ( formFields: UseFormReturn['fields'], ): tFormStepsStatus => { if ( @@ -155,7 +159,42 @@ export const getPasswordStepStatus = ( formFields.serverAddress.error ) { return 'disabled'; - }else{ + } else { return 'complete'; } -} \ No newline at end of file +}; + +export enum tFormStepsLabel { + operatingSystemSelection = 'operating system', + serverAddress = 'server address', +} + +export const getIncompleteSteps = ( + formFields: UseFormReturn['fields'], +): tFormStepsLabel[] => { + const steps: FormStepsDependencies = { + operatingSystemSelection: ['operatingSystemSelection'], + serverAddress: ['serverAddress'], + }; + const statusManager = new RegisterAgentFormStatusManager(formFields, steps); + // replace fields array using label names + return statusManager.getIncompleteSteps().map(field => { + return tFormStepsLabel[field] || field; + }); +}; + +export enum tFormFieldsLabel { + agentName = 'agent name', + agentGroups = 'agent groups', + serverAddress = 'server address', +} + +export const getInvalidFields = ( + formFields: UseFormReturn['fields'], +): tFormFieldsLabel[] => { + const statusManager = new RegisterAgentFormStatusManager(formFields); + + return statusManager.getInvalidFields().map(field => { + return tFormFieldsLabel[field] || field; + }); +}; From be7b1cd42f635e4488159e83b9d90c4065d85226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chantal=20Bel=C3=A9n=20kelm?= <99441266+chantal-kelm@users.noreply.github.com> Date: Mon, 10 Jul 2023 15:52:15 -0300 Subject: [PATCH 17/46] Step 2: the design triggers warnings (#5649) * changing design to remove console warnings * update changelog * Changes in the display of pop-up windows in the agents log section * semicolon is added --- CHANGELOG.md | 1 - .../public/components/common/form/index.tsx | 2 +- .../components/group-input/group-input.scss | 5 +- .../components/group-input/group-input.tsx | 23 ++-- .../optionals-inputs/optionals-inputs.tsx | 19 ++-- .../server-address/server-address-input.tsx | 35 ------ .../server-address/server-address-title.tsx | 53 --------- .../server-address/server-address.tsx | 103 ++++++++++++++++++ .../containers/steps/steps.scss | 14 ++- .../register-agent/containers/steps/steps.tsx | 34 +++--- 10 files changed, 155 insertions(+), 134 deletions(-) delete mode 100644 plugins/main/public/controllers/register-agent/components/server-address/server-address-input.tsx delete mode 100644 plugins/main/public/controllers/register-agent/components/server-address/server-address-title.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 276f72d427..f1fc056e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,6 @@ All notable changes to the Wazuh app project will be documented in this file. - Added development so that the images of the new agent deployment page also have dark mode. [#5620](https://github.com/wazuh/wazuh-kibana-app/pull/5620) - Added register agent form status callout message [#5634](https://github.com/wazuh/wazuh-kibana-app/pull/5634) - ### Changed - Changed of regular expression in RBAC. [#5201](https://github.com/wazuh/wazuh-kibana-app/pull/5201) diff --git a/plugins/main/public/components/common/form/index.tsx b/plugins/main/public/components/common/form/index.tsx index 2b7cb7ec0f..d10797ca0d 100644 --- a/plugins/main/public/components/common/form/index.tsx +++ b/plugins/main/public/components/common/form/index.tsx @@ -14,7 +14,7 @@ export interface InputFormProps { value: any; onChange: (event: React.ChangeEvent) => void; error?: string; - label?: string; + label?: string | React.ReactNode; header?: | React.ReactNode | ((props: { value: any; error?: string }) => React.ReactNode); diff --git a/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss b/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss index e69348c07b..575880c792 100644 --- a/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss +++ b/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss @@ -1,7 +1,4 @@ -.groupTitle { - margin-top: '32px'; - flex-direction: 'row'; - font-style: normal; +.registerAgentLabels { font-weight: 700; font-size: 12px; line-height: 20px; diff --git a/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx b/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx index 68f436252c..e12c301850 100644 --- a/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx +++ b/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx @@ -10,6 +10,7 @@ import { } from '@elastic/eui'; import { webDocumentationLink } from '../../../../../common/services/web_documentation'; import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +import './group-input.scss'; const popoverAgentGroup = ( @@ -35,25 +36,31 @@ const GroupInput = ({ value, options, onChange }) => { const closeAgentGroup = () => setIsPopoverAgentGroup(false); return ( <> - + + +

+ Select one or more existing groups +

+
- Select one or more existing groups - + > } isOpen={isPopoverAgentGroup} closePopover={closeAgentGroup} diff --git a/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx b/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx index 28c91f69af..28806fb2fa 100644 --- a/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx +++ b/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx @@ -13,6 +13,7 @@ import { InputForm } from '../../../../components/common/form'; import { OPTIONAL_PARAMETERS_TEXT } from '../../utils/register-agent-data'; import { webDocumentationLink } from '../../../../../common/services/web_documentation'; import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +import '../group-input/group-input.scss'; interface OptionalsInputsProps { formFields: UseFormReturn['fields']; } @@ -56,24 +57,28 @@ const OptionalsInputs = (props: OptionalsInputsProps) => { fullWidth={false} label={ <> - + + +

Assign an agent name

+
- Assign an agent name - + > } isOpen={isPopoverAgentName} closePopover={closeAgentName} diff --git a/plugins/main/public/controllers/register-agent/components/server-address/server-address-input.tsx b/plugins/main/public/controllers/register-agent/components/server-address/server-address-input.tsx deleted file mode 100644 index 493735b88b..0000000000 --- a/plugins/main/public/controllers/register-agent/components/server-address/server-address-input.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import React, { Fragment } from 'react'; -import { SERVER_ADDRESS_TEXTS } from '../../utils/register-agent-data'; -import { EnhancedFieldConfiguration } from '../../../../components/common/form/types'; -import { InputForm } from '../../../../components/common/form'; - -interface ServerAddressInputProps { - formField: EnhancedFieldConfiguration; -} - -const ServerAddressInput = (props: ServerAddressInputProps) => { - const { formField } = props; - - return ( - - - {SERVER_ADDRESS_TEXTS.map((data, index) => ( - - - {data.subtitle} - - - ))} - - } - fullWidth={false} - placeholder='Server address' - /> - - ); -}; - -export default ServerAddressInput; diff --git a/plugins/main/public/controllers/register-agent/components/server-address/server-address-title.tsx b/plugins/main/public/controllers/register-agent/components/server-address/server-address-title.tsx deleted file mode 100644 index 76475c5a6d..0000000000 --- a/plugins/main/public/controllers/register-agent/components/server-address/server-address-title.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiLink, EuiPopover } from "@elastic/eui" -import React, { useState } from "react"; -import { webDocumentationLink } from "../../../../../common/services/web_documentation"; -import { PLUGIN_VERSION_SHORT } from "../../../../../common/constants"; - -const ServerAddressTitle = () => { - const [isPopoverServerAddress, setIsPopoverServerAddress] = useState(false); - const closeServerAddress = () => setIsPopoverServerAddress(false); - const onButtonServerAddress = () => - setIsPopoverServerAddress( - isPopoverServerAddress => !isPopoverServerAddress, - ); - - const popoverServerAddress = ( - - Learn about{' '} - - Server address. - - - ); - - return ( - - - Server address - - } - isOpen={isPopoverServerAddress} - closePopover={closeServerAddress} - anchorPosition='rightCenter' - > - {popoverServerAddress} - - - ) -} - -export default ServerAddressTitle; \ No newline at end of file diff --git a/plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx b/plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx new file mode 100644 index 0000000000..8b9a981e6d --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx @@ -0,0 +1,103 @@ +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPopover, + EuiButtonEmpty, + EuiLink, +} from '@elastic/eui'; +import React, { Fragment, useState } from 'react'; +import { SERVER_ADDRESS_TEXTS } from '../../utils/register-agent-data'; +import { EnhancedFieldConfiguration } from '../../../../components/common/form/types'; +import { InputForm } from '../../../../components/common/form'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +import '../group-input/group-input.scss'; + +interface ServerAddressInputProps { + formField: EnhancedFieldConfiguration; +} + +const popoverServerAddress = ( + + Learn about{' '} + + Server address. + + +); + +const ServerAddressInput = (props: ServerAddressInputProps) => { + const { formField } = props; + const [isPopoverServerAddress, setIsPopoverServerAddress] = useState(false); + const onButtonServerAddress = () => + setIsPopoverServerAddress( + isPopoverServerAddress => !isPopoverServerAddress, + ); + const closeServerAddress = () => setIsPopoverServerAddress(false); + + return ( + + + {SERVER_ADDRESS_TEXTS.map((data, index) => ( + + + {data.subtitle} + + + ))} + + + + + + Assign a server address + + + + + } + isOpen={isPopoverServerAddress} + closePopover={closeServerAddress} + anchorPosition='rightCenter' + > + {popoverServerAddress} + + + + + } + fullWidth={false} + placeholder='Server address' + /> + + ); +}; + +export default ServerAddressInput; diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.scss b/plugins/main/public/controllers/register-agent/containers/steps/steps.scss index b6ef8d579a..337cc41298 100644 --- a/plugins/main/public/controllers/register-agent/containers/steps/steps.scss +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.scss @@ -1,9 +1,11 @@ -.stepTitle { - font-style: normal; - font-weight: 700; - font-size: 16px; - letter-spacing: 0.6px; - flex-direction: row; +.register-agent-wizard-container { + .euiStep__title { + font-style: normal; + font-weight: 700; + font-size: 16px; + letter-spacing: 0.6px; + flex-direction: row; + } } .stepSubtitleServerAddress { diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx index 26140391e9..bfa904db8c 100644 --- a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx @@ -17,8 +17,7 @@ import { } from '../../core/config/os-commands-definitions'; import { UseFormReturn } from '../../../../components/common/form/types'; import CommandOutput from '../../components/command-output/command-output'; -import ServerAddressTitle from '../../components/server-address/server-address-title'; -import ServerAddressInput from '../../components/server-address/server-address-input'; +import ServerAddress from '../../components/server-address/server-address'; import OptionalsInputs from '../../components/optionals-inputs/optionals-inputs'; import { getAgentCommandsStepStatus, @@ -134,23 +133,19 @@ export const Steps = ({ const registerAgentFormSteps = [ { - title: ( - - Select the package to download and install on your system: - - ), + title: 'Select the package to download and install on your system:', children: osCard, status: getOSSelectorStepStatus(form.fields), }, { - title: , - children: , + title: 'Server address', + children: , status: getServerAddressStepStatus(form.fields), }, ...(needsPassword && !wazuhPassword ? [ { - title: Wazuh password, + title: 'Wazuh password', children: ( Optional settings
, + title: 'Optional settings', children: , status: getOptionalParameterStepStatus( form.fields, @@ -186,17 +181,16 @@ export const Steps = ({ ), }, { - title: ( - - Run the following commands to download and install the Wazuh agent: - - ), + title: + 'Run the following commands to download and install the Wazuh agent:', children: ( <> {missingStepsName?.length ? ( ) : null} @@ -224,13 +218,15 @@ export const Steps = ({ status: installCommandStepStatus, }, { - title: Start the Wazuh agent:, + title: 'Start the Wazuh agent:', children: ( <> {missingStepsName?.length ? ( ) : null} From 0241a595acf81d3f1a7a0fa980b90d4502e2e9fb Mon Sep 17 00:00:00 2001 From: "chantal.kelm" Date: Tue, 11 Jul 2023 11:01:14 -0300 Subject: [PATCH 18/46] update changelog --- CHANGELOG.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1fc056e71..14c4d60527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,13 +10,9 @@ All notable changes to the Wazuh app project will be documented in this file. - Added `ignore` and `restrict` options to Syslog configuration. [#5203](https://github.com/wazuh/wazuh-kibana-app/pull/5203) - Added the `extensions.github` and `extensions.office` settings to the default configuration file [#5376](https://github.com/wazuh/wazuh-kibana-app/pull/5376) - Added new global error treatment (client-side) [#4163](https://github.com/wazuh/wazuh-kibana-app/pull/4163) -- Added a description to step 3 of the deploy a new agent section. [#5419](https://github.com/wazuh/wazuh-kibana-app/pull/5419) -- Added a title to the agent name input of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) -- Added callout below the agent name entry of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) + - Added new CLI to generate API data from specification file [#5519](https://github.com/wazuh/wazuh-kibana-app/pull/5519) - Added specific RBAC permissions to Security section [#5551](https://github.com/wazuh/wazuh-kibana-app/pull/5551) -- Added development so that the images of the new agent deployment page also have dark mode. [#5620](https://github.com/wazuh/wazuh-kibana-app/pull/5620) -- Added register agent form status callout message [#5634](https://github.com/wazuh/wazuh-kibana-app/pull/5634) ### Changed @@ -28,7 +24,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed the placeholder of the agent name input of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) - Changed the query to search for an agent in `management/configuration`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) - Changed the search bar in management/log to the one used in the rest of the app. [#5476](https://github.com/wazuh/wazuh-kibana-app/pull/5476) -- Changed the deploy a new agent page from step one to step three. [#5554](https://github.com/wazuh/wazuh-kibana-app/pull/5554) [#5462](https://github.com/wazuh/wazuh-kibana-app/pull/5462) +- Changed the design of the wizard to add agents. [#5457](https://github.com/wazuh/wazuh-kibana-app/pull/5457) ### Fixed @@ -56,7 +52,6 @@ All notable changes to the Wazuh app project will be documented in this file. - Removed pretty parameter from cron job requests. [#5532](https://github.com/wazuh/wazuh-kibana-app/pull/5532) - Removed unnecessary requests in `Management/Status` section. [#5528](https://github.com/wazuh/wazuh-kibana-app/pull/5528) - Removed obsolete code that caused duplicate requests to the api in `Management`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) -- Removed the custom colors that did not allow to activate the default dark mode of elastic. [#5620](https://github.com/wazuh/wazuh-kibana-app/pull/5620) ## Wazuh v4.5.0 - OpenSearch Dashboards 2.6.0 - Revision 01 From bf647a70114601b5ed7a023b2f9ce0943782c0c4 Mon Sep 17 00:00:00 2001 From: "chantal.kelm" Date: Tue, 11 Jul 2023 11:14:25 -0300 Subject: [PATCH 19/46] update changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14c4d60527..993df19be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ All notable changes to the Wazuh app project will be documented in this file. - Added `ignore` and `restrict` options to Syslog configuration. [#5203](https://github.com/wazuh/wazuh-kibana-app/pull/5203) - Added the `extensions.github` and `extensions.office` settings to the default configuration file [#5376](https://github.com/wazuh/wazuh-kibana-app/pull/5376) - Added new global error treatment (client-side) [#4163](https://github.com/wazuh/wazuh-kibana-app/pull/4163) - - Added new CLI to generate API data from specification file [#5519](https://github.com/wazuh/wazuh-kibana-app/pull/5519) - Added specific RBAC permissions to Security section [#5551](https://github.com/wazuh/wazuh-kibana-app/pull/5551) From 3c5f9650b81bbcc843d6c70d75de08ffced74c9c Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 11 Jul 2023 17:48:25 +0200 Subject: [PATCH 20/46] Merge 4.5.1 into 4.6.0 (#5671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change windows agent service name (#5538) * Change windows agent service name to Wazuh Change windows agent service name to Wazuh * Add CHANGELOG * Remove agent name in agent info ribbon (#5497) * remove: agent name in agent info ribbon * changelog: add pull request entry --------- Co-authored-by: Álex Ruiz * Fix IPV6 visualizations (#5471) * add ipv6 service * add test for service * Fix issue in agents-table * fix issue in agents-info * fix groups agents issue * Fix width in groups agents * use mapResponseItem * Add copy button to groups * Add copy button to info * fix for node list * Optimize code * Fix styles * Edit changelog * Edit changelog * Add imposter changes to test ipv6 * Replace onMouseDown with onClick * Move copy buttons to the left * fix: removed compressipv6 property of TableWzAPI * feat: add tableLayout property to some tables and remove IPv6 address compression add tableLayout=auto property to some tables: - Agents/{agent_id}/Inventory data - Management/Cluster/Nodes - Agents - Management/Configuration/Client - Management/Global configuration/Remote remove IPv6 address compression * remove: remove unused service to IPv6 compression * revert: revert changes in TableWzAPI component * add: add mocked responses to some syscollector endpoints * remove: unwanted table columns properties * changelog: add pull request entry * Fix imposter --------- Co-authored-by: Antonio David Gutiérrez Co-authored-by: Álex Ruiz Co-authored-by: yenienserrano Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> * Bump v4.4.4-2.6.0-rc2 * Add Apple Silicon architecture to the register Agent wizard (#5478) * Add Apple Silicon architecture * Add changelog * Change macOS environment variables * Revert "Change macOS environment variables" This reverts commit 108e86626045de6b5cd7b7053a8c6333d8bf8b89. * Change macOS architecture ids * Add missing supported versions to the Docker environments (#5584) feat(environments): add latest versions to Docker environments - Add Kibana versions: 7.17.7, 7.17.8, 7.17.9 and 7.17.10 - Add OpenSearch: 2.6.0 - Add OpenSearch Dashboards: 2.6.0 - Add Wazuh 4.4.1, 4.4.2, 4.4.3 and 4.4.4 * Bump 4.5.1 * Change the method to make the redirect (#5539) * Change the metod to make the redirect * Remove unused code * Add changelog --------- Co-authored-by: Álex Ruiz * Fix agents active coverage stat as NaN (#5490) * fix: agents active coverate stat as NaN Ensure the values used to calculate have the expected types and the total count is greater than 0. * remove: unused openRegistrationDocs method * changelog: add entry * fix: check if agents active coverage is a NaN * changelog: fix entry --------- Co-authored-by: Álex Ruiz * [Backport 4.5.1] Update test snapshots for 4.5 (#5607) * Update test snapshots for 4.5 (#5601) * Add missing supported versions to the Docker environments (#5584) feat(environments): add latest versions to Docker environments - Add Kibana versions: 7.17.7, 7.17.8, 7.17.9 and 7.17.10 - Add OpenSearch: 2.6.0 - Add OpenSearch Dashboards: 2.6.0 - Add Wazuh 4.4.1, 4.4.2, 4.4.3 and 4.4.4 * Update test snapshost * Update API data to 4.5 * Update branch patterns for GH Actions --------- Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> (cherry picked from commit 1ae5f19a9edc967187b2d946aad6e8d8f0afff14) * Fix API reference links in endpoints.json * Add kbn-dev 7.17.11 (#5628) * Merge 4.5.0 into 4.5.1 (#5670) * Update test snapshots for 4.5 (#5601) * Add missing supported versions to the Docker environments (#5584) feat(environments): add latest versions to Docker environments - Add Kibana versions: 7.17.7, 7.17.8, 7.17.9 and 7.17.10 - Add OpenSearch: 2.6.0 - Add OpenSearch Dashboards: 2.6.0 - Add Wazuh 4.4.1, 4.4.2, 4.4.3 and 4.4.4 * Update test snapshost * Update API data to 4.5 * Update branch patterns for GH Actions --------- Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> * Fix API reference links in endpoints.json * Merge 4.4 into 4.5.0 (#5669) Merge v4.4.5-2.6.0 into 4.4 (#5665) * Bump Wazuh and platform versions for v4.4.5 (#5639) * Update changelog * Update opensearch_dashboards.json * Update package.json * Update readme * Update tag script * Change tag.py version value * Empty tag suffix * Prepare tag.py for v4.4.5-rc1 (#5645) Add -rc1 tag suffix * Fix incompatible version of triple-beam subdependency (#5652) fix: add yarn.lock file and set version of triple-beam in yarn.lock * Update unit-test.yml (#5655) * Add support for Wazuh 4.4.5-rc2 (#5659) * Update revision of v4.4.5 in the Changelog * Bump v4.4.5-2.6.0-rc2 --------- Co-authored-by: Nicolas Agustin Guevara Pihen <42900763+Tostti@users.noreply.github.com> Co-authored-by: Federico Rodriguez Co-authored-by: Álex Ruiz Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> --------- Co-authored-by: Álex Ruiz Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Nicolas Agustin Guevara Pihen <42900763+Tostti@users.noreply.github.com> Co-authored-by: Federico Rodriguez --------- Co-authored-by: Julio César Biset <43619595+jbiset@users.noreply.github.com> Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Álex Ruiz Co-authored-by: Nicolas Agustin Guevara Pihen <42900763+Tostti@users.noreply.github.com> Co-authored-by: Antonio David Gutiérrez Co-authored-by: Federico Rodriguez --- .github/workflows/unit-test.yml | 8 +- .gitignore | 4 +- CHANGELOG.md | 30 +- README.md | 27 +- docker/images/kbn-7.17.11-dev.Dockerfile | 17 + docker/imposter/agents/agent.json | 2 +- docker/imposter/agents/group.json | 95 + .../node/response-with-everything.json | 26 +- docker/imposter/syscollector/netaddr.js | 29 + docker/imposter/syscollector/ports.js | 192 ++ docker/imposter/wazuh-config.yml | 9 + docker/kbn-dev/dev.sh | 9 +- docker/wazuh-4.4-wz/pre.sh | 113 +- docker/wazuh-4.4-wz/rel.sh | 80 +- docker/wazuh-4.x-es/pre.sh | 106 +- docker/wazuh-4.x-es/rel.sh | 133 +- plugins/main/common/api-info/endpoints.json | 888 ++--- .../common/services/web_documentation.test.ts | 18 +- plugins/main/package.json | 2 +- .../components/syscollector-table.tsx | 103 +- .../components/common/welcome/agents-info.js | 147 +- .../common/welcome/agents-welcome.js | 933 +++--- .../management/cluster/node-list.tsx | 215 +- .../controllers/agent/agents-preview.js | 64 +- .../agent/components/agents-preview.js | 274 +- .../agent/components/agents-table.js | 275 +- .../agent/components/register-agent.js | 4 +- .../controllers/agent/wazuh-config/index.ts | 8 +- .../management/configuration/client/client.js | 39 +- .../global-configuration-remote.js | 31 +- .../management/groups/group-agents-table.js | 116 +- plugins/main/scripts/tag.py | 6 +- plugins/main/yarn.lock | 2888 +++++++++++++++++ 33 files changed, 5051 insertions(+), 1840 deletions(-) create mode 100644 docker/images/kbn-7.17.11-dev.Dockerfile create mode 100644 docker/imposter/agents/group.json create mode 100644 docker/imposter/syscollector/netaddr.js create mode 100644 docker/imposter/syscollector/ports.js create mode 100644 plugins/main/yarn.lock diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index aa4b3195f2..b7e7ec1c5e 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -5,7 +5,6 @@ # # Jest is a third-party software https://jestjs.io/ - name: Run unit test on: @@ -22,13 +21,8 @@ on: default: 'yarn test:jest' description: Select the type of test to run. options: - - 'yarn test:jest' + - 'yarn test:jest' pull_request: - branches: - - 'master' - - '[345].[0-9]+' # Minor branches - - '[345].[0-9]+.[0-9]+' # Patch branches - - '[345].[0-9]+.[0-9]+-7.[0-9]+' # Minor branches - Kibana jobs: # Run unit tests with Jest diff --git a/.gitignore b/.gitignore index 2cc137cef8..f86051e821 100644 --- a/.gitignore +++ b/.gitignore @@ -72,8 +72,6 @@ typings/ target/ build/ -yarn.lock - cypress/node_modules/ cypress/.idea/ cypress/cypress.env.json @@ -84,4 +82,4 @@ cypress/cookies.json public/assets/custom/* # Mac files -.DS_Store \ No newline at end of file +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ff2f430b..fea52d91a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,12 +54,38 @@ All notable changes to the Wazuh app project will be documented in this file. - Removed unnecessary requests in `Management/Status` section. [#5528](https://github.com/wazuh/wazuh-kibana-app/pull/5528) - Removed obsolete code that caused duplicate requests to the api in `Management`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) +## Wazuh v4.5.1 - OpenSearch Dashboards 2.6.0 - Revision 01 + +### Added + +- Add Apple Silicon architecture button to the register Agent wizard [#5478](https://github.com/wazuh/wazuh-kibana-app/pull/5478) + +### Fixed + +- Fixed the rendering of tables that contains IPs and agent overview [#5471](https://github.com/wazuh/wazuh-kibana-app/pull/5471) +- Fixed the agents active coverage stat as NaN in Details panel of Agents section [#5490](https://github.com/wazuh/wazuh-kibana-app/pull/5490) + +### Removed + +- Removed the agent name in the agent info ribbon [#5497](https://github.com/wazuh/wazuh-kibana-app/pull/5497) + +### Changed + +- Changed method to perform redirection on agent table buttons [#5539](https://github.com/wazuh/wazuh-kibana-app/pull/5539) +- Changed windows agent service name in the deploy agent wizard [#5538](https://github.com/wazuh/wazuh-kibana-app/pull/5538) + ## Wazuh v4.5.0 - OpenSearch Dashboards 2.6.0 - Revision 01 ### Added - Support for Wazuh 4.5.0 +## Wazuh v4.4.5 - OpenSearch Dashboards 2.6.0 - Revision 02 + +### Added + +- Support for Wazuh 4.4.5 + ## Wazuh v4.4.4 - OpenSearch Dashboards 2.6.0 - Revision 01 ### Added @@ -108,8 +134,8 @@ All notable changes to the Wazuh app project will be documented in this file. - Added the option to sort by the agent's count in the group table. [#4323](https://github.com/wazuh/wazuh-kibana-app/pull/4323) - Added agent synchronization status in the agent module. [#3874](https://github.com/wazuh/wazuh-kibana-app/pull/3874) [#5143](https://github.com/wazuh/wazuh-kibana-app/pull/5143) [#5177](https://github.com/wazuh/wazuh-kibana-app/pull/5177) - Added the ability to set the agent name in the installation command. [#4739](https://github.com/wazuh/wazuh-kibana-app/pull/4739) -- Added validation to the plugin's settings [#4503](https://github.com/wazuh/wazuh-kibana-app/pull/4503)[#4785](https://github.com/wazuh/wazuh-kibana-app/pull/4785) -- Added new settings to customize the header and footer on the PDF reports [#4505](https://github.com/wazuh/wazuh-kibana-app/pull/4505)[#4798](https://github.com/wazuh/wazuh-kibana-app/pull/4798)[#4805](https://github.com/wazuh/wazuh-kibana-app/pull/4805) +- Added validation to the plugin's settings [#4503](https://github.com/wazuh/wazuh-kibana-app/pull/4503) [#4785](https://github.com/wazuh/wazuh-kibana-app/pull/4785) +- Added new settings to customize the header and footer on the PDF reports [#4505](https://github.com/wazuh/wazuh-kibana-app/pull/4505) [#4798](https://github.com/wazuh/wazuh-kibana-app/pull/4798) [#4805](https://github.com/wazuh/wazuh-kibana-app/pull/4805) - Added a new setting to enable or disable the customization [#4507](https://github.com/wazuh/wazuh-kibana-app/pull/4507) - Added the ability to upload an image for the `customization.logo.*` settings in `Settings/Configuration` [#4504](https://github.com/wazuh/wazuh-kibana-app/pull/4504) - Added macOS support to the 'Deploy new agent' section [#4867](https://github.com/wazuh/wazuh-kibana-app/pull/4867) diff --git a/README.md b/README.md index 96e1c1753d..9538e7a660 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ - [Wazuh UI](#wazuh-ui) - [Contribute](#contribute) - [License](#license) -- [Copyright](#copyright) +- [Copyright](#copyright) - - - - + + + + ## Wazuh UI @@ -100,27 +100,26 @@ You can learn more about it at [wazuh.com][web] ## Contribute -If you want to contribute to our project please don't hesitate to send a pull request. -Take a look at the [branches and tags][branches] page in our Wiki, and also to our +If you want to contribute to our project please don't hesitate to send a pull request. +Take a look at the [branches and tags][branches] page in our Wiki, and also to our [contributing](CONTRIBUTING.md) guidelines. ## License This project is licensed under the [GNU General Public License v2.0](LICENSE). -This program is free software; you can redistribute it and/or modify it under the terms -of the GNU General Public License as published by the Free Software Foundation; either +This program is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ## Copyright - Copyright © Wazuh, Inc. - [dashboard]: https://github.com/wazuh/wazuh-dashboard [web]: https://wazuh.com [docs]: https://documentation.wazuh.com [install]: https://documentation.wazuh.com/current/installation-guide/index.html [slack]: https://join.slack.com/t/wazuh/shared_invite/zt-1lgu531ur-7M_k_ZQbpdo4QCn_pHee3w [branches]: https://github.com/wazuh/wazuh-kibana-app/wiki/About-our-branches-and-tags -[wiki]: https://github.com/wazuh/wazuh-kibana-app/wiki \ No newline at end of file +[wiki]: https://github.com/wazuh/wazuh-kibana-app/wiki diff --git a/docker/images/kbn-7.17.11-dev.Dockerfile b/docker/images/kbn-7.17.11-dev.Dockerfile new file mode 100644 index 0000000000..793b08ecb5 --- /dev/null +++ b/docker/images/kbn-7.17.11-dev.Dockerfile @@ -0,0 +1,17 @@ +FROM node:16.20.1 AS builder-kbn-7.17.11 +RUN npm install --global @bazel/bazelisk@1.10.1 +USER node +RUN git clone --depth 1 --branch v7.17.11 https://github.com/elastic/kibana /home/node/kbn +RUN chown node.node /home/node/kbn + +WORKDIR /home/node/kbn +RUN yarn kbn bootstrap +RUN yarn config set registry http://host.docker.internal:4873 && \ + sed -i 's/https:\/\/registry.yarnpkg.com/http:\/\/host.docker.internal:4873/g' yarn.lock +RUN rm -rf /home/node/.cache/yarn && rm -rf /home/node/.cache/Cypress && rm -rf /home/node/.cache/ms-playwright +RUN mkdir -p /home/node/kbn/data/wazuh/config + +FROM node:16.20.1 +USER node +COPY --from=builder-kbn-7.17.11 /home/node/ /home/node/ +WORKDIR /home/node/kbn diff --git a/docker/imposter/agents/agent.json b/docker/imposter/agents/agent.json index 0389d55c2c..c8715054c7 100644 --- a/docker/imposter/agents/agent.json +++ b/docker/imposter/agents/agent.json @@ -11,7 +11,7 @@ "uname": "Linux |ip-10-0-1-106 |4.9.0-9-amd64 |#1 SMP Debian 4.9.168-1+deb9u2 (2019-05-13) |x86_64", "version": "9" }, - "ip": "10.0.1.106", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "configSum": "6f4293818ef64291ca53727fb9ab8958", "mergedSum": "7976a83d1aebcca09bc14459b5518ed5", "id": "001", diff --git a/docker/imposter/agents/group.json b/docker/imposter/agents/group.json new file mode 100644 index 0000000000..a05fb465ba --- /dev/null +++ b/docker/imposter/agents/group.json @@ -0,0 +1,95 @@ +{ + "data": { + "affected_items": [ + { + "os": { + "arch": "x86_64", + "codename": "Focal Fossa", + "major": 20, + "minor": 4, + "name": "Ubuntu", + "platform": "ubuntu", + "uname": "Linux |b2497efbf876 |5.8.0-45-generic |#51~20.04.1-Ubuntu SMP Tue Feb 23 13:46:31 UTC 2021 |x86_64", + "version": "20.04.2 LTS" + }, + "mergedSum": "2c769b2ea138d472ee8f1ba23412b5d4", + "node_name": "worker1", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", + "id": 4, + "manager": "wazuh-worker1", + "group": [ + "default", + "group1" + ], + "name": "b2497efbf876", + "configSum": "052374472f3a0d5c8508241dcc455ea7", + "status": "active", + "dateAdd": "2021-05-27T09:14:19Z", + "registerIP": "any", + "lastKeepAlive": "2021-05-27T09:23:59Z", + "version": "Wazuh v4.3.0" + }, + { + "os": { + "arch": "x86_64", + "codename": "Focal Fossa", + "major": 20, + "minor": 4, + "name": "Ubuntu", + "platform": "ubuntu", + "uname": "Linux |600e27371700 |5.8.0-45-generic |#51~20.04.1-Ubuntu SMP Tue Feb 23 13:46:31 UTC 2021 |x86_64", + "version": "20.04.2 LTS" + }, + "mergedSum": "9a016508cea1e997ab8569f5cfab30f5", + "node_name": "worker1", + "ip": "FE80:1234:2223:A000:2202:B3FF:FE1E:8329", + "id": 5, + "manager": "wazuh-worker1", + "group": [ + "default", + "group2" + ], + "name": "Infinity", + "configSum": "ab73af41699f13fdd81903b5f23d8d00", + "status": "active", + "dateAdd": "2021-05-27T09:14:19Z", + "registerIP": "any", + "lastKeepAlive": "2021-05-27T09:23:52Z", + "version": "Wazuh v4.3.0" + }, + { + "os": { + "arch": "x86_64", + "codename": "Focal Fossa", + "major": 20, + "minor": 4, + "name": "Ubuntu", + "platform": "ubuntu", + "uname": "Linux |4bdac19ce5e3 |5.8.0-45-generic |#51~20.04.1-Ubuntu SMP Tue Feb 23 13:46:31 UTC 2021 |x86_64", + "version": "20.04.2 LTS" + }, + "mergedSum": "9a016508cea1e997ab8569f5cfab30f5", + "node_name": "worker2", + "ip": "172.20.0.10", + "id": 6, + "manager": "wazuh-worker2", + "group": [ + "default", + "group3" + ], + "name": "4bdac19ce5e3", + "configSum": "ab73af41699f13fdd81903b5f23d8d00", + "status": "active", + "dateAdd": "2021-05-27T09:14:19Z", + "registerIP": "any", + "lastKeepAlive": "2021-05-27T09:23:52Z", + "version": "Wazuh v4.3.0" + } + ], + "total_affected_items": 3, + "total_failed_items": 0, + "failed_items": [] + }, + "message": "All selected agents information was returned", + "error": 0 +} diff --git a/docker/imposter/cluster/node/response-with-everything.json b/docker/imposter/cluster/node/response-with-everything.json index db61b8f399..7e74c50e49 100644 --- a/docker/imposter/cluster/node/response-with-everything.json +++ b/docker/imposter/cluster/node/response-with-everything.json @@ -5,91 +5,91 @@ "name": "master-node", "type": "master", "version": "4.5.0", - "ip": "wazuh-master", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2020-05-27T10:50:49.175Z" }, { "name": "worker1", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.7", + "ip": "FE80:1234:2223:A000:2202:B3FF:FE1E:8329", "connection_date": "2021-05-27T10:50:51.342Z" }, { "name": "worker2", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.6", + "ip": "127.0.0.2", "connection_date": "2021-05-27T10:48:54.093Z" }, { "name": "worker3", "type": "worker", "version": "4.5.0", - "ip": "wazuh-worker", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:50:51.342Z" }, { "name": "worker4", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.7", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:50:51.342Z" }, { "name": "worker5", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.6", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:48:54.093Z" }, { "name": "worker6", "type": "worker", "version": "4.5.0", - "ip": "wazuh-worker", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:50:51.342Z" }, { "name": "worker7", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.7", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:48:54.093Z" }, { "name": "worker8", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.6", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:50:51.342Z" }, { "name": "worker9", "type": "worker", "version": "4.5.0", - "ip": "wazuh-worker", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2019-05-27T10:48:54.093Z" }, { "name": "worker10", "type": "worker", "version": "4.5.0", - "ip": "wazuh-worker", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2022-05-27T10:48:54.093Z" }, { "name": "worker11", "type": "worker", "version": "4.5.0", - "ip": "172.26.0.6", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2019-05-27T10:48:54.093Z" }, { "name": "worker12", "type": "worker", "version": "4.5.0", - "ip": "wazuh-worker", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "connection_date": "2021-05-27T10:50:51.342Z" } ], diff --git a/docker/imposter/syscollector/netaddr.js b/docker/imposter/syscollector/netaddr.js new file mode 100644 index 0000000000..6cccddf671 --- /dev/null +++ b/docker/imposter/syscollector/netaddr.js @@ -0,0 +1,29 @@ +var data = { + data: { + affected_items: [ + { + address: '172.26.0.7', + iface: 'eth0', + netmask: '255.255.0.0', + broadcast: '172.26.255.255', + proto: 'ipv4', + agent_id: 1, + }, + { + address: 'FE80:0034:0223:A000:0002:B3FF:0000:8329', + iface: 'eth0', + netmask: 'FE80:0034:0223:A000:0002:B3FF:0000:8329', + broadcast: 'FE80:0034:0223:A000:0002:B3FF:0000:8329', + proto: 'ipv6', + agent_id: 1, + }, + ], + total_affected_items: 1, + total_failed_items: 0, + failed_items: [], + }, + message: 'All specified syscollector information was returned', + error: 0, +}; + +respond().withStatusCode(200).withData(JSON.stringify(data)); diff --git a/docker/imposter/syscollector/ports.js b/docker/imposter/syscollector/ports.js new file mode 100644 index 0000000000..f81206de5e --- /dev/null +++ b/docker/imposter/syscollector/ports.js @@ -0,0 +1,192 @@ +var agentID = context.request.pathParams.agent_id; + +var ipv4_01 = { + local: { + ip: '0.0.0.0', + port: 46841, + }, + remote: { + ip: '0.0.0.0', + port: 0, + }, + scan: { + id: 0, + time: '2021-05-28T11:16:14Z', + }, + inode: 12387152, + rx_queue: 0, + protocol: 'tcp', + pid: 0, + tx_queue: 0, + agent_id: agentID, +}; + +var ipv4_02 = { + local: { + ip: '0.0.0.0', + port: 80, + }, + remote: { + ip: '0.0.0.0', + port: 0, + }, + scan: { + id: 0, + time: '2021-05-28T11:16:14Z', + }, + inode: 12387152, + rx_queue: 0, + protocol: 'tcp', + pid: 0, + tx_queue: 0, + agent_id: agentID, +}; + +var ipv4_03 = { + local: { + ip: '0.0.0.0', + port: 443, + }, + remote: { + ip: '0.0.0.0', + port: 0, + }, + scan: { + id: 0, + time: '2021-05-28T11:16:14Z', + }, + state: 'listening', + inode: 12387152, + rx_queue: 0, + protocol: 'tcp', + pid: 0, + tx_queue: 0, + agent_id: agentID, +}; + +var ipv6_01 = { + local: { + ip: 'FE80:0034:0223:A000:0002:B3FF:0000:8329', + port: 1515, + }, + remote: { + ip: '0.0.0.0', + port: 0, + }, + scan: { + id: 315935312, + time: '2020-04-15T11:02:07Z', + }, + state: 'listening', + inode: 12397153, + rx_queue: 0, + protocol: 'tcp', + tx_queue: 0, + agent_id: agentID, +}; + +var ipv6_02 = { + local: { + ip: 'FE80:0034:0223:A000:0002:B3FF:0000:8329', + port: 80, + }, + remote: { + ip: '0.0.0.0', + port: 0, + }, + scan: { + id: 315935312, + time: '2020-04-15T11:02:07Z', + }, + state: 'listening', + inode: 12397153, + rx_queue: 0, + protocol: 'tcp', + tx_queue: 0, + agent_id: agentID, +}; + +var ipv6_03 = { + local: { + ip: 'FE80:0034:0223:A000:0002:B3FF:0000:8329', + port: 443, + }, + remote: { + ip: '0.0.0.0', + port: 0, + }, + scan: { + id: 315935312, + time: '2020-04-15T11:02:07Z', + }, + state: 'listening', + inode: 12397153, + rx_queue: 0, + protocol: 'tcp', + tx_queue: 0, + agent_id: agentID, +}; + +var affected_items_agents = { + '001': [ + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv6_01, + ipv4_03, + ipv4_01, + ipv4_02, + ipv4_03, + ], + '002': [ipv4_01, ipv4_02, ipv4_03], + '003': [ipv6_01, ipv6_02, ipv6_03], +}; + +var affected_items = + affected_items_agents[agentID] || affected_items_agents['001']; +var total_affected_items = affected_items.length; + +var limit = context.request.queryParams.limit; +var offset = context.request.queryParams.offset; + +if (offset || limit) { + affected_items = affected_items.slice(offset, offset + limit); +} + +var response = { + data: { + affected_items: affected_items, + total_affected_items: total_affected_items, + total_failed_items: 0, + failed_items: [], + }, + message: 'All specified syscollector information was returned', + error: 0, +}; + +respond().withStatusCode(200).withData(JSON.stringify(response)); diff --git a/docker/imposter/wazuh-config.yml b/docker/imposter/wazuh-config.yml index 2a4d1930b5..028dd20d3f 100755 --- a/docker/imposter/wazuh-config.yml +++ b/docker/imposter/wazuh-config.yml @@ -376,6 +376,9 @@ resources: # Get agents in a group - method: GET path: /groups/{group_id}/agents + response: + statusCode: 200 + staticFile: agents/group.json # Get group configuration - method: GET @@ -827,6 +830,9 @@ resources: # Get agent netaddr - method: GET path: /syscollector/{agent_id}/netaddr + response: + statusCode: 200 + scriptFile: syscollector/netaddr.js # Get agent netiface - method: GET @@ -847,6 +853,9 @@ resources: # Get agent ports - method: GET path: /syscollector/{agent_id}/ports + response: + statusCode: 200 + scriptFile: syscollector/ports.js # Get agent processes - method: GET diff --git a/docker/kbn-dev/dev.sh b/docker/kbn-dev/dev.sh index 03647f7aee..d0ee3d5bf3 100755 --- a/docker/kbn-dev/dev.sh +++ b/docker/kbn-dev/dev.sh @@ -10,10 +10,11 @@ elastic_versions=( '7.17.4' '7.17.5' '7.17.6' - '7.17.7' - '7.17.8' - '7.17.9' - '7.17.10' + '7.17.7' + '7.17.8' + '7.17.9' + '7.17.10' + '7.17.11' '8.0.0' '8.1.0' '8.2.1' diff --git a/docker/wazuh-4.4-wz/pre.sh b/docker/wazuh-4.4-wz/pre.sh index 981f2bbd30..9487376cfa 100755 --- a/docker/wazuh-4.4-wz/pre.sh +++ b/docker/wazuh-4.4-wz/pre.sh @@ -2,10 +2,13 @@ versions=( "4.4.0" - "4.4.1" - "4.4.2" - "4.4.3" - "4.4.4" + "4.4.1" + "4.4.2" + "4.4.3" + "4.4.4" + "4.4.5" + "4.5.0" + "4.5.1" ) wazuh_api_version=( @@ -32,26 +35,24 @@ usage() { exit -1 } -if [ $# -ne 3 ] - then - echo "Incorrect number of arguments " $# - usage +if [ $# -ne 3 ]; then + echo "Incorrect number of arguments " $# + usage fi -if [[ ! " ${versions[*]} " =~ " ${1} " ]] - then - echo "Version ${1} not found in ${versions[*]}" - exit -1 +if [[ ! " ${versions[*]} " =~ " ${1} " ]]; then + echo "Version ${1} not found in ${versions[*]}" + exit -1 fi [ -n "$2" ] && [ "$2" -eq "$2" ] 2>/dev/null if [ $? -ne 0 ]; then - echo "$2 is not number" - exit -1 + echo "$2 is not number" + exit -1 fi patch_version=$2 -cat << EOF > config/imposter/api_info.json +cat <config/imposter/api_info.json { "data": { "title": "Wazuh API REST", @@ -72,46 +73,46 @@ export KIBANA_PASSWORD=${PASSWORD:-SecretPassword} export COMPOSE_PROJECT_NAME=wz-pre-${WAZUH_STACK//./} case "$3" in - up) - # recreate volumes - docker compose -f pre.yml up -Vd +up) + # recreate volumes + docker compose -f pre.yml up -Vd - # This installs Wazuh and integrates with a default Wazuh stack - # v=$( echo -n $WAZUH_STACK | sed 's/\.//g' ) - echo - echo "Install the pre-release package manually with:" - echo - echo "1. Uninstall current version of the Wazuh app:" - echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1 /usr/share/wazuh-dashboard/bin/opensearch-dashboards-plugin remove wazuh" - echo - echo "2. Restart Wazuh Dashboard:" - echo "docker restart ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1" - echo - echo "3. Copy the pre-release package to the running Wazuh Dashboard container:" - echo docker cp wazuh-4.4.${patch_version}-1.zip ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1:/tmp - echo - echo "4. Install the package we have just uploaded:" - echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1 /usr/share/wazuh-dashboard/bin/opensearch-dashboards-plugin install file:///tmp/wazuh-4.4.${patch_version}-1.zip" - echo - echo "5. Restart the Wazuh Dashboard container:" - echo "docker restart ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1" - echo - echo "6. Upload the Wazuh app configuration:" - echo "docker cp ./config/wazuh_dashboard/wazuh.yml ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1:/usr/share/wazuh-dashboard/data/wazuh/config/" - echo - echo "7. Access the running instance in:" - echo "https://localhost:${KIBANA_PORT}" - echo - ;; - down) - # delete volumes - docker compose -f pre.yml down -v --remove-orphans - ;; - stop) - docker compose -f rel.yml -p ${COMPOSE_PROJECT_NAME} stop - ;; - *) - echo "Action must be either up or down" - usage - ;; + # This installs Wazuh and integrates with a default Wazuh stack + # v=$( echo -n $WAZUH_STACK | sed 's/\.//g' ) + echo + echo "Install the pre-release package manually with:" + echo + echo "1. Uninstall current version of the Wazuh app:" + echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1 /usr/share/wazuh-dashboard/bin/opensearch-dashboards-plugin remove wazuh" + echo + echo "2. Restart Wazuh Dashboard:" + echo "docker restart ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1" + echo + echo "3. Copy the pre-release package to the running Wazuh Dashboard container:" + echo docker cp wazuh-4.4.${patch_version}-1.zip ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1:/tmp + echo + echo "4. Install the package we have just uploaded:" + echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1 /usr/share/wazuh-dashboard/bin/opensearch-dashboards-plugin install file:///tmp/wazuh-4.4.${patch_version}-1.zip" + echo + echo "5. Restart the Wazuh Dashboard container:" + echo "docker restart ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1" + echo + echo "6. Upload the Wazuh app configuration:" + echo "docker cp ./config/wazuh_dashboard/wazuh.yml ${COMPOSE_PROJECT_NAME}-wazuh.dashboard-1:/usr/share/wazuh-dashboard/data/wazuh/config/" + echo + echo "7. Access the running instance in:" + echo "https://localhost:${KIBANA_PORT}" + echo + ;; +down) + # delete volumes + docker compose -f pre.yml down -v --remove-orphans + ;; +stop) + docker compose -f rel.yml -p ${COMPOSE_PROJECT_NAME} stop + ;; +*) + echo "Action must be either up or down" + usage + ;; esac diff --git a/docker/wazuh-4.4-wz/rel.sh b/docker/wazuh-4.4-wz/rel.sh index 9723041319..cd74d62c4b 100755 --- a/docker/wazuh-4.4-wz/rel.sh +++ b/docker/wazuh-4.4-wz/rel.sh @@ -2,10 +2,13 @@ versions=( "4.4.0" - "4.4.1" - "4.4.2" - "4.4.3" - "4.4.4" + "4.4.1" + "4.4.2" + "4.4.3" + "4.4.4" + "4.4.5" + "4.5.0" + "4.5.1" ) usage() { @@ -18,16 +21,14 @@ usage() { exit -1 } -if [ $# -lt 2 ] - then - echo "Incorrect number of arguments " $# - usage +if [ $# -lt 2 ]; then + echo "Incorrect number of arguments " $# + usage fi -if [[ ! " ${versions[*]} " =~ " ${1} " ]] - then - echo "Version ${1} not found in ${versions[*]}" - exit -1 +if [[ ! " ${versions[*]} " =~ " ${1} " ]]; then + echo "Version ${1} not found in ${versions[*]}" + exit -1 fi export WAZUH_STACK=${1} @@ -39,38 +40,37 @@ profile="standard" export WAZUH_DASHBOARD_CONF=./config/wazuh_dashboard/wazuh_dashboard.yml export SEC_CONFIG_FILE=./config/wazuh_indexer/config.yml -if [[ "$3" =~ "saml" ]] -then +if [[ "$3" =~ "saml" ]]; then profile="saml" export WAZUH_DASHBOARD_CONF=./config/wazuh_dashboard/wazuh_dashboard_saml.yml export SEC_CONFIG_FILE=./config/wazuh_indexer/config-saml.yml fi case "$2" in - up) - docker compose --profile $profile -f rel.yml -p ${COMPOSE_PROJECT_NAME} up -Vd - echo - echo "1. (Optional) Enroll an agent (Ubuntu 20.04):" - echo "docker run --name ${COMPOSE_PROJECT_NAME}-agent --network ${COMPOSE_PROJECT_NAME} --label com.docker.compose.project=${COMPOSE_PROJECT_NAME} -d ubuntu:20.04 bash -c '" - echo " apt update -y" - echo " apt install -y curl lsb-release" - echo " curl -so \wazuh-agent-${WAZUH_STACK}.deb \\" - echo " https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${WAZUH_STACK}-1_amd64.deb \\" - echo " && WAZUH_MANAGER='wazuh.manager' WAZUH_AGENT_GROUP='default' dpkg -i ./wazuh-agent-${WAZUH_STACK}.deb" - echo - echo " /etc/init.d/wazuh-agent start" - echo " tail -f /var/ossec/logs/ossec.log" - echo "'" - echo - ;; - down) - docker compose --profile $profile -f rel.yml -p ${COMPOSE_PROJECT_NAME} down -v --remove-orphans - ;; - stop) - docker compose --profile $profile -f rel.yml -p ${COMPOSE_PROJECT_NAME} stop - ;; - *) - echo "Action must be either up or down" - usage - ;; +up) + docker compose --profile $profile -f rel.yml -p ${COMPOSE_PROJECT_NAME} up -Vd + echo + echo "1. (Optional) Enroll an agent (Ubuntu 20.04):" + echo "docker run --name ${COMPOSE_PROJECT_NAME}-agent --network ${COMPOSE_PROJECT_NAME} --label com.docker.compose.project=${COMPOSE_PROJECT_NAME} -d ubuntu:20.04 bash -c '" + echo " apt update -y" + echo " apt install -y curl lsb-release" + echo " curl -so \wazuh-agent-${WAZUH_STACK}.deb \\" + echo " https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${WAZUH_STACK}-1_amd64.deb \\" + echo " && WAZUH_MANAGER='wazuh.manager' WAZUH_AGENT_GROUP='default' dpkg -i ./wazuh-agent-${WAZUH_STACK}.deb" + echo + echo " /etc/init.d/wazuh-agent start" + echo " tail -f /var/ossec/logs/ossec.log" + echo "'" + echo + ;; +down) + docker compose --profile $profile -f rel.yml -p ${COMPOSE_PROJECT_NAME} down -v --remove-orphans + ;; +stop) + docker compose --profile $profile -f rel.yml -p ${COMPOSE_PROJECT_NAME} stop + ;; +*) + echo "Action must be either up or down" + usage + ;; esac diff --git a/docker/wazuh-4.x-es/pre.sh b/docker/wazuh-4.x-es/pre.sh index bb820548c5..4f1ef6606f 100755 --- a/docker/wazuh-4.x-es/pre.sh +++ b/docker/wazuh-4.x-es/pre.sh @@ -13,10 +13,11 @@ elastic_versions=( "7.17.4" "7.17.5" "7.17.6" - "7.17.7" - "7.17.8" - "7.17.9" - "7.17.10" + "7.17.7" + "7.17.8" + "7.17.9" + "7.17.10" + "7.17.11" ) wazuh_api_version=( @@ -33,9 +34,12 @@ wazuh_api_version=( "4.3.10" "4.4.0" "4.4.1" - "4.4.2" - "4.4.3" - "4.4.4" + "4.4.2" + "4.4.3" + "4.4.4" + "4.4.5" + "4.5.0" + "4.5.1" ) usage() { @@ -56,26 +60,24 @@ usage() { exit -1 } -if [ $# -ne 3 ] - then - echo "Incorrect number of arguments " $# - usage +if [ $# -ne 3 ]; then + echo "Incorrect number of arguments " $# + usage fi -if [[ ! " ${elastic_versions[*]} " =~ " ${1} " ]] - then - echo "Version ${1} not found in ${elastic_versions[*]}" - exit -1 +if [[ ! " ${elastic_versions[*]} " =~ " ${1} " ]]; then + echo "Version ${1} not found in ${elastic_versions[*]}" + exit -1 fi # [ -n "$2" ] && [ "$2" -eq "$2" ] 2>/dev/null if [ $? -ne 0 ]; then - echo "Version ${2} not found in ${wazuh_api_version[*]}" - exit -1 + echo "Version ${2} not found in ${wazuh_api_version[*]}" + exit -1 fi wazuh_version=$2 -cat << EOF > config/imposter/api_info.json +cat <config/imposter/api_info.json { "data": { "title": "Wazuh API REST", @@ -99,39 +101,39 @@ export KIBANA_PORT=${PORT:-5601} export COMPOSE_PROJECT_NAME=es-pre-${ES_VERSION//./} case "$3" in - up) - # recreate volumes - docker compose -f pre.yml up -Vd +up) + # recreate volumes + docker compose -f pre.yml up -Vd - # This installs Wazuh and integrates with a default Elastic stack - # v=$( echo -n $ES_VERSION | sed 's/\.//g' ) - echo - echo "Install the pre-release package manually with:" - echo - echo "1. Copy the pre-release package to the running Kibana container:" - echo "docker cp wazuh_kibana-${wazuh_version}_${ES_VERSION}-1.zip ${COMPOSE_PROJECT_NAME}-kibana-1:/tmp" - echo - echo "2. Install the pre-release package:" - echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-kibana-1 /usr/share/kibana/bin/kibana-plugin install file:///tmp/wazuh_kibana-${wazuh_version}_${ES_VERSION}-1.zip" - echo - echo "3. Restart Kibana:" - echo "docker restart ${COMPOSE_PROJECT_NAME}-kibana-1" - echo - echo "4. Upload the Wazuh app configuration:" - echo "docker cp ./config/kibana/wazuh.yml ${COMPOSE_PROJECT_NAME}-kibana-1:/usr/share/kibana/data/wazuh/config/" - echo - echo "5. Open Kibana in a browser:" - echo "http://localhost:${KIBANA_PORT}" - echo - ;; - down) - # delete volumes - docker compose -f pre.yml down -v --remove-orphans - ;; - stop) - docker compose -f pre.yml -p ${COMPOSE_PROJECT_NAME} stop - ;; - *) - usage - ;; + # This installs Wazuh and integrates with a default Elastic stack + # v=$( echo -n $ES_VERSION | sed 's/\.//g' ) + echo + echo "Install the pre-release package manually with:" + echo + echo "1. Copy the pre-release package to the running Kibana container:" + echo "docker cp wazuh_kibana-${wazuh_version}_${ES_VERSION}-1.zip ${COMPOSE_PROJECT_NAME}-kibana-1:/tmp" + echo + echo "2. Install the pre-release package:" + echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-kibana-1 /usr/share/kibana/bin/kibana-plugin install file:///tmp/wazuh_kibana-${wazuh_version}_${ES_VERSION}-1.zip" + echo + echo "3. Restart Kibana:" + echo "docker restart ${COMPOSE_PROJECT_NAME}-kibana-1" + echo + echo "4. Upload the Wazuh app configuration:" + echo "docker cp ./config/kibana/wazuh.yml ${COMPOSE_PROJECT_NAME}-kibana-1:/usr/share/kibana/data/wazuh/config/" + echo + echo "5. Open Kibana in a browser:" + echo "http://localhost:${KIBANA_PORT}" + echo + ;; +down) + # delete volumes + docker compose -f pre.yml down -v --remove-orphans + ;; +stop) + docker compose -f pre.yml -p ${COMPOSE_PROJECT_NAME} stop + ;; +*) + usage + ;; esac diff --git a/docker/wazuh-4.x-es/rel.sh b/docker/wazuh-4.x-es/rel.sh index 06be6384e3..1aef8980b5 100755 --- a/docker/wazuh-4.x-es/rel.sh +++ b/docker/wazuh-4.x-es/rel.sh @@ -13,10 +13,11 @@ elastic_versions=( "7.17.4" "7.17.5" "7.17.6" - "7.17.7" - "7.17.8" - "7.17.9" - "7.17.10" + "7.17.7" + "7.17.8" + "7.17.9" + "7.17.10" + "7.17.11" ) wazuh_versions=( @@ -29,13 +30,16 @@ wazuh_versions=( "4.3.6" "4.3.7" "4.3.8" - "4.3.9" - "4.3.10" - "4.4.0" - "4.4.1" - "4.4.2" - "4.4.3" - "4.4.4" + "4.3.9" + "4.3.10" + "4.4.0" + "4.4.1" + "4.4.2" + "4.4.3" + "4.4.4" + "4.4.5" + "4.5.0" + "4.5.1" ) usage() { @@ -49,22 +53,19 @@ usage() { exit -1 } -if [ $# -ne 3 ] - then - echo "Incorrect number of arguments " $# - usage +if [ $# -ne 3 ]; then + echo "Incorrect number of arguments " $# + usage fi -if [[ ! " ${elastic_versions[*]} " =~ " ${1} " ]] - then - echo "Version ${1} not found in ${elastic_versions[*]}" - exit -1 +if [[ ! " ${elastic_versions[*]} " =~ " ${1} " ]]; then + echo "Version ${1} not found in ${elastic_versions[*]}" + exit -1 fi -if [[ ! " ${wazuh_versions[*]} " =~ " ${2} " ]] - then - echo "Version ${2} not found in ${wazuh_versions[*]}" - exit -1 +if [[ ! " ${wazuh_versions[*]} " =~ " ${2} " ]]; then + echo "Version ${2} not found in ${wazuh_versions[*]}" + exit -1 fi export ES_VERSION=$1 @@ -77,48 +78,48 @@ export KIBANA_PORT=${PORT:-5601} export COMPOSE_PROJECT_NAME=es-rel-${ES_VERSION//./} case "$3" in - up) - # recreate volumes - docker compose -f rel.yml up -Vd +up) + # recreate volumes + docker compose -f rel.yml up -Vd - # This installs Wazuh and integrates with a default Elastic stack - # v=$( echo -n $ES_VERSION | sed 's/\.//g' ) - echo - echo "Install Wazuh ${WAZUH_VERSION} into Elastic ${ES_VERSION} manually with:" - echo - echo "1. Install the Wazuh app for Kibana" - echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-kibana-1 /usr/share/kibana/bin/kibana-plugin install https://packages.wazuh.com/4.x/ui/kibana/wazuh_kibana-${WAZUH_VERSION}_${ES_VERSION}-1.zip" - echo - echo "2. Restart Kibana" - echo "docker restart ${COMPOSE_PROJECT_NAME}-kibana-1" - echo - echo "3. Configure Kibana" - echo "docker cp ./config/kibana/wazuh.yml ${COMPOSE_PROJECT_NAME}-kibana-1:/usr/share/kibana/data/wazuh/config/" - echo - echo "4. Open Kibana in a browser:" - echo "http://localhost:${KIBANA_PORT}" - echo - echo "5. (Optional) Enroll an agent (Ubuntu 20.04):" - echo "docker run --name ${COMPOSE_PROJECT_NAME}-agent --network ${COMPOSE_PROJECT_NAME} --label com.docker.compose.project=${COMPOSE_PROJECT_NAME} -d ubuntu:20.04 bash -c '" - echo " apt update -y" - echo " apt install -y curl lsb-release" - echo " curl -so \wazuh-agent-${WAZUH_VERSION}.deb \\" - echo " https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${WAZUH_VERSION}-1_amd64.deb \\" - echo " && WAZUH_MANAGER='wazuh.manager' WAZUH_AGENT_GROUP='default' dpkg -i ./wazuh-agent-${WAZUH_VERSION}.deb" - echo - echo " /etc/init.d/wazuh-agent start" - echo " tail -f /var/ossec/logs/ossec.log" - echo "'" - echo - ;; - down) - # delete volumes - docker compose -f rel.yml down -v --remove-orphans - ;; - stop) - docker compose -f rel.yml -p ${COMPOSE_PROJECT_NAME} stop - ;; - *) - usage - ;; + # This installs Wazuh and integrates with a default Elastic stack + # v=$( echo -n $ES_VERSION | sed 's/\.//g' ) + echo + echo "Install Wazuh ${WAZUH_VERSION} into Elastic ${ES_VERSION} manually with:" + echo + echo "1. Install the Wazuh app for Kibana" + echo "docker exec -ti ${COMPOSE_PROJECT_NAME}-kibana-1 /usr/share/kibana/bin/kibana-plugin install https://packages.wazuh.com/4.x/ui/kibana/wazuh_kibana-${WAZUH_VERSION}_${ES_VERSION}-1.zip" + echo + echo "2. Restart Kibana" + echo "docker restart ${COMPOSE_PROJECT_NAME}-kibana-1" + echo + echo "3. Configure Kibana" + echo "docker cp ./config/kibana/wazuh.yml ${COMPOSE_PROJECT_NAME}-kibana-1:/usr/share/kibana/data/wazuh/config/" + echo + echo "4. Open Kibana in a browser:" + echo "http://localhost:${KIBANA_PORT}" + echo + echo "5. (Optional) Enroll an agent (Ubuntu 20.04):" + echo "docker run --name ${COMPOSE_PROJECT_NAME}-agent --network ${COMPOSE_PROJECT_NAME} --label com.docker.compose.project=${COMPOSE_PROJECT_NAME} -d ubuntu:20.04 bash -c '" + echo " apt update -y" + echo " apt install -y curl lsb-release" + echo " curl -so \wazuh-agent-${WAZUH_VERSION}.deb \\" + echo " https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${WAZUH_VERSION}-1_amd64.deb \\" + echo " && WAZUH_MANAGER='wazuh.manager' WAZUH_AGENT_GROUP='default' dpkg -i ./wazuh-agent-${WAZUH_VERSION}.deb" + echo + echo " /etc/init.d/wazuh-agent start" + echo " tail -f /var/ossec/logs/ossec.log" + echo "'" + echo + ;; +down) + # delete volumes + docker compose -f rel.yml down -v --remove-orphans + ;; +stop) + docker compose -f rel.yml -p ${COMPOSE_PROJECT_NAME} stop + ;; +*) + usage + ;; esac diff --git a/plugins/main/common/api-info/endpoints.json b/plugins/main/common/api-info/endpoints.json index 26157f21f8..0691b9fb38 100644 --- a/plugins/main/common/api-info/endpoints.json +++ b/plugins/main/common/api-info/endpoints.json @@ -7,9 +7,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.default_controller.default_info", "description": "Return basic information about the API", "summary": "Get API info", - "tags": [ - "API Info" - ], + "tags": ["API Info"], "query": [ { "name": "pretty", @@ -26,9 +24,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agents", "description": "Return information about all available agents or a list of them", "summary": "List agents", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -57,10 +53,7 @@ "description": "Agent groups configuration sync status", "schema": { "type": "string", - "enum": [ - "synced", - "not synced" - ] + "enum": ["synced", "not synced"] } }, { @@ -205,12 +198,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "active", - "pending", - "never_connected", - "disconnected" - ] + "enum": ["active", "pending", "never_connected", "disconnected"] }, "minItems": 1 } @@ -238,9 +226,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_config", "description": "Return the active configuration the agent is currently using. This can be different from the configuration present in the configuration file, if it has been modified and the agent has not been restarted yet", "summary": "Get active configuration", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -338,9 +324,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_daemon_stats", "description": "Return Wazuh statistical information from specified daemons in a specified agent", "summary": "Get Wazuh daemon stats from an agent", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -362,10 +346,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "wazuh-analysisd", - "wazuh-remoted" - ] + "enum": ["wazuh-analysisd", "wazuh-remoted"] } } }, @@ -392,9 +373,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_sync_agent", "description": "Return whether the agent configuration has been synchronized with the agent or not. This can be useful to check after updating a group configuration", "summary": "Get configuration sync status", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -432,9 +411,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_key", "description": "Return the key of an agent", "summary": "Get key", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -472,9 +449,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_component_stats", "description": "Return Wazuh's {component} statistical information from agent {agent_id}", "summary": "Get agent's component stats", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -493,10 +468,7 @@ "required": true, "schema": { "type": "string", - "enum": [ - "logcollector", - "agent" - ] + "enum": ["logcollector", "agent"] } } ], @@ -524,9 +496,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_no_group", "description": "Return a list with all the available agents without an assigned group", "summary": "List agents without group", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "limit", @@ -606,9 +576,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_outdated", "description": "Return the list of outdated agents", "summary": "List outdated agents", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "limit", @@ -677,9 +645,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_fields", "description": "Return all the different combinations that agents have for the selected fields. It also indicates the total number of agents that have each combination", "summary": "List agents distinct", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "fields", @@ -759,9 +725,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_summary_os", "description": "Return a summary of the OS of available agents", "summary": "Summarize agents OS", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "pretty", @@ -786,9 +750,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_summary_status", "description": "Return a summary of the connection and groups configuration synchronization statuses of available agents", "summary": "Summarize agents status", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "pretty", @@ -813,9 +775,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agent_upgrade", "description": "Return the agents upgrade results", "summary": "Get upgrade results", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -941,9 +901,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.ciscat_controller.get_agents_ciscat_results", "description": "Return the agent's ciscat results info", "summary": "Get results", - "tags": [ - "Ciscat" - ], + "tags": ["Ciscat"], "args": [ { "name": ":agent_id", @@ -1105,9 +1063,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_configuration_node", "description": "Return wazuh configuration used in node {node_id}. The 'section' and 'field' parameters will be ignored if 'raw' parameter is provided.", "summary": "Get node config", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1199,9 +1155,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_node_config", "description": "Return the requested configuration in JSON format for the specified node", "summary": "Get node active configuration", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":component", @@ -1297,9 +1251,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_daemon_stats_node", "description": "Return Wazuh statistical information from specified daemons in a specified cluster node", "summary": "Get Wazuh daemon stats from a cluster node", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1319,11 +1271,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "wazuh-analysisd", - "wazuh-remoted", - "wazuh-db" - ] + "enum": ["wazuh-analysisd", "wazuh-remoted", "wazuh-db"] } } }, @@ -1350,9 +1298,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_info_node", "description": "Return basic information about a specified node such as version, compilation date, installation path", "summary": "Get node info", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1388,9 +1334,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_log_node", "description": "Return the last 2000 wazuh log entries in the specified node", "summary": "Get node logs", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1493,9 +1437,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_log_summary_node", "description": "Return a summary of the last 2000 wazuh log entries in the specified node", "summary": "Get node logs summary", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1531,9 +1473,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_stats_node", "description": "Return Wazuh statistical information in node {node_id} for the current or specified date", "summary": "Get node stats", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1577,9 +1517,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_stats_analysisd_node", "description": "Return Wazuh analysisd statistical information in node {node_id}", "summary": "Get node stats analysisd", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1615,9 +1553,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_stats_hourly_node", "description": "Return Wazuh statistical information in node {node_id} per hour. Each number in the averages field represents the average of alerts per hour", "summary": "Get node stats hour", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1653,9 +1589,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_stats_remoted_node", "description": "Return Wazuh remoted statistical information in node {node_id}", "summary": "Get node stats remoted", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1691,9 +1625,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_stats_weekly_node", "description": "Return Wazuh statistical information in node {node_id} per week. Each number in the averages field represents the average of alerts per hour for that specific day", "summary": "Get node stats week", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1729,9 +1661,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_status_node", "description": "Return the status of all Wazuh daemons in node node_id", "summary": "Get node status", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -1767,9 +1697,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_api_config", "description": "Return the API configuration of all nodes (or a list of them) in JSON format", "summary": "Get nodes API config", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "nodes_list", @@ -1804,9 +1732,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_conf_validation", "description": "Return whether the Wazuh configuration is correct or not in all cluster nodes or a list of them", "summary": "Check nodes config", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "nodes_list", @@ -1841,9 +1767,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_healthcheck", "description": "Return cluster healthcheck information for all nodes or a list of them. Such information includes last keep alive, last synchronization time and number of agents reporting on each node", "summary": "Get nodes healthcheck", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "nodes_list", @@ -1878,9 +1802,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_config", "description": "Return the current node cluster configuration", "summary": "Get local node config", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "pretty", @@ -1905,9 +1827,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_cluster_node", "description": "Return basic information about the cluster node receiving the request", "summary": "Get local node info", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "pretty", @@ -1932,9 +1852,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_cluster_nodes", "description": "Get information about all nodes in the cluster or a list of them", "summary": "Get nodes info", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "limit", @@ -2014,10 +1932,7 @@ "description": "Filter by node type", "schema": { "type": "string", - "enum": [ - "worker", - "master" - ] + "enum": ["worker", "master"] } }, { @@ -2035,9 +1950,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_nodes_ruleset_sync_status", "description": "Return ruleset synchronization status for all nodes or a list of them. This synchronization only covers the user custom ruleset", "summary": "Get cluster nodes ruleset synchronization status", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "nodes_list", @@ -2072,9 +1985,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.get_status", "description": "Return information about the cluster status", "summary": "Get cluster status", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "pretty", @@ -2099,9 +2010,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.decoder_controller.get_decoders", "description": "Return information about all decoders included in ossec.conf. This information include decoder's route, decoder's name, decoder's file among others", "summary": "List decoders", - "tags": [ - "Decoders" - ], + "tags": ["Decoders"], "query": [ { "name": "decoder_names", @@ -2201,11 +2110,7 @@ "description": "Filter by list status. Use commas to enter multiple statuses", "schema": { "type": "string", - "enum": [ - "enabled", - "disabled", - "all" - ], + "enum": ["enabled", "disabled", "all"], "minItems": 1 } }, @@ -2224,9 +2129,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.decoder_controller.get_decoders_files", "description": "Return information about all decoders files used in Wazuh. This information include decoder's file, decoder's route and decoder's status among others", "summary": "Get files", - "tags": [ - "Decoders" - ], + "tags": ["Decoders"], "query": [ { "name": "filename", @@ -2297,11 +2200,7 @@ "description": "Filter by list status. Use commas to enter multiple statuses", "schema": { "type": "string", - "enum": [ - "enabled", - "disabled", - "all" - ], + "enum": ["enabled", "disabled", "all"], "minItems": 1 } }, @@ -2320,9 +2219,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.decoder_controller.get_file", "description": "Get the content of a specified decoder file", "summary": "Get decoders file content", - "tags": [ - "Decoders" - ], + "tags": ["Decoders"], "args": [ { "name": ":filename", @@ -2366,9 +2263,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.decoder_controller.get_decoders_parents", "description": "Return information about all parent decoders. A parent decoder is a decoder used as base of other decoders", "summary": "Get parent decoders", - "tags": [ - "Decoders" - ], + "tags": ["Decoders"], "query": [ { "name": "limit", @@ -2441,9 +2336,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_cis_cat_results", "description": "Return CIS-CAT results for all agents or a list of them", "summary": "Get agents CIS-CAT results", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -2598,9 +2491,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_hardware_info", "description": "Return all agents (or a list of them) hardware info. This information include cpu, ram, scan info among others of all agents", "summary": "Get agents hardware", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -2737,9 +2628,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_hotfixes_info", "description": "Return all agents (or a list of them) hotfixes info", "summary": "Get agents hotfixes", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -2832,9 +2721,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_network_address_info", "description": "Return all agents (or a list of them) IPv4 and IPv6 addresses associated to their network interfaces. This information include used IP protocol, interface, and IP address among others", "summary": "Get agents netaddr", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "address", @@ -2952,9 +2839,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_network_interface_info", "description": "Return all agents (or a list of them) network interfaces. This information includes rx, scan, tx info and some network information among other", "summary": "Get agents netiface", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "adapter", @@ -3153,9 +3038,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_network_protocol_info", "description": "Return all agents (or a list of them) routing configuration for each network interface. This information includes interface, type protocol information among other", "summary": "Get agents netproto", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -3176,12 +3059,7 @@ "schema": { "type": "string", "description": "DHCP status", - "enum": [ - "enabled", - "disabled", - "unknown", - "BOOTP" - ] + "enum": ["enabled", "disabled", "unknown", "BOOTP"] } }, { @@ -3279,9 +3157,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_os_info", "description": "Return all agents (or a list of them) OS info. This information includes os information, architecture information among other", "summary": "Get agents OS", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -3407,9 +3283,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_packages_info", "description": "Return all agents (or a list of them) packages info. This information includes name, section, size, and priority information of all packages among other", "summary": "Get agents packages", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -3533,9 +3407,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_ports_info", "description": "Return all agents (or a list of them) ports info. This information includes local IP, Remote IP, protocol information among other", "summary": "Get agents ports", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -3685,9 +3557,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.get_processes_info", "description": "Return all agents (or a list of them) processes info", "summary": "Get agents processes", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -3885,9 +3755,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_list_group", "description": "Get information about all groups or a list of them. Returns a list containing basic information about each group such as number of agents belonging to the group and the checksums of the configuration and shared files", "summary": "Get groups", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "query": [ { "name": "groups_list", @@ -3982,9 +3850,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_agents_in_group", "description": "Return the list of agents that belong to the specified group", "summary": "Get agents in a group", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "args": [ { "name": ":group_id", @@ -4068,12 +3934,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "active", - "pending", - "never_connected", - "disconnected" - ] + "enum": ["active", "pending", "never_connected", "disconnected"] }, "minItems": 1 } @@ -4093,9 +3954,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_group_config", "description": "Return the group configuration defined in the `agent.conf` file", "summary": "Get group configuration", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "args": [ { "name": ":group_id", @@ -4153,9 +4012,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_group_files", "description": "Return the files placed under the group directory", "summary": "Get group files", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "args": [ { "name": ":group_id", @@ -4250,9 +4107,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_group_file_json", "description": "Return the content of the specified group file parsed to JSON", "summary": "Get a file in group", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "args": [ { "name": ":file_name", @@ -4290,12 +4145,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "conf", - "rootkit_files", - "rootkit_trojans", - "rcl" - ] + "enum": ["conf", "rootkit_files", "rootkit_trojans", "rcl"] } } }, @@ -4314,9 +4164,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.get_group_file_xml", "description": "Return the contents of the specified group file parsed to XML", "summary": "Get a file in group", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "args": [ { "name": ":file_name", @@ -4354,12 +4202,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "conf", - "rootkit_files", - "rootkit_trojans", - "rcl" - ] + "enum": ["conf", "rootkit_files", "rootkit_trojans", "rcl"] } } }, @@ -4378,9 +4221,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cdb_list_controller.get_lists", "description": "Return the contents of all CDB lists. Optionally, the result can be filtered by several criteria. See available parameters for more details", "summary": "Get CDB lists info", - "tags": [ - "Lists" - ], + "tags": ["Lists"], "query": [ { "name": "filename", @@ -4472,9 +4313,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cdb_list_controller.get_lists_files", "description": "Return the path from all CDB lists. Use this method to know all the CDB lists and their location in the filesystem relative to Wazuh installation folder", "summary": "Get CDB lists files", - "tags": [ - "Lists" - ], + "tags": ["Lists"], "query": [ { "name": "filename", @@ -4555,9 +4394,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cdb_list_controller.get_file", "description": "Return the content of a CDB list file. Only the filename can be specified. It will be searched recursively if not found", "summary": "Get CDB list file content", - "tags": [ - "Lists" - ], + "tags": ["Lists"], "args": [ { "name": ":filename", @@ -4601,9 +4438,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_api_config", "description": "Return the local API configuration in JSON format", "summary": "Get API config", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -4628,9 +4463,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_configuration", "description": "Return wazuh configuration used. The 'section' and 'field' parameters will be ignored if 'raw' parameter is provided.", "summary": "Get configuration", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "field", @@ -4711,9 +4544,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_manager_config_ondemand", "description": "Return the requested active configuration in JSON format", "summary": "Get active configuration", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "args": [ { "name": ":component", @@ -4800,9 +4631,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_conf_validation", "description": "Return whether the Wazuh configuration is correct", "summary": "Check config", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -4827,9 +4656,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_daemon_stats", "description": "Return Wazuh statistical information from specified daemons", "summary": "Get Wazuh daemon stats", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "daemons_list", @@ -4838,11 +4665,7 @@ "type": "array", "items": { "type": "string", - "enum": [ - "wazuh-analysisd", - "wazuh-remoted", - "wazuh-db" - ] + "enum": ["wazuh-analysisd", "wazuh-remoted", "wazuh-db"] } } }, @@ -4869,9 +4692,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_info", "description": "Return basic information such as version, compilation date, installation path", "summary": "Get information", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -4896,9 +4717,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_log", "description": "Return the last 2000 wazuh log entries", "summary": "Get logs", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "level", @@ -4990,9 +4809,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_log_summary", "description": "Return a summary of the last 2000 wazuh log entries", "summary": "Get logs summary", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -5017,9 +4834,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_stats", "description": "Return Wazuh statistical information for the current or specified date", "summary": "Get stats", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "date", @@ -5052,9 +4867,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_stats_analysisd", "description": "Return Wazuh analysisd statistical information", "summary": "Get stats analysisd", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -5079,9 +4892,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_stats_hourly", "description": "Return Wazuh statistical information per hour. Each number in the averages field represents the average of alerts per hour", "summary": "Get stats hour", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -5106,9 +4917,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_stats_remoted", "description": "Return Wazuh remoted statistical information", "summary": "Get stats remoted", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -5133,9 +4942,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_stats_weekly", "description": "Return Wazuh statistical information per week. Each number in the averages field represents the average of alerts per hour for that specific day", "summary": "Get stats week", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -5160,9 +4967,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.get_status", "description": "Return the status of all Wazuh daemons", "summary": "Get status", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -5187,9 +4992,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_groups", "description": "Return the groups from MITRE database", "summary": "Get MITRE groups", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "group_ids", @@ -5280,9 +5083,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_metadata", "description": "Return the metadata from MITRE database", "summary": "Get MITRE metadata", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "pretty", @@ -5307,9 +5108,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_mitigations", "description": "Return the mitigations from MITRE database", "summary": "Get MITRE mitigations", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "limit", @@ -5400,9 +5199,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_references", "description": "Return the references from MITRE database", "summary": "Get MITRE references", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "limit", @@ -5493,9 +5290,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_software", "description": "Return the software from MITRE database", "summary": "Get MITRE software", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "limit", @@ -5586,9 +5381,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_tactics", "description": "Return the tactics from MITRE database", "summary": "Get MITRE tactics", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "limit", @@ -5679,9 +5472,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.mitre_controller.get_techniques", "description": "Return the techniques from MITRE database", "summary": "Get MITRE techniques", - "tags": [ - "MITRE" - ], + "tags": ["MITRE"], "query": [ { "name": "limit", @@ -5772,9 +5563,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.overview_controller.get_overview_agents", "description": "Return a dictionary with a full agents overview", "summary": "Get agents overview", - "tags": [ - "Overview" - ], + "tags": ["Overview"], "query": [ { "name": "pretty", @@ -5799,9 +5588,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rootcheck_controller.get_rootcheck_agent", "description": "Return the rootcheck database of an agent", "summary": "Get results", - "tags": [ - "Rootcheck" - ], + "tags": ["Rootcheck"], "args": [ { "name": ":agent_id", @@ -5926,9 +5713,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rootcheck_controller.get_last_scan_agent", "description": "Return the timestamp of the last rootcheck scan of an agent", "summary": "Get last scan datetime", - "tags": [ - "Rootcheck" - ], + "tags": ["Rootcheck"], "args": [ { "name": ":agent_id", @@ -5966,9 +5751,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.get_rules", "description": "Return a list containing information about each rule such as file where it's defined, description, rule group, status, etc", "summary": "List rules", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "query": [ { "name": "filename", @@ -6133,11 +5916,7 @@ "description": "Filter by list status. Use commas to enter multiple statuses", "schema": { "type": "string", - "enum": [ - "enabled", - "disabled", - "all" - ], + "enum": ["enabled", "disabled", "all"], "minItems": 1 } }, @@ -6164,9 +5943,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.get_rules_files", "description": "Return a list containing all files used to define rules and their status", "summary": "Get files", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "query": [ { "name": "filename", @@ -6237,11 +6014,7 @@ "description": "Filter by list status. Use commas to enter multiple statuses", "schema": { "type": "string", - "enum": [ - "enabled", - "disabled", - "all" - ], + "enum": ["enabled", "disabled", "all"], "minItems": 1 } }, @@ -6260,9 +6033,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.get_file", "description": "Get the content of a specified rule in the ruleset", "summary": "Get rules file content", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "args": [ { "name": ":filename", @@ -6306,9 +6077,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.get_rules_groups", "description": "Return a list containing all rule groups names", "summary": "Get groups", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "query": [ { "name": "limit", @@ -6370,9 +6139,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.get_rules_requirement", "description": "Return all specified requirement names defined in the Wazuh ruleset", "summary": "Get requirements", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "args": [ { "name": ":requirement", @@ -6452,9 +6219,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.sca_controller.get_sca_agent", "description": "Return the security SCA database of an agent", "summary": "Get results", - "tags": [ - "SCA" - ], + "tags": ["SCA"], "args": [ { "name": ":agent_id", @@ -6577,9 +6342,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.sca_controller.get_sca_checks", "description": "Return the policy monitoring alerts for a given policy", "summary": "Get policy checks", - "tags": [ - "SCA" - ], + "tags": ["SCA"], "args": [ { "name": ":agent_id", @@ -6790,9 +6553,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_rbac_actions", "description": "Get all RBAC actions, including the potential related resources and endpoints.", "summary": "List RBAC actions", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "endpoint", @@ -6816,9 +6577,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_security_config", "description": "Return the security configuration in JSON format", "summary": "Get security config", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -6843,9 +6602,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_policies", "description": "Get all policies in the system, including the administrator policy", "summary": "List policies", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "limit", @@ -6930,9 +6687,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_rbac_resources", "description": "This method should be called to get all current defined RBAC resources.", "summary": "List RBAC resources", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -6969,9 +6724,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_roles", "description": "For a specific list, indicate the ids separated by commas. Example: ?role_ids=1,2,3", "summary": "List roles", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "limit", @@ -7056,9 +6809,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_rules", "description": "Get a list of security rules from the system or all of them. These rules must be mapped with roles to obtain certain access privileges. For a specific list, indicate the ids separated by commas. Example: ?rule_ids=1,2,3", "summary": "List security rules", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "limit", @@ -7143,9 +6894,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.deprecated_login_user", "description": "This method should be called to get an API token. This token will expire after auth_token_exp_timeout seconds (default: 900). This value can be changed using PUT /security/config", "summary": "Login", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "raw", @@ -7162,9 +6911,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_users", "description": "Get the information of a specified user", "summary": "List users", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "limit", @@ -7249,9 +6996,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_user_me", "description": "Get the information of the current user", "summary": "Get current user info", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -7276,9 +7021,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.get_user_me_policies", "description": "Get the processed policies information for the current user", "summary": "Get current user processed policies", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -7295,9 +7038,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscheck_controller.get_syscheck_agent", "description": "Return FIM findings in the specified agent", "summary": "Get results", - "tags": [ - "Syscheck" - ], + "tags": ["Syscheck"], "args": [ { "name": ":agent_id", @@ -7317,10 +7058,7 @@ "description": "Filter by architecture", "schema": { "type": "string", - "enum": [ - "[x32]", - "[x64]" - ] + "enum": ["[x32]", "[x64]"] } }, { @@ -7447,11 +7185,7 @@ "description": "Filter by file type. Registry_key and registry_value types are only available in Windows agents", "schema": { "type": "string", - "enum": [ - "file", - "registry_key", - "registry_value" - ] + "enum": ["file", "registry_key", "registry_value"] } }, { @@ -7485,9 +7219,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscheck_controller.get_last_scan_agent", "description": "Return when the last syscheck scan started and ended. If the scan is still in progress the end date will be unknown", "summary": "Get last scan datetime", - "tags": [ - "Syscheck" - ], + "tags": ["Syscheck"], "args": [ { "name": ":agent_id", @@ -7525,9 +7257,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_hardware_info", "description": "Return the agent's hardware info. This information include cpu, ram, scan info among others", "summary": "Get agent hardware", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -7576,9 +7306,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_hotfix_info", "description": "Return all hotfixes installed by Microsoft(R) in Windows(R) systems (KB... fixes)", "summary": "Get agent hotfixes", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -7678,9 +7406,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_network_address_info", "description": "Return the agent's network address info. This information include used IP protocol, interface, IP address among others", "summary": "Get agent netaddr", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -7813,9 +7539,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_network_interface_info", "description": "Return the agent's network interface info. This information include rx, scan, tx info and some network information among others", "summary": "Get agent netiface", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -8020,9 +7744,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_network_protocol_info", "description": "Return the agent's routing configuration for each network interface", "summary": "Get agent netproto", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -8043,12 +7765,7 @@ "schema": { "type": "string", "description": "DHCP status", - "enum": [ - "enabled", - "disabled", - "unknown", - "BOOTP" - ] + "enum": ["enabled", "disabled", "unknown", "BOOTP"] } }, { @@ -8153,9 +7870,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_os_info", "description": "Return the agent's OS info. This information include os information, architecture information among others of all agents", "summary": "Get agent OS", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -8204,9 +7919,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_packages_info", "description": "Return the agent's packages info. This information include name, section, size, priority information of all packages among others", "summary": "Get agent packages", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -8337,9 +8050,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_ports_info", "description": "Return the agent's ports info. This information include local IP, Remote IP, protocol information among others", "summary": "Get agent ports", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -8496,9 +8207,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscollector_controller.get_processes_info", "description": "Return the agent's processes info", "summary": "Get agent processes", - "tags": [ - "Syscollector" - ], + "tags": ["Syscollector"], "args": [ { "name": ":agent_id", @@ -8703,9 +8412,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.task_controller.get_tasks_status", "description": "Returns all available information about the specified tasks", "summary": "List tasks", - "tags": [ - "Tasks" - ], + "tags": ["Tasks"], "query": [ { "name": "agents_list", @@ -8842,9 +8549,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.vulnerability_controller.get_vulnerability_agent", "description": "Return the vulnerabilities of an agent", "summary": "Get vulnerabilities", - "tags": [ - "Vulnerability" - ], + "tags": ["Vulnerability"], "args": [ { "name": ":agent_id", @@ -8967,11 +8672,7 @@ "description": "Filter by CVE status", "schema": { "type": "string", - "enum": [ - "valid", - "pending", - "obsolete" - ] + "enum": ["valid", "pending", "obsolete"] } }, { @@ -8979,10 +8680,7 @@ "description": "Filter by CVE type", "schema": { "type": "string", - "enum": [ - "os", - "package" - ] + "enum": ["os", "package"] } }, { @@ -9008,9 +8706,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.vulnerability_controller.get_last_scan_agent", "description": "Return when the last full and partial vulnerability scan of a specified agent ended.", "summary": "Get last scan datetime", - "tags": [ - "Vulnerability" - ], + "tags": ["Vulnerability"], "args": [ { "name": ":agent_id", @@ -9048,9 +8744,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.vulnerability_controller.get_vulnerabilities_field_summary", "description": "Return a summary of the vulnerabilities' field of an agent", "summary": "Get agent vulnerabilities' field summary", - "tags": [ - "Vulnerability" - ], + "tags": ["Vulnerability"], "args": [ { "name": ":agent_id", @@ -9129,9 +8823,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.active_response_controller.run_command", "description": "Run an Active Response command on all agents or a list of them", "summary": "Run command", - "tags": [ - "Active-response" - ], + "tags": ["Active-response"], "query": [ { "name": "agents_list", @@ -9194,9 +8886,7 @@ } } }, - "required": [ - "command" - ] + "required": ["command"] } ] }, @@ -9205,9 +8895,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.put_agent_single_group", "description": "Assign an agent to a specified group", "summary": "Assign agent to group", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -9262,9 +8950,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.restart_agent", "description": "Restart the specified agent", "summary": "Restart agent", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -9302,9 +8988,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.put_multiple_agent_single_group", "description": "Assign all agents or a list of them to the specified group", "summary": "Assign agents to group", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -9359,9 +9043,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.restart_agents_by_group", "description": "Restart all agents which belong to a given group", "summary": "Restart agents in group", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":group_id", @@ -9398,9 +9080,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.restart_agents_by_node", "description": "Restart all agents which belong to a specific given node", "summary": "Restart agents in node", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":node_id", @@ -9436,9 +9116,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.reconnect_agents", "description": "Force reconnect all agents or a list of them", "summary": "Force reconnect agents", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -9476,9 +9154,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.restart_agents", "description": "Restart all agents or a list of them", "summary": "Restart agents", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -9516,9 +9192,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.put_upgrade_agents", "description": "Upgrade agents using a WPK file from online repository. When upgrading more than 3000 agents at the same time, it's highly recommended to use the parameter `wait_for_complete` set to `true` to avoid a possible API timeout", "summary": "Upgrade agents", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -9677,9 +9351,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.put_upgrade_custom_agents", "description": "Upgrade the agents using a local WPK file. When upgrading more than 3000 agents at the same time, it's highly recommended to use the parameter `wait_for_complete` set to `true` to avoid a possible API timeout", "summary": "Upgrade agents custom", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -9823,9 +9495,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.update_configuration", "description": "Replace wazuh configuration for the given node with the data contained in the API request", "summary": "Update node configuration", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "args": [ { "name": ":node_id", @@ -9861,9 +9531,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cluster_controller.put_restart", "description": "Restart all nodes in the cluster or a list of them", "summary": "Restart nodes", - "tags": [ - "Cluster" - ], + "tags": ["Cluster"], "query": [ { "name": "nodes_list", @@ -9898,9 +9566,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.decoder_controller.put_file", "description": "Upload or replace a user decoder file content", "summary": "Update decoders file", - "tags": [ - "Decoders" - ], + "tags": ["Decoders"], "args": [ { "name": ":filename", @@ -9944,9 +9610,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.put_group_config", "description": "Update an specified group's configuration. This API call expects a full valid XML file with the shared configuration tags/syntax", "summary": "Update group configuration", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "args": [ { "name": ":group_id", @@ -9983,9 +9647,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cdb_list_controller.put_file", "description": "Replace or upload a CDB list file with the data contained in the API request", "summary": "Update CDB list file", - "tags": [ - "Lists" - ], + "tags": ["Lists"], "args": [ { "name": ":filename", @@ -10029,9 +9691,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.logtest_controller.run_logtest_tool", "description": "Run logtest tool to check if a specified log raises any alert among other information", "summary": "Run logtest", - "tags": [ - "Logtest" - ], + "tags": ["Logtest"], "query": [ { "name": "pretty", @@ -10053,11 +9713,7 @@ "body": [ { "type": "object", - "required": [ - "event", - "log_format", - "location" - ], + "required": ["event", "log_format", "location"], "properties": { "token": { "type": "string", @@ -10084,9 +9740,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.update_configuration", "description": "Replace Wazuh configuration with the data contained in the API request", "summary": "Update Wazuh configuration", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -10111,9 +9765,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.manager_controller.put_restart", "description": "Restart the wazuh manager", "summary": "Restart manager", - "tags": [ - "Manager" - ], + "tags": ["Manager"], "query": [ { "name": "pretty", @@ -10138,9 +9790,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rootcheck_controller.put_rootcheck", "description": "Run rootcheck scan in all agents or a list of them", "summary": "Run scan", - "tags": [ - "Rootcheck" - ], + "tags": ["Rootcheck"], "query": [ { "name": "agents_list", @@ -10178,9 +9828,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.put_file", "description": "Upload or replace a user ruleset file content", "summary": "Update rules file", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "args": [ { "name": ":filename", @@ -10224,9 +9872,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.put_security_config", "description": "Update the security configuration with the data contained in the API request", "summary": "Update security config", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -10260,10 +9906,7 @@ "rbac_mode": { "description": "RBAC mode (white/black)", "type": "string", - "enum": [ - "white", - "black" - ], + "enum": ["white", "black"], "example": "white" } } @@ -10275,9 +9918,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.update_policy", "description": "Modify a policy, at least one property must be indicated", "summary": "Update policy", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":policy_id", @@ -10341,11 +9982,7 @@ "description": "Effect of the policy" } }, - "required": [ - "actions", - "resources", - "effect" - ] + "required": ["actions", "resources", "effect"] } } } @@ -10356,9 +9993,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.update_role", "description": "Modify a role, cannot modify associated policies in this endpoint, at least one property must be indicated", "summary": "Update role", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":role_id", @@ -10408,9 +10043,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.update_rule", "description": "Modify a security rule by specifying its ID", "summary": "Update security rule", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":rule_id", @@ -10464,18 +10097,14 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.revoke_all_tokens", "description": "This method should be called to revoke all active JWT tokens", "summary": "Revoke JWT tokens", - "tags": [ - "Security" - ] + "tags": ["Security"] }, { "name": "/security/users/:user_id", "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.update_user", "description": "Modify a user's password by specifying their ID", "summary": "Update users", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":user_id", @@ -10523,9 +10152,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.edit_run_as", "description": "Modify a user's allow_run_as flag by specifying their ID", "summary": "Enable/Disable run_as", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":user_id", @@ -10570,9 +10197,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscheck_controller.put_syscheck", "description": "Run FIM scan in all agents", "summary": "Run scan", - "tags": [ - "Syscheck" - ], + "tags": ["Syscheck"], "query": [ { "name": "agents_list", @@ -10610,9 +10235,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.vulnerability_controller.run_vulnerability_scan", "description": "Run a vulnerability detector scan in all nodes", "summary": "Run vulnerability detector scan", - "tags": [ - "Vulnerability" - ], + "tags": ["Vulnerability"], "query": [ { "name": "pretty", @@ -10642,9 +10265,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.add_agent", "description": "Add a new agent", "summary": "Add agent", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "pretty", @@ -10678,9 +10299,7 @@ "format": "alphanumeric" } }, - "required": [ - "name" - ] + "required": ["name"] } ] }, @@ -10689,9 +10308,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.insert_agent", "description": "Add an agent specifying its name, ID and IP. If an agent with the same name, the same ID or the same IP already exists, replace it using the `force` parameter", "summary": "Add agent full", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "pretty", @@ -10771,9 +10388,7 @@ } } }, - "required": [ - "name" - ] + "required": ["name"] } ] }, @@ -10782,9 +10397,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.post_new_agent", "description": "Add a new agent with name `agent_name`. This agent will use `any` as IP", "summary": "Add agent quick", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agent_name", @@ -10819,9 +10432,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.post_group", "description": "Create a new group", "summary": "Create a group", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "query": [ { "name": "pretty", @@ -10851,9 +10462,7 @@ "maxLength": 128 } }, - "required": [ - "group_id" - ] + "required": ["group_id"] } ] }, @@ -10862,9 +10471,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.add_policy", "description": "Add a new policy, all fields need to be specified", "summary": "Add policy", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -10886,10 +10493,7 @@ "body": [ { "type": "object", - "required": [ - "name", - "policy" - ], + "required": ["name", "policy"], "properties": { "name": { "description": "Policy name", @@ -10920,11 +10524,7 @@ "description": "Effect of the policy" } }, - "required": [ - "actions", - "resources", - "effect" - ] + "required": ["actions", "resources", "effect"] } } } @@ -10935,9 +10535,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.add_role", "description": "Add a new role, all fields need to be specified", "summary": "Add role", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -10959,9 +10557,7 @@ "body": [ { "type": "object", - "required": [ - "name" - ], + "required": ["name"], "properties": { "name": { "type": "string", @@ -10978,9 +10574,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.set_role_policy", "description": "Create a specified relation role-policy, one role may have multiples policies", "summary": "Add policies to role", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":role_id", @@ -11039,9 +10633,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.set_role_rule", "description": "Create a specific role-rule relation. One role may have multiple security rules", "summary": "Add security rules to role", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":role_id", @@ -11091,9 +10683,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.add_rule", "description": "Add a new security rule", "summary": "Add security rule", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -11115,10 +10705,7 @@ "body": [ { "type": "object", - "required": [ - "name", - "rule" - ], + "required": ["name", "rule"], "properties": { "name": { "type": "string", @@ -11139,9 +10726,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.login_user", "description": "This method should be called to get an API token. This token will expire after auth_token_exp_timeout seconds (default: 900). This value can be changed using PUT /security/config", "summary": "Login", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "raw", @@ -11158,9 +10743,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.run_as_login", "description": "This method should be called to get an API token using an authorization context body. This token will expire after auth_token_exp_timeout seconds (default: 900). This value can be changed using PUT /security/config", "summary": "Login auth_context", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "raw", @@ -11177,9 +10760,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.create_user", "description": "Add a new API user to the system", "summary": "Add user", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -11213,10 +10794,7 @@ "format": "password" } }, - "required": [ - "username", - "password" - ] + "required": ["username", "password"] } ] }, @@ -11225,9 +10803,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.set_user_role", "description": "Create a specified relation role-policy, one user may have multiples roles", "summary": "Add roles to user", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":user_id", @@ -11291,9 +10867,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.delete_agents", "description": "Delete all agents or a list of them based on optional criteria", "summary": "Delete agents", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -11456,9 +11030,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.delete_single_agent_multiple_groups", "description": "Remove the agent from all groups or a list of them. The agent will automatically revert to the default group if it is removed from all its assigned groups", "summary": "Remove agent from groups", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -11508,9 +11080,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.delete_single_agent_single_group", "description": "Remove an agent from a specified group. If the agent belongs to several groups, only the specified group will be deleted.", "summary": "Remove agent from group", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "args": [ { "name": ":agent_id", @@ -11558,9 +11128,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.delete_multiple_agent_single_group", "description": "Remove all agents assignment or a list of them from the specified group", "summary": "Remove agents from group", - "tags": [ - "Agents" - ], + "tags": ["Agents"], "query": [ { "name": "agents_list", @@ -11609,9 +11177,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.decoder_controller.delete_file", "description": "Delete a specified decoder file", "summary": "Delete decoders file", - "tags": [ - "Decoders" - ], + "tags": ["Decoders"], "args": [ { "name": ":filename", @@ -11647,9 +11213,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.clear_rootcheck_database", "description": "Clear rootcheck database for all agents or a list of them", "summary": "Clear rootcheck results", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -11688,9 +11252,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.experimental_controller.clear_syscheck_database", "description": "Clear the syscheck database for all agents or a list of them", "summary": "Clear agents FIM results", - "tags": [ - "Experimental" - ], + "tags": ["Experimental"], "query": [ { "name": "agents_list", @@ -11729,9 +11291,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.agent_controller.delete_groups", "description": "Delete all groups or a list of them", "summary": "Delete groups", - "tags": [ - "Groups" - ], + "tags": ["Groups"], "query": [ { "name": "groups_list", @@ -11770,9 +11330,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.cdb_list_controller.delete_file", "description": "Delete a specified CDB list file. Only the filename can be specified. It will be searched recursively if not found", "summary": "Delete CDB list file", - "tags": [ - "Lists" - ], + "tags": ["Lists"], "args": [ { "name": ":filename", @@ -11808,9 +11366,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.logtest_controller.end_logtest_session", "description": "Delete the saved logtest session corresponding to {token}", "summary": "End session", - "tags": [ - "Logtest" - ], + "tags": ["Logtest"], "args": [ { "name": ":token", @@ -11846,9 +11402,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rootcheck_controller.delete_rootcheck", "description": "Clear an agent's rootcheck database", "summary": "Clear results", - "tags": [ - "Rootcheck" - ], + "tags": ["Rootcheck"], "args": [ { "name": ":agent_id", @@ -11886,9 +11440,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.rule_controller.delete_file", "description": "Delete a specified rule file", "summary": "Delete rules file", - "tags": [ - "Rules" - ], + "tags": ["Rules"], "args": [ { "name": ":filename", @@ -11924,9 +11476,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.delete_security_config", "description": "Replaces the security configuration with the original one", "summary": "Restore default security config", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -11951,9 +11501,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.remove_policies", "description": "Delete a list of policies or all policies in the system, roles linked to policies are not going to be removed", "summary": "Delete policies", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "policy_ids", @@ -11991,9 +11539,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.remove_roles", "description": "Policies linked to roles are not going to be removed", "summary": "Delete roles", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -12031,9 +11577,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.remove_role_policy", "description": "Delete a specified relation role-policy", "summary": "Remove policies from role", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":role_id", @@ -12083,9 +11627,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.remove_role_rule", "description": "Delete a specific role-rule relation", "summary": "Remove security rules from role", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":role_id", @@ -12135,9 +11677,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.remove_rules", "description": "Delete a list of security rules or all security rules in the system, roles linked to rules are not going to be deleted", "summary": "Delete security rules", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -12175,18 +11715,14 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.logout_user", "description": "This method should be called to invalidate all the current user's tokens", "summary": "Logout current user", - "tags": [ - "Security" - ] + "tags": ["Security"] }, { "name": "/security/users", "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.delete_users", "description": "Delete a list of users by specifying their IDs", "summary": "Delete users", - "tags": [ - "Security" - ], + "tags": ["Security"], "query": [ { "name": "pretty", @@ -12224,9 +11760,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.security_controller.remove_user_role", "description": "Delete a specified relation user-roles", "summary": "Remove roles from user", - "tags": [ - "Security" - ], + "tags": ["Security"], "args": [ { "name": ":user_id", @@ -12276,9 +11810,7 @@ "documentation": "https://documentation.wazuh.com/4.6/user-manual/api/reference.html#operation/api.controllers.syscheck_controller.delete_syscheck_agent", "description": "Clear file integrity monitoring scan results for a specified agent. Only available for agents < 3.12.0, it doesn't apply for more recent ones", "summary": "Clear results", - "tags": [ - "Syscheck" - ], + "tags": ["Syscheck"], "args": [ { "name": ":agent_id", @@ -12313,4 +11845,4 @@ } ] } -] \ No newline at end of file +] diff --git a/plugins/main/common/services/web_documentation.test.ts b/plugins/main/common/services/web_documentation.test.ts index 1287549a58..967e8b3233 100644 --- a/plugins/main/common/services/web_documentation.test.ts +++ b/plugins/main/common/services/web_documentation.test.ts @@ -1,14 +1,22 @@ -import { DOCUMENTATION_WEB_BASE_URL, PLUGIN_VERSION_SHORT } from "../constants"; -import { webDocumentationLink } from "./web_documentation"; +import { DOCUMENTATION_WEB_BASE_URL, PLUGIN_VERSION_SHORT } from '../constants'; +import { webDocumentationLink } from './web_documentation'; test(`Generate a web documentation URL using to the plugin short version`, () => { - expect(webDocumentationLink('user-manual/agent-enrollment/index.html')).toBe(`${DOCUMENTATION_WEB_BASE_URL}/${PLUGIN_VERSION_SHORT}/user-manual/agent-enrollment/index.html`); + expect(webDocumentationLink('user-manual/agent-enrollment/index.html')).toBe( + `${DOCUMENTATION_WEB_BASE_URL}/${PLUGIN_VERSION_SHORT}/user-manual/agent-enrollment/index.html`, + ); }); test(`Generate a web documentation URL to the base URL using to the plugin short version`, () => { - expect(webDocumentationLink('')).toBe(`${DOCUMENTATION_WEB_BASE_URL}/${PLUGIN_VERSION_SHORT}/`); + expect(webDocumentationLink('')).toBe( + `${DOCUMENTATION_WEB_BASE_URL}/${PLUGIN_VERSION_SHORT}/`, + ); }); test(`Generate a web documentation URL using a specific version`, () => { - expect(webDocumentationLink('user-manual/agent-enrollment/index.html', '4.6')).toBe(`${DOCUMENTATION_WEB_BASE_URL}/4.6/user-manual/agent-enrollment/index.html`); + expect( + webDocumentationLink('user-manual/agent-enrollment/index.html', '4.6'), + ).toBe( + `${DOCUMENTATION_WEB_BASE_URL}/4.6/user-manual/agent-enrollment/index.html`, + ); }); diff --git a/plugins/main/package.json b/plugins/main/package.json index 861f321872..43894ca580 100644 --- a/plugins/main/package.json +++ b/plugins/main/package.json @@ -3,7 +3,7 @@ "version": "4.6.0", "revision": "01", "stage": "stable", - "commit": "0e7201ff8", + "commit": "c805cbcd0", "pluginPlatform": { "version": "2.6.0" }, diff --git a/plugins/main/public/components/agents/syscollector/components/syscollector-table.tsx b/plugins/main/public/components/agents/syscollector/components/syscollector-table.tsx index 4df55316fd..e5e5b0056f 100644 --- a/plugins/main/public/components/agents/syscollector/components/syscollector-table.tsx +++ b/plugins/main/public/components/agents/syscollector/components/syscollector-table.tsx @@ -1,18 +1,33 @@ -import React, { useState } from "react"; -import { EuiPanel, EuiFlexGroup, EuiButtonEmpty, EuiFlexItem, EuiText, EuiLoadingSpinner, EuiFieldSearch, EuiHorizontalRule, EuiIcon, EuiBasicTable } from "@elastic/eui"; +import React, { useState } from 'react'; +import { + EuiPanel, + EuiFlexGroup, + EuiButtonEmpty, + EuiFlexItem, + EuiText, + EuiLoadingSpinner, + EuiFieldSearch, + EuiHorizontalRule, + EuiIcon, + EuiBasicTable, +} from '@elastic/eui'; import { useApiRequest } from '../../../common/hooks/useApiRequest'; import { KeyEquivalence } from '../../../../../common/csv-key-equivalence'; import { AppState } from '../../../../react-services/app-state'; - export function SyscollectorTable({ tableParams }) { - const [params, setParams] = useState<{ limit: number, offset: number, select:string, q?: string}>({ + const [params, setParams] = useState<{ + limit: number; + offset: number; + select: string; + q?: string; + }>({ limit: 10, offset: 0, - select: tableParams.columns.map(({id}) => id).join(",") + select: tableParams.columns.map(({ id }) => id).join(','), }); const [pageIndex, setPageIndex] = useState(0); - const [searchBarValue, setSearchBarValue] = useState(""); + const [searchBarValue, setSearchBarValue] = useState(''); const [pageSize, setPageSize] = useState(10); const [sortField, setSortField] = useState(''); const [timerDelaySearch, setTimerDelaySearch] = useState(); @@ -26,17 +41,16 @@ export function SyscollectorTable({ tableParams }) { setPageSize(pageSize); setSortField(sortField); setSortDirection(sortDirection); - const field = (sortField === 'os_name') ? '' : sortField; - const direction = (sortDirection === 'asc') ? '+' : '-'; + const field = sortField === 'os_name' ? '' : sortField; + const direction = sortDirection === 'asc' ? '+' : '-'; const newParams = { ...params, limit: pageSize, offset: Math.floor((pageIndex * pageSize) / params.limit) * params.limit, - ...(!!field ? { sort: `${direction}${field}` } : {}) - } + ...(!!field ? { sort: `${direction}${field}` } : {}), + }; setParams(newParams); - }; const buildColumns = () => { @@ -63,51 +77,70 @@ export function SyscollectorTable({ tableParams }) { sort: { field: sortField, direction: sortDirection, - } + }, }; - const onChange = (e) => { + const onChange = e => { const value = e.target.value; setSearchBarValue(value); timerDelaySearch && clearTimeout(timerDelaySearch); const timeoutId = setTimeout(() => { - const { q, ...rest} = params; + const { q, ...rest } = params; const newParams = { ...rest, - ...(value ? { - q: tableParams.columns.map(({id}) => `${id}~${value}`).join(",") - }: {}) - }; + ...(value + ? { + q: tableParams.columns + .map(({ id }) => `${id}~${value}`) + .join(','), + } + : {}), + }; setParams(newParams); setPageIndex(0); - }, 400) + }, 400); setTimerDelaySearch(timeoutId); - } + }; const getTotal = () => { if (loading) - return <>{'( '}{' )'}; - else - return `(${data.total_affected_items})`; - } + return ( + <> + {'( '} + + {' )'} + + ); + else return `(${data.total_affected_items})`; + }; const downloadCsv = async () => { await AppState.downloadCsv( tableParams.path, tableParams.exportFormatted, - !!params.q ? [{ name: 'q', value: params.q }] : [] - ) - } + !!params.q ? [{ name: 'q', value: params.q }] : [], + ); + }; return ( - + -   {tableParams.title} {tableParams.hasTotal ? getTotal() : ''} + + {' '} + {' '} +  {' '} + + {tableParams.title} {tableParams.hasTotal ? getTotal() : ''} + {' '} + - - {tableParams.searchBar && + + {tableParams.searchBar && ( - } + )} - + Download CSV diff --git a/plugins/main/public/components/common/welcome/agents-info.js b/plugins/main/public/components/common/welcome/agents-info.js index cad892a208..00ab5b8f81 100644 --- a/plugins/main/public/components/common/welcome/agents-info.js +++ b/plugins/main/public/components/common/welcome/agents-info.js @@ -12,18 +12,12 @@ * Find more information about this on the LICENSE file. */ import React, { Component, Fragment } from 'react'; -import { - EuiStat, - EuiFlexItem, - EuiFlexGroup, - EuiBadge -} from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup, EuiBadge } from '@elastic/eui'; import { WzRequest } from '../../../react-services/wz-request'; import { formatUIDate } from '../../../react-services/time-service'; - import WzTextWithTooltipIfTruncated from '../wz-text-with-tooltip-if-truncated'; import { WzStat } from '../../wz-stat'; -import { GroupTruncate } from '../util/agent-group-truncate' +import { GroupTruncate } from '../util/agent-group-truncate'; import { AgentStatus } from '../../agents/agent_status'; import { compressIPv6 } from '../../../services/ipv6-services'; @@ -34,11 +28,11 @@ export class AgentInfo extends Component { this.state = {}; } - async componentDidMount() { const managerVersion = await WzRequest.apiReq('GET', '/', {}); this.setState({ - managerVersion: (((managerVersion || {}).data || {}).data || {}).api_version || {} + managerVersion: + (((managerVersion || {}).data || {}).data || {}).api_version || {}, }); } @@ -54,13 +48,14 @@ export class AgentInfo extends Component { icon = 'apple'; } - return + return ( + + ); } - addTextPlatformRender(agent, style) { const checkField = field => { return field !== undefined ? field : '-'; @@ -74,30 +69,32 @@ export class AgentInfo extends Component { const osName = os_name === '- -' ? '-' : os_name; return ( - - {this.getPlatformIcon(this.props.agent)} - {' '}{osName} + + {this.getPlatformIcon(this.props.agent)} {osName} - ) + ); } addGroupsRender(agent) { // this was rendered with a EuiHealth, but EuiHealth has a div wrapper, and this section is rendered within a

tag.

tags aren't allowed within

tags. return ( - { - agent.group.map((group, key) => ( - this.props.goGroups(this.props.agent, key)}> - {group} - - )) - } + {agent.group.map((group, key) => ( + this.props.goGroups(this.props.agent, key)} + > + {group} + + ))} - ) + ); } buildStats(items) { @@ -106,7 +103,10 @@ export class AgentInfo extends Component { }; const stats = items.map(item => { // We add tooltipProps, so that the ClusterNode and Operating System fields occupy their space and do not exceed this, overlapping with the one on the right - const tooltipProps = item.description === 'Cluster node' ? { anchorClassName: 'wz-width-100' } : {}; + const tooltipProps = + item.description === 'Cluster node' + ? { anchorClassName: 'wz-width-100' } + : {}; return ( + {...this.props} + /> ) : item.description === 'Operating system' ? ( this.addTextPlatformRender(this.props.agent, item.style) ) : item.description === 'Status' ? ( - + ) : ( - + {checkField(item.title)} ) } description={item.description} - titleSize="xs" + titleSize='xs' /> ); @@ -146,25 +154,64 @@ export class AgentInfo extends Component { if (this.props.isCondensed) { arrayStats = [ { title: agent.id, description: 'ID', style: { maxWidth: 100 } }, - { title: agent.status, description: 'Status', style: { maxWidth: 150 } }, - { title: agent.version, description: 'Version', style: { maxWidth: 150 } }, + { + title: agent.status, + description: 'Status', + style: { maxWidth: 150 }, + }, + { + title: agent.version, + description: 'Version', + style: { maxWidth: 150 }, + }, { title: agent.name, description: 'Operating system', - style: { minWidth: 200, maxWidth: 200 } - } + style: { minWidth: 200, maxWidth: 200 }, + }, ]; } else { arrayStats = [ { title: agent.id, description: 'ID', style: { minWidth: 30 } }, - { title: agent.status, description: 'Status', style: { minWidth: 130 } }, - { title: compressIPv6(agent.ip), description: 'IP address', style: { minWidth: 80 } }, - { title: agent.version, description: 'Version', style: { minWidth: 100 } }, + { + title: agent.status, + description: 'Status', + style: { minWidth: 100 }, + }, + { + title: agent.ip, + description: 'IP address', + style: { minwidth: 150 }, + }, + { + title: agent.version, + description: 'Version', + style: { minWidth: 100 }, + }, { title: agent.group, description: 'Groups', style: { minWidth: 150 } }, - { title: agent.name, description: 'Operating system', style: { minWidth: 150 } }, - { title: agent.node_name && agent.node_name !== 'unknown' ? agent.node_name : '-', description: 'Cluster node', style: { minWidth: 120 } }, - { title: formatUIDate(agent.dateAdd), description: 'Registration date', style: { minWidth: 180 } }, - { title: formatUIDate(agent.lastKeepAlive), description: 'Last keep alive', style: { minWidth: 180 } }, + { + title: agent.name, + description: 'Operating system', + style: { minWidth: 150 }, + }, + { + title: + agent.node_name && agent.node_name !== 'unknown' + ? agent.node_name + : '-', + description: 'Cluster node', + style: { minWidth: 120 }, + }, + { + title: formatUIDate(agent.dateAdd), + description: 'Registration date', + style: { minWidth: 180 }, + }, + { + title: formatUIDate(agent.lastKeepAlive), + description: 'Last keep alive', + style: { minWidth: 180 }, + }, ]; } @@ -172,7 +219,11 @@ export class AgentInfo extends Component { return ( - + {stats} diff --git a/plugins/main/public/components/common/welcome/agents-welcome.js b/plugins/main/public/components/common/welcome/agents-welcome.js index 4d0d3386de..20c1ded43c 100644 --- a/plugins/main/public/components/common/welcome/agents-welcome.js +++ b/plugins/main/public/components/common/welcome/agents-welcome.js @@ -31,9 +31,13 @@ import { EuiButtonIcon, EuiEmptyPrompt, EuiPageBody, - EuiLink } from '@elastic/eui'; -import { FimEventsTable, ScaScan, MitreTopTactics, RequirementVis } from './components'; +import { + FimEventsTable, + ScaScan, + MitreTopTactics, + RequirementVis, +} from './components'; import { AgentInfo } from './agents-info'; import { WAZUH_MODULES } from '../../../../common/wazuh-modules'; import store from '../../../redux/store'; @@ -59,495 +63,598 @@ import { webDocumentationLink } from '../../../../common/services/web_documentat export const AgentsWelcome = compose( withReduxProvider, - withErrorBoundary)( -class AgentsWelcome extends Component { - _isMount = false; - constructor(props) { - super(props); - - this.offset = 275; + withErrorBoundary, +)( + class AgentsWelcome extends Component { + _isMount = false; + constructor(props) { + super(props); - this.state = { - extensions: this.props.extensions, - lastScans: [], - isLoading: true, - sortField: 'start_scan', - sortDirection: 'desc', - actionAgents: true, // Hide actions agents - selectedRequirement: 'pci', - menuAgent: {}, - maxModules: 6, - widthWindow: window.innerWidth - }; - } + this.offset = 275; - updateWidth = () => { + this.state = { + extensions: this.props.extensions, + lastScans: [], + isLoading: true, + sortField: 'start_scan', + sortDirection: 'desc', + actionAgents: true, // Hide actions agents + selectedRequirement: 'pci', + menuAgent: {}, + maxModules: 6, + widthWindow: window.innerWidth, + }; + } - let menuSize = (window.innerWidth - this.offset); - let maxModules = 6; - if (menuSize > 1250) { - maxModules = 6; - } else { - if (menuSize > 1100) { - maxModules = 5; + updateWidth = () => { + let menuSize = window.innerWidth - this.offset; + let maxModules = 6; + if (menuSize > 1250) { + maxModules = 6; } else { - if (menuSize > 900) { - maxModules = 4; + if (menuSize > 1100) { + maxModules = 5; } else { - maxModules = 3; - if (menuSize < 750) { - maxModules = null; + if (menuSize > 900) { + maxModules = 4; + } else { + maxModules = 3; + if (menuSize < 750) { + maxModules = null; + } } } } - } - - this.setState({ maxModules: maxModules, widthWindow: window.innerWidth }); - }; - - setGlobalBreadcrumb() { - const breadcrumb = [ - { text: '' }, - { - text: 'Agents', - href: "#/agents-preview" - }, - { - text: `${this.props.agent.name}`, - className: 'wz-global-breadcrumb-btn euiBreadcrumb--truncate', - truncate: false, - } - ]; - store.dispatch(updateGlobalBreadcrumb(breadcrumb)); - } + this.setState({ maxModules: maxModules, widthWindow: window.innerWidth }); + }; - async componentDidMount() { - this._isMount = true; - store.dispatch(updateCurrentAgentData(this.props.agent)); - this.updateMenuAgents(); - this.updateWidth(); - this.setGlobalBreadcrumb(); - const tabVisualizations = new TabVisualizations(); - tabVisualizations.removeAll(); - tabVisualizations.setTab('welcome'); - tabVisualizations.assign({ - welcome: 8 - }); - const filterHandler = new FilterHandler(AppState.getCurrentPattern()); - const $injector = getAngularModule().$injector; - this.router = $injector.get('$route'); - window.addEventListener('resize', this.updateWidth); //eslint-disable-line - await VisFactoryHandler.buildAgentsVisualizations( - filterHandler, - 'welcome', - null, - this.props.agent.id - ); - } + setGlobalBreadcrumb() { + const breadcrumb = [ + { text: '' }, + { + text: 'Agents', + href: '#/agents-preview', + }, + { + text: `${this.props.agent.name}`, + className: 'wz-global-breadcrumb-btn euiBreadcrumb--truncate', + truncate: false, + }, + ]; + store.dispatch(updateGlobalBreadcrumb(breadcrumb)); + } - updateMenuAgents() { - const defaultMenuAgents = { - general: { - id: 'general', - text: 'Security events', - isPin: true, - }, - fim: { - id: 'fim', - text: 'Integrity monitoring', - isPin: true, - }, - sca: { - id: 'sca', - text: 'SCA', - isPin: true, - }, - audit: { - id: 'audit', - text: 'System Auditing', - isPin: true, - }, - vuls: { - id: 'vuls', - text: 'Vulnerabilities', - isPin: true, - }, - mitre: { - id: 'mitre', - text: 'MITRE ATT&CK', - isPin: true, - }, + async componentDidMount() { + this._isMount = true; + store.dispatch(updateCurrentAgentData(this.props.agent)); + this.updateMenuAgents(); + this.updateWidth(); + this.setGlobalBreadcrumb(); + const tabVisualizations = new TabVisualizations(); + tabVisualizations.removeAll(); + tabVisualizations.setTab('welcome'); + tabVisualizations.assign({ + welcome: 8, + }); + const filterHandler = new FilterHandler(AppState.getCurrentPattern()); + const $injector = getAngularModule().$injector; + this.router = $injector.get('$route'); + window.addEventListener('resize', this.updateWidth); //eslint-disable-line + await VisFactoryHandler.buildAgentsVisualizations( + filterHandler, + 'welcome', + null, + this.props.agent.id, + ); } - let menuAgent = JSON.parse(window.localStorage.getItem('menuAgent')); + updateMenuAgents() { + const defaultMenuAgents = { + general: { + id: 'general', + text: 'Security events', + isPin: true, + }, + fim: { + id: 'fim', + text: 'Integrity monitoring', + isPin: true, + }, + sca: { + id: 'sca', + text: 'SCA', + isPin: true, + }, + audit: { + id: 'audit', + text: 'System Auditing', + isPin: true, + }, + vuls: { + id: 'vuls', + text: 'Vulnerabilities', + isPin: true, + }, + mitre: { + id: 'mitre', + text: 'MITRE ATT&CK', + isPin: true, + }, + }; - // Check if pinned modules to agent menu are enabled in Settings/Modules, if not then modify localstorage removing the disabled modules - if (menuAgent) { - const needUpdateMenuAgent = Object.keys(menuAgent).map(moduleName => menuAgent[moduleName]).reduce((accum, item) => { - if (typeof this.props.extensions[item.id] !== 'undefined' && this.props.extensions[item.id] === false) { - delete menuAgent[item.id]; - accum = true; + let menuAgent = JSON.parse(window.localStorage.getItem('menuAgent')); + + // Check if pinned modules to agent menu are enabled in Settings/Modules, if not then modify localstorage removing the disabled modules + if (menuAgent) { + const needUpdateMenuAgent = Object.keys(menuAgent) + .map(moduleName => menuAgent[moduleName]) + .reduce((accum, item) => { + if ( + typeof this.props.extensions[item.id] !== 'undefined' && + this.props.extensions[item.id] === false + ) { + delete menuAgent[item.id]; + accum = true; + } + return accum; + }, false); + if (needUpdateMenuAgent) { + // Update the pinned modules matching to enabled modules in Setings/Modules + window.localStorage.setItem('menuAgent', JSON.stringify(menuAgent)); } - return accum; - }, false); - if (needUpdateMenuAgent) { - // Update the pinned modules matching to enabled modules in Setings/Modules - window.localStorage.setItem('menuAgent', JSON.stringify(menuAgent)) + } else { + menuAgent = defaultMenuAgents; + window.localStorage.setItem( + 'menuAgent', + JSON.stringify(defaultMenuAgents), + ); } - } else { - menuAgent = defaultMenuAgents; - window.localStorage.setItem('menuAgent', JSON.stringify(defaultMenuAgents)); + this.setState({ menuAgent: menuAgent }); } - this.setState({ menuAgent: menuAgent }); - } - renderModules() { - const menuAgent = [...Object.keys(this.state.menuAgent).map((item) => { return this.state.menuAgent[item] })]; + renderModules() { + const menuAgent = [ + ...Object.keys(this.state.menuAgent).map(item => { + return this.state.menuAgent[item]; + }), + ]; - return ( - - { - menuAgent.map((menuAgent, i) => { - if (i < this.state.maxModules && hasAgentSupportModule(this.props.agent, menuAgent.id)) { + return ( + + {menuAgent.map((menuAgent, i) => { + if ( + i < this.state.maxModules && + hasAgentSupportModule(this.props.agent, menuAgent.id) + ) { return ( - + { - window.location.href = `#/overview/?tab=${menuAgent.id}&tabView=${menuAgent.text === 'Security configuration assessment' ? 'inventory' : 'panels'}`; + window.location.href = `#/overview/?tab=${ + menuAgent.id + }&tabView=${ + menuAgent.text === 'Security configuration assessment' + ? 'inventory' + : 'panels' + }`; this.router.reload(); - }} style={{ cursor: 'pointer' }}> - {menuAgent.text !== 'Security configuration assessment' ? menuAgent.text : 'SCA'}  + }} + style={{ cursor: 'pointer' }} + > + + {menuAgent.text !== 'Security configuration assessment' + ? menuAgent.text + : 'SCA'} +   + - ) - } - } - )} - - this.setState({ switchModule: !this.state.switchModule })}> - More... - + ); } - isOpen={this.state.switchModule} - closePopover={() => this.setState({ switchModule: false })} - repositionOnScroll={false} - anchorPosition="downCenter"> -

- -
- this.updateMenuAgents()} - closePopover={() => { - this.setState({ switchModule: false }) - } - } - switchTab={(module) => this.props.switchTab(module)}> -
-
-
- - - - ) - } - - renderTitle() { - - const notNeedStatus = true; - - return ( - - - - - - -

- {this.state.widthWindow >= 768?( - - {this.props.agent.name} - - ): - ( - - {this.props.agent.name} - - ) - } -

-
-
-
- { - (this.state.maxModules !== null && - this.renderModules()) || - - this.setState({ switchModule: !this.state.switchModule })}> - Modules - + })} + + + this.setState({ switchModule: !this.state.switchModule }) } - isOpen={this.state.switchModule} - closePopover={() => this.setState({ switchModule: false })} - repositionOnScroll={false} - anchorPosition="downCenter"> -
- -
- this.updateMenuAgents()} - closePopover={() => { - this.setState({ switchModule: false }) - } - } - switchTab={(module) => this.props.switchTab(module)}> -
-
-
-
-
+ > + More... + } - + isOpen={this.state.switchModule} + closePopover={() => this.setState({ switchModule: false })} + repositionOnScroll={false} + anchorPosition='downCenter' + > +
+ +
+ this.updateMenuAgents()} + closePopover={() => { + this.setState({ switchModule: false }); + }} + switchTab={module => this.props.switchTab(module)} + > +
+
+
+
+
+ + ); + } + + renderTitle() { + return ( + + + + {(this.state.maxModules !== null && this.renderModules()) || ( + + + this.setState({ + switchModule: !this.state.switchModule, + }) + } + > + Modules + + } + isOpen={this.state.switchModule} + closePopover={() => this.setState({ switchModule: false })} + repositionOnScroll={false} + anchorPosition='downCenter' + > +
+ +
+ this.updateMenuAgents()} + closePopover={() => { + this.setState({ switchModule: false }); + }} + switchTab={module => this.props.switchTab(module)} + > +
+
+
+
+
+ )} + this.props.switchTab('syscollector', notNeedStatus)}> + iconType='inspect' + onClick={() => + this.props.switchTab('syscollector', notNeedStatus) + } + > Inventory data this.props.switchTab('stats', notNeedStatus)}> + iconType='stats' + onClick={() => this.props.switchTab('stats', notNeedStatus)} + > Stats this.props.switchTab('configuration', notNeedStatus)}> + iconType='gear' + onClick={() => + this.props.switchTab('configuration', notNeedStatus) + } + > Configuration
- ); - } + ); + } + onTimeChange = datePicker => { + const { start: from, end: to } = datePicker; + this.setState({ datePicker: { from, to } }); + }; - onTimeChange = (datePicker) => { - const { start: from, end: to } = datePicker; - this.setState({ datePicker: { from, to } }); - } + getOptions() { + return [ + { value: 'pci', text: 'PCI DSS' }, + { value: 'gdpr', text: 'GDPR' }, + { value: 'nist', text: 'NIST 800-53' }, + { value: 'hipaa', text: 'HIPAA' }, + { value: 'gpg13', text: 'GPG13' }, + { value: 'tsc', text: 'TSC' }, + ]; + } + + setSelectValue(e) { + this.setState({ selectedRequirement: e.target.value }); + } + + getRequirementVis() { + if (this.state.selectedRequirement === 'pci') { + return 'Wazuh-App-Agents-Welcome-Top-PCI'; + } + if (this.state.selectedRequirement === 'gdpr') { + return 'Wazuh-App-Agents-Welcome-Top-GDPR'; + } + if (this.state.selectedRequirement === 'hipaa') { + return 'Wazuh-App-Agents-Welcome-Top-HIPAA'; + } + if (this.state.selectedRequirement === 'nist') { + return 'Wazuh-App-Agents-Welcome-Top-NIST-800-53'; + } + if (this.state.selectedRequirement === 'gpg13') { + return 'Wazuh-App-Agents-Welcome-Top-GPG-13'; + } + if (this.state.selectedRequirement === 'tsc') { + return 'Wazuh-App-Agents-Welcome-Top-TSC'; + } + return 'Wazuh-App-Agents-Welcome-Top-PCI'; + } - renderMitrePanel() { - return ( - - + renderMitrePanel() { + return ( + + + + + +

+ +

MITRE

+
+

+
+ + + { + window.location.href = `#/overview?tab=mitre`; + this.router.reload(); + }} + aria-label='Open MITRE' + /> + + +
+
+ + + + + + +
+
+ ); + } + + renderCompliancePanel() { + return ( + + ); + } + + renderEventCountVisualization() { + return ( + -

-

MITRE

+

+ +

Events count evolution

+

- - - { - window.location.href = `#/overview?tab=mitre`; - this.router.reload(); - } - } - aria-label="Open MITRE" /> - -
+ +
+ + + +
+
+ +
- - - - - -
-
- - ) - } - - renderCompliancePanel() { - return ( - - ) - } + ); + } - renderEventCountVisualization() { - return ( - - - - -

-

Events count evolution

-

-
-
- -
- - - -
-
- -
-
-
- ) - } + renderSCALastScan() { + return ( + + + + ); + } - renderSCALastScan() { - return ( - - - - ) - } + render() { + const title = this.renderTitle(); - render() { - const title = this.renderTitle(); + if (this.props.agent.status === API_NAME_AGENT_STATUS.NEVER_CONNECTED) { + return ( + Agent has never connected.} + body={ + +

+ The agent has been registered but has not yet connected to the + manager. +

+ + Checking connection with the Wazuh server + +
+ } + actions={ + + Back + + } + /> + ); + } - if (this.props.agent.status === API_NAME_AGENT_STATUS.NEVER_CONNECTED) { return ( - Agent has never connected.} - body={ - -

- The agent has been registered but has not yet connected to the manager. -

- - Checking connection with the Wazuh server - -
- } - actions={ - - Back - - } - />) - } - - return ( -
-
-
- {title} +
+
+
{title}
-
-
- - - -
-
- - - +
+ + +
+
+ + + +
-
- - - {/* DatePicker */} - { }} /> - - - {this.state.widthWindow < 1150 && ( - - - - {this.renderMitrePanel()} - - {this.renderCompliancePanel()} - - - - - - - - {/* Events count evolution */} - {this.renderEventCountVisualization()} - - - - - - {this.renderSCALastScan()} - - - - ) || ( + + + + {' '} + {/* DatePicker */} + {}} /> + + + {(this.state.widthWindow < 1150 && ( + + + + {this.renderMitrePanel()} + + {this.renderCompliancePanel()} + + + + + + + + + {' '} + {/* Events count evolution */} + {this.renderEventCountVisualization()} + + + + + {this.renderSCALastScan()} + + + )) || ( - + {this.renderMitrePanel()} {this.renderCompliancePanel()} - + - + - {/* Events count evolution */} + + {' '} + {/* Events count evolution */} {this.renderEventCountVisualization()} - - {this.renderSCALastScan()} - + {this.renderSCALastScan()} )} - - - + + +
-
- ); - } -}) + ); + } + }, +); diff --git a/plugins/main/public/components/management/cluster/node-list.tsx b/plugins/main/public/components/management/cluster/node-list.tsx index 4a5288db33..580c747d14 100644 --- a/plugins/main/public/components/management/cluster/node-list.tsx +++ b/plugins/main/public/components/management/cluster/node-list.tsx @@ -1,113 +1,128 @@ -import React, { Component } from 'react'; -import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiButtonIcon, EuiTitle, EuiInMemoryTable, EuiFieldSearch } from '@elastic/eui'; +import React, { Component, Fragment } from 'react'; +import { + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, + EuiButtonIcon, + EuiTitle, + EuiInMemoryTable, + EuiFieldSearch, +} from '@elastic/eui'; import { WzRequest } from '../../../react-services/wz-request'; import { withErrorBoundary } from '../../common/hocs'; -export const NodeList = withErrorBoundary (class NodeList extends Component { +export const NodeList = withErrorBoundary( + class NodeList extends Component { constructor(props) { - super(props); - this.state = { - nodes: [], - loading: false - }; + super(props); + this.state = { + nodes: [], + loading: false, + }; } async componentDidMount() { - this.search(); + this.search(); } async search(searchTerm = false) { - let params = {}; - if (searchTerm) { - params.search = searchTerm; - } - this.setState({ loading: true }); - try{ - const request = await WzRequest.apiReq('GET', '/cluster/nodes', {params}); - this.setState({ nodes: (((request || {}).data || {}).data || {}).affected_items || [], loading: false }); - }catch(error){ - this.setState({ loading: false }); - } + let params = {}; + if (searchTerm) { + params.search = searchTerm; + } + this.setState({ loading: true }); + try { + const request = await WzRequest.apiReq('GET', '/cluster/nodes', { + params, + }); + this.setState({ + nodes: (((request || {}).data || {}).data || {}).affected_items || [], + loading: false, + }); + } catch (error) { + this.setState({ loading: false }); + } } - render() { - const columns = [ - { - field: 'name', - name: 'Name', - sortable: true, - truncateText: true, - }, - { - field: 'version', - name: 'Version', - sortable: true, - }, - { - field: 'ip', - name: 'IP address', - sortable: true, - }, - { - field: 'type', - name: 'Type', - sortable: true, - } - ]; + const columns = [ + { + field: 'name', + name: 'Name', + sortable: true, + }, + { + field: 'version', + name: 'Version', + sortable: true, + }, + { + field: 'ip', + name: 'IP address', + sortable: true, + }, + { + field: 'type', + name: 'Type', + sortable: true, + }, + ]; - const sorting = { - sort: { - field: 'name', - direction: 'asc', - }, - }; - return ( - - - - - - - this.props.goBack()} - /> - - - - -

Nodes

-
-
-
-
-
- - - this.search(e)} - isClearable={true} - fullWidth={true} - aria-label="Filter" - /> - - - - - - - -
- ); + const sorting = { + sort: { + field: 'name', + direction: 'asc', + }, + }; + return ( + + + + + + + this.props.goBack()} + /> + + + + +

Nodes

+
+
+
+
+
+ + + this.search(e)} + isClearable={true} + fullWidth={true} + aria-label='Filter' + /> + + + + + + + +
+ ); } -}); + }, +); diff --git a/plugins/main/public/controllers/agent/agents-preview.js b/plugins/main/public/controllers/agent/agents-preview.js index bee6e8d47f..6fd90cce6e 100644 --- a/plugins/main/public/controllers/agent/agents-preview.js +++ b/plugins/main/public/controllers/agent/agents-preview.js @@ -35,7 +35,15 @@ export class AgentsPreviewController { * @param {Object} errorHandler * @param {Object} csvReq */ - constructor($scope, $location, $route, errorHandler, csvReq, commonData, $window) { + constructor( + $scope, + $location, + $route, + errorHandler, + csvReq, + commonData, + $window, + ) { this.$scope = $scope; this.genericReq = GenericRequest; this.$location = $location; @@ -58,11 +66,15 @@ export class AgentsPreviewController { this.api = JSON.parse(AppState.getCurrentAPI()).id; const loc = this.$location.search(); if ((loc || {}).agent && (loc || {}).agent !== '000') { - this.commonData.setTimefilter(getDataPlugin().timefilter.timefilter.getTime()); + this.commonData.setTimefilter( + getDataPlugin().timefilter.timefilter.getTime(), + ); return this.showAgent({ id: loc.agent }); } - this.isClusterEnabled = AppState.getClusterInfo() && AppState.getClusterInfo().status === 'enabled'; + this.isClusterEnabled = + AppState.getClusterInfo() && + AppState.getClusterInfo().status === 'enabled'; this.loading = true; this.osPlatforms = []; this.versions = []; @@ -83,7 +95,7 @@ export class AgentsPreviewController { this.$location.search('tab', this.submenuNavItem); }); - this.$scope.$on('wazuhFetched', (evt) => { + this.$scope.$on('wazuhFetched', evt => { evt.stopPropagation(); }); this.registerAgentsProps = { @@ -91,14 +103,14 @@ export class AgentsPreviewController { hasAgents: () => this.hasAgents, reload: () => this.$route.reload(), getWazuhVersion: () => this.getWazuhVersion(), - getCurrentApiAddress: () => this.getCurrentApiAddress() + getCurrentApiAddress: () => this.getCurrentApiAddress(), }; this.hasAgents = true; this.init = false; const instance = new DataFactory(WzRequest.apiReq, '/agents', false, false); //Props this.tableAgentsProps = { - updateSummary: (summary) => { + updateSummary: summary => { this.summary = summary; if (this.summary.total === 0) { this.addNewAgent(true); @@ -116,7 +128,7 @@ export class AgentsPreviewController { this.downloadCsv(filters); this.$scope.$applyAsync(); }, - showAgent: (agent) => { + showAgent: agent => { this.showAgent(agent); this.$scope.$applyAsync(); }, @@ -124,10 +136,17 @@ export class AgentsPreviewController { return await this.getMostActive(); }, clickAction: (item, openAction = false) => { - clickAction(item, openAction, instance, this.shareAgent, this.$location, this.$scope); + clickAction( + item, + openAction, + instance, + this.shareAgent, + this.$location, + this.$scope, + ); this.$scope.$applyAsync(); }, - formatUIDate: (date) => formatUIDate(date), + formatUIDate: date => formatUIDate(date), summary: this.summary, }; //Load @@ -186,14 +205,18 @@ export class AgentsPreviewController { 'GET', `/elastic/top/${this.firstUrlParam}/${this.secondUrlParam}/agent.name/${ this.pattern - }?agentsList=${store.getState().appStateReducers.allowedAgents.toString()}` + }?agentsList=${store + .getState() + .appStateReducers.allowedAgents.toString()}`, ); this.mostActiveAgent.name = data.data.data; const info = await this.genericReq.request( 'GET', `/elastic/top/${this.firstUrlParam}/${this.secondUrlParam}/agent.id/${ this.pattern - }?agentsList=${store.getState().appStateReducers.allowedAgents.toString()}` + }?agentsList=${store + .getState() + .appStateReducers.allowedAgents.toString()}`, ); if (info.data.data === '' && this.mostActiveAgent.name !== '') { this.mostActiveAgent.id = '000'; @@ -226,9 +249,12 @@ export class AgentsPreviewController { try { this.errorInit = false; const clusterInfo = AppState.getClusterInfo(); - this.firstUrlParam = clusterInfo.status === 'enabled' ? 'cluster' : 'manager'; + this.firstUrlParam = + clusterInfo.status === 'enabled' ? 'cluster' : 'manager'; this.secondUrlParam = clusterInfo[this.firstUrlParam]; - this.pattern = (await getDataPlugin().indexPatterns.get(AppState.getCurrentPattern())).title; + this.pattern = ( + await getDataPlugin().indexPatterns.get(AppState.getCurrentPattern()) + ).title; } catch (error) { const options = { context: `${AgentsPreviewController.name}.load`, @@ -251,12 +277,6 @@ export class AgentsPreviewController { this.addingNewAgent = flag; } - openRegistrationDocs() { - this.$window.open(webDocumentationLink(user-manual/registering/index.html), - '_blank' - ); - } - /** * Returns the current API address */ @@ -264,7 +284,7 @@ export class AgentsPreviewController { try { const result = await this.genericReq.request('GET', '/hosts/apis'); const entries = result.data || []; - const host = entries.filter((e) => { + const host = entries.filter(e => { return e.id == this.api; }); const url = host[0].url; @@ -278,7 +298,9 @@ export class AgentsPreviewController { error: { error: error, message: error.message || error, - title: `Could not get the Wazuh API address: ${error.message || error}`, + title: `Could not get the Wazuh API address: ${ + error.message || error + }`, }, }; getErrorOrchestrator().handleError(options); diff --git a/plugins/main/public/controllers/agent/components/agents-preview.js b/plugins/main/public/controllers/agent/components/agents-preview.js index 1bfc59b37f..31ed799603 100644 --- a/plugins/main/public/controllers/agent/components/agents-preview.js +++ b/plugins/main/public/controllers/agent/components/agents-preview.js @@ -42,11 +42,17 @@ import { formatUIDate } from '../../../../public/react-services/time-service'; import { compose } from 'redux'; import { withErrorBoundary } from '../../../components/common/hocs'; import './agents-preview.scss'; -import { UI_LOGGER_LEVELS, UI_ORDER_AGENT_STATUS } from '../../../../common/constants'; +import { + UI_LOGGER_LEVELS, + UI_ORDER_AGENT_STATUS, +} from '../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../react-services/common-services'; import { VisualizationBasic } from '../../../components/common/charts/visualizations/basic'; -import { agentStatusColorByAgentStatus, agentStatusLabelByAgentStatus } from '../../../../common/services/wz_agent_status'; +import { + agentStatusColorByAgentStatus, + agentStatusLabelByAgentStatus, +} from '../../../../common/services/wz_agent_status'; export const AgentsPreview = compose( withErrorBoundary, @@ -57,7 +63,7 @@ export const AgentsPreview = compose( { action: 'agent:read', resource: 'agent:id:*' }, { action: 'agent:read', resource: 'agent:group:*' }, ], - ]) + ]), )( class AgentsPreview extends Component { _isMount = false; @@ -68,7 +74,13 @@ export const AgentsPreview = compose( loadingSummary: false, showAgentsEvolutionVisualization: true, agentTableFilters: [], - agentStatusSummary: { active: '-', disconnected: '-', total: '-', pending: '-', never_connected: '-' }, + agentStatusSummary: { + active: '-', + disconnected: '-', + total: '-', + pending: '-', + never_connected: '-', + }, agentConfiguration: {}, agentsActiveCoverage: 0, }; @@ -76,7 +88,7 @@ export const AgentsPreview = compose( this.agentStatus = UI_ORDER_AGENT_STATUS.map(agentStatus => ({ status: agentStatus, label: agentStatusLabelByAgentStatus(agentStatus), - color: agentStatusColorByAgentStatus(agentStatus) + color: agentStatusColorByAgentStatus(agentStatus), })); } @@ -84,9 +96,10 @@ export const AgentsPreview = compose( this._isMount = true; this.fetchAgentStatusDetailsData(); if (this.wazuhConfig.getConfig()['wazuh.monitoring.enabled']) { - this._isMount && this.setState({ - showAgentsEvolutionVisualization: true - }); + this._isMount && + this.setState({ + showAgentsEvolutionVisualization: true, + }); const tabVisualizations = new TabVisualizations(); tabVisualizations.removeAll(); tabVisualizations.setTab('general'); @@ -94,7 +107,11 @@ export const AgentsPreview = compose( general: 1, }); const filterHandler = new FilterHandler(AppState.getCurrentPattern()); - await VisFactoryHandler.buildOverviewVisualizations(filterHandler, 'general', null); + await VisFactoryHandler.buildOverviewVisualizations( + filterHandler, + 'general', + null, + ); } } @@ -102,7 +119,6 @@ export const AgentsPreview = compose( this._isMount = false; } - groupBy = function (arr) { return arr.reduce(function (prev, item) { if (item in prev) prev[item]++; @@ -112,30 +128,54 @@ export const AgentsPreview = compose( }; async fetchSummaryStatus() { this.setState({ loadingSummary: true }); - const {data: {data: { connection: agentStatusSummary, configuration: agentConfiguration }}} = await WzRequest.apiReq('GET', '/agents/summary/status', {}); + const { + data: { + data: { + connection: agentStatusSummary, + configuration: agentConfiguration, + }, + }, + } = await WzRequest.apiReq('GET', '/agents/summary/status', {}); this.props.tableProps.updateSummary(agentStatusSummary); + + const agentsActiveCoverage = ( + (agentStatusSummary.active / agentStatusSummary.total) * + 100 + ).toFixed(2); + this.setState({ loadingSummary: false, agentStatusSummary, agentConfiguration, - agentsActiveCoverage: ((agentStatusSummary.active / agentStatusSummary.total) * 100).toFixed(2), + /* Calculate the agents active coverage. + Ensure the calculated value is not a NaN, otherwise set a 0. + */ + agentsActiveCoverage: isNaN(agentsActiveCoverage) + ? 0 + : agentsActiveCoverage, }); } async fetchAgents() { this.setState({ loadingAgents: true }); - const { data: { data: { affected_items: [lastRegisteredAgent] } } } = await WzRequest.apiReq('GET', '/agents', { + const { + data: { + data: { + affected_items: [lastRegisteredAgent], + }, + }, + } = await WzRequest.apiReq('GET', '/agents', { params: { limit: 1, sort: '-dateAdd', q: 'id!=000' }, }); const agentMostActive = await this.props.tableProps.getMostActive(); this.setState({ loadingAgents: false, lastRegisteredAgent, - agentMostActive + agentMostActive, }); } - async fetchAgentStatusDetailsData(){ + async fetchAgentStatusDetailsData() { try { this.fetchSummaryStatus(); this.fetchAgents(); @@ -170,37 +210,44 @@ export const AgentsPreview = compose( agentTableFilters: [{ field: 'q', value: `status=${status}` }], }); } - onRenderComplete(){ + onRenderComplete() { this.setState({ - evolutionRenderComplete: true - }) + evolutionRenderComplete: true, + }); } render() { const evolutionIsReady = this.props.resultState !== 'loading'; return ( - + - - { - ( + + { <> - - + + - + ({ - label, - value: this.state.agentStatusSummary[status] || 0, - color, - onClick: () => this.filterAgentByStatus(status) - }))} + data={this.agentStatus.map( + ({ status, label, color }) => ({ + label, + value: + this.state.agentStatusSummary[status] || 0, + color, + onClick: () => this.filterAgentByStatus(status), + }), + )} noDataTitle='No results' noDataMessage='No results were found.' /> @@ -208,24 +255,32 @@ export const AgentsPreview = compose( - - - - {this.agentStatus.map(({status, label, color}) => ( + + + + {this.agentStatus.map(({ status, label, color }) => ( - this.filterAgentByStatus(status)} style={{cursor: 'pointer'}}> + + + this.filterAgentByStatus(status) + } + style={{ cursor: 'pointer' }} + > {this.state.agentStatusSummary[status]} } - titleSize="s" + titleSize='s' description={label} titleColor={color} - className="white-space-nowrap" + className='white-space-nowrap' /> ))} @@ -234,43 +289,63 @@ export const AgentsPreview = compose( isLoading={this.state.loadingSummary} title={`${this.state.agentsActiveCoverage}%`} titleSize='s' - description="Agents coverage" - className="white-space-nowrap" - /> + description='Agents coverage' + className='white-space-nowrap' + /> - - - - this.showAgent(this.state.lastRegisteredAgent)}> - {this.state.lastRegisteredAgent?.name || '-'} - - - } - titleSize="s" - description="Last registered agent" - titleColor="primary" - /> - + + + + + this.showAgent( + this.state.lastRegisteredAgent, + ) + } + > + {this.state.lastRegisteredAgent?.name || '-'} + + + } + titleSize='s' + description='Last registered agent' + titleColor='primary' + /> + { - + - this.showAgent(this.state.agentMostActive)}> + + + this.showAgent(this.state.agentMostActive) + } + > {this.state.agentMostActive?.name || '-'} } - titleSize="s" - description="Most active agent" - titleColor="primary" + titleSize='s' + description='Most active agent' + titleColor='primary' /> } @@ -278,41 +353,41 @@ export const AgentsPreview = compose( - )} - + - - - + +
- - - + + + +
+ {!evolutionIsReady && ( +
+
- {!evolutionIsReady && ( -
- -
- )} -
-
-
-
+ )} +
+
+ +
- + formatUIDate(date)} + formatUIDate={date => formatUIDate(date)} reload={() => this.fetchAgentStatusDetailsData()} /> @@ -329,7 +403,7 @@ export const AgentsPreview = compose(
); } - } + }, ); AgentsTable.propTypes = { diff --git a/plugins/main/public/controllers/agent/components/agents-table.js b/plugins/main/public/controllers/agent/components/agents-table.js index ed1ab8dd7f..212bd69505 100644 --- a/plugins/main/public/controllers/agent/components/agents-table.js +++ b/plugins/main/public/controllers/agent/components/agents-table.js @@ -11,7 +11,7 @@ * Find more information about this on the LICENSE file. */ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiBasicTable, @@ -31,12 +31,20 @@ import { import { getToasts } from '../../../kibana-services'; import { AppNavigate } from '../../../react-services/app-navigate'; import { GroupTruncate } from '../../../components/common/util'; -import { WzSearchBar, filtersToObject } from '../../../components/wz-search-bar'; +import { + WzSearchBar, + filtersToObject, +} from '../../../components/wz-search-bar'; import { getAgentFilterValues } from '../../../controllers/management/components/management/groups/get-agents-filters-values'; import { WzButtonPermissions } from '../../../components/common/permissions/button'; import { formatUIDate } from '../../../react-services/time-service'; import { withErrorBoundary } from '../../../components/common/hocs'; -import { API_NAME_AGENT_STATUS, UI_LOGGER_LEVELS, UI_ORDER_AGENT_STATUS, AGENT_SYNCED_STATUS } from '../../../../common/constants'; +import { + API_NAME_AGENT_STATUS, + UI_LOGGER_LEVELS, + UI_ORDER_AGENT_STATUS, + AGENT_SYNCED_STATUS, +} from '../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../react-services/common-services'; import { AgentStatus } from '../../../components/agents/agent_status'; @@ -61,7 +69,9 @@ export const AgentsTable = withErrorBoundary( purgeModal: false, isFilterColumnOpen: false, filters: sessionStorage.getItem('agents_preview_selected_options') - ? JSON.parse(sessionStorage.getItem('agents_preview_selected_options')) + ? JSON.parse( + sessionStorage.getItem('agents_preview_selected_options'), + ) : [], }; this.suggestions = [ @@ -84,84 +94,96 @@ export const AgentsTable = withErrorBoundary( label: 'os.platform', description: 'Filter by operating system platform', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('os.platform', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('os.platform', value, { q: 'id!=000' }), }, { type: 'q', label: 'ip', description: 'Filter by agent IP address', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('ip', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('ip', value, { q: 'id!=000' }), }, { type: 'q', label: 'name', description: 'Filter by agent name', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('name', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('name', value, { q: 'id!=000' }), }, { type: 'q', label: 'id', description: 'Filter by agent id', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('id', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('id', value, { q: 'id!=000' }), }, { type: 'q', label: 'group', description: 'Filter by agent group', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('group', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('group', value, { q: 'id!=000' }), }, { type: 'q', label: 'node_name', description: 'Filter by node name', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('node_name', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('node_name', value, { q: 'id!=000' }), }, { type: 'q', label: 'manager', description: 'Filter by manager', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('manager', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('manager', value, { q: 'id!=000' }), }, { type: 'q', label: 'version', description: 'Filter by agent version', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('version', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('version', value, { q: 'id!=000' }), }, { type: 'q', label: 'configSum', description: 'Filter by agent config sum', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('configSum', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('configSum', value, { q: 'id!=000' }), }, { type: 'q', label: 'mergedSum', description: 'Filter by agent merged sum', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('mergedSum', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('mergedSum', value, { q: 'id!=000' }), }, { type: 'q', label: 'dateAdd', description: 'Filter by add date', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('dateAdd', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('dateAdd', value, { q: 'id!=000' }), }, { type: 'q', label: 'lastKeepAlive', description: 'Filter by last keep alive', operators: ['=', '!='], - values: async (value) => getAgentFilterValues('lastKeepAlive', value, { q: 'id!=000' }), + values: async value => + getAgentFilterValues('lastKeepAlive', value, { q: 'id!=000' }), }, ]; this.downloadCsv.bind(this); @@ -221,17 +243,25 @@ export const AgentsTable = withErrorBoundary( const selectFieldsList = this.defaultColumns .filter(field => field.field != 'actions') .map(field => field.field.replace('os_', 'os.')); // "os_name" subfield should be specified as 'os.name' - const selectFields = [...selectFieldsList, 'os.platform', 'os.uname', 'os.version'].join(','); // Add version and uname fields to render the OS icon and version in the table - - const rawAgents = await this.props.wzReq('GET', '/agents', { params: { ...this.buildFilter(), select: selectFields } }); - const formatedAgents = (((rawAgents || {}).data || {}).data || {}).affected_items.map( - this.formatAgent.bind(this) - ); + const selectFields = [ + ...selectFieldsList, + 'os.platform', + 'os.uname', + 'os.version', + ].join(','); // Add version and uname fields to render the OS icon and version in the table + + const rawAgents = await this.props.wzReq('GET', '/agents', { + params: { ...this.buildFilter(), select: selectFields }, + }); + const formatedAgents = ( + ((rawAgents || {}).data || {}).data || {} + ).affected_items.map(this.formatAgent.bind(this)); this._isMount && this.setState({ agents: formatedAgents, - totalItems: (((rawAgents || {}).data || {}).data || {}).total_affected_items, + totalItems: (((rawAgents || {}).data || {}).data || {}) + .total_affected_items, isLoading: false, }); } catch (error) { @@ -251,7 +281,6 @@ export const AgentsTable = withErrorBoundary( } } - buildFilter() { const { pageIndex, pageSize, filters } = this.state; @@ -281,8 +310,16 @@ export const AgentsTable = withErrorBoundary( } formatAgent(agent) { - const agentVersion = agent.version !== undefined ? agent.version.split(' ')[1] : '-'; - const node_name = agent.node_name && agent.node_name !== 'unknown' ? agent.node_name : '-'; + const checkField = field => { + return field !== undefined ? field : '-'; + }; + const agentVersion = + agent.version !== undefined ? agent.version.split(' ')[1] : '-'; + const node_name = + agent.node_name && agent.node_name !== 'unknown' + ? agent.node_name + : '-'; + return { id: agent.id, name: agent.name, @@ -294,7 +331,9 @@ export const AgentsTable = withErrorBoundary( version: agentVersion, node_name: node_name, dateAdd: agent.dateAdd ? formatUIDate(agent.dateAdd) : '-', - lastKeepAlive: agent.lastKeepAlive ? formatUIDate(agent.lastKeepAlive) : '-', + lastKeepAlive: agent.lastKeepAlive + ? formatUIDate(agent.lastKeepAlive) + : '-', actions: agent, upgrading: false, }; @@ -303,28 +342,40 @@ export const AgentsTable = withErrorBoundary( actionButtonsRender(agent) { return (
- + { + onClick={ev => { ev.stopPropagation(); - this.props.clickAction(agent, 'default'); + AppNavigate.navigateToModule(ev, 'agents', { + tab: 'welcome', + agent: agent.id, + }); }} - iconType="eye" + iconType='eye' color={'primary'} - aria-label="Open summary panel for this agent" + aria-label='Open summary panel for this agent' />   {agent.status !== API_NAME_AGENT_STATUS.NEVER_CONNECTED && ( - + { + onClick={ev => { ev.stopPropagation(); - this.props.clickAction(agent, 'configuration'); + AppNavigate.navigateToModule(ev, 'agents', { + tab: 'configuration', + agent: agent.id, + }); }} color={'primary'} - iconType="wrench" - aria-label="Open configuration for this agent" + iconType='wrench' + aria-label='Open configuration for this agent' /> )} @@ -333,8 +384,11 @@ export const AgentsTable = withErrorBoundary( } addIconPlatformRender(agent) { - let icon = ''; - const os = agent?.os || {}; + let icon = false; + const checkField = field => { + return field !== undefined ? field : '-'; + }; + const os = (agent || {}).os; if ((os?.uname || '').includes('Linux')) { icon = 'linux'; @@ -343,15 +397,18 @@ export const AgentsTable = withErrorBoundary( } else if (os?.platform === 'darwin') { icon = 'apple'; } - const os_name = `${agent?.os?.name || ''} ${agent?.os?.version || ''}`; + const os_name = + checkField(agent?.os?.name) + ' ' + checkField(agent?.os?.version); return ( - - {' '} - {os_name.trim() || '-'} + + + + {' '} + {os_name === '- -' ? '-' : os_name} ); } @@ -367,8 +424,8 @@ export const AgentsTable = withErrorBoundary( downloadCsv = () => { const filters = this.buildFilter(); const formatedFilters = Object.keys(filters) - .filter((field) => !['limit', 'offset', 'sort'].includes(field)) - .map((field) => ({ name: field, value: filters[field] })); + .filter(field => !['limit', 'offset', 'sort'].includes(field)) + .map(field => ({ name: field, value: filters[field] })); this.props.downloadCsv(formatedFilters); }; @@ -382,14 +439,14 @@ export const AgentsTable = withErrorBoundary( return ( <> - + Export formatted - + - + @@ -414,11 +471,13 @@ export const AgentsTable = withErrorBoundary( } else if (selectedItems.length === pageSize) { return (
- + @@ -426,7 +485,7 @@ export const AgentsTable = withErrorBoundary( { this._isMount && - this.setState((prevState) => ({ + this.setState(prevState => ({ allSelected: !prevState.allSelected, })); }} @@ -438,18 +497,24 @@ export const AgentsTable = withErrorBoundary( - +
); } } getTableColumnsSelected() { - return JSON.parse(window.localStorage.getItem('columnsSelectedTableAgent')) || []; + return ( + JSON.parse(window.localStorage.getItem('columnsSelectedTableAgent')) || + [] + ); } setTableColumnsSelected(data) { - window.localStorage.setItem('columnsSelectedTableAgent', JSON.stringify(data)); + window.localStorage.setItem( + 'columnsSelectedTableAgent', + JSON.stringify(data), + ); } // Columns with the property truncateText: true won't wrap the text @@ -478,7 +543,7 @@ export const AgentsTable = withErrorBoundary( name: 'Group(s)', sortable: true, show: true, - render: (groups) => (groups !== '-' ? this.renderGroups(groups) : '-'), + render: groups => (groups !== '-' ? this.renderGroups(groups) : '-'), }, { field: 'os_name', @@ -517,14 +582,19 @@ export const AgentsTable = withErrorBoundary( truncateText: true, sortable: true, show: true, - render: (status) => , + render: status => ( + + ), }, { field: 'group_config_status', name: 'Synced', sortable: true, show: false, - render: (synced) => , + render: synced => , }, { align: 'right', @@ -532,7 +602,7 @@ export const AgentsTable = withErrorBoundary( field: 'actions', name: 'Actions', show: true, - render: (agent) => this.actionButtonsRender(agent), + render: agent => this.actionButtonsRender(agent), }, ]; @@ -541,15 +611,17 @@ export const AgentsTable = withErrorBoundary( if (selectedColumns.length != 0) { const newSelectedColumns = []; - selectedColumns.forEach((item) => { + selectedColumns.forEach(item => { if (item.show) { - const column = this.defaultColumns.find((column) => column.field === item.field); + const column = this.defaultColumns.find( + column => column.field === item.field, + ); newSelectedColumns.push(column); } }); return newSelectedColumns; } else { - const fieldColumns = this.defaultColumns.map((item) => { + const fieldColumns = this.defaultColumns.map(item => { return { field: item.field, name: item.name, @@ -579,9 +651,9 @@ export const AgentsTable = withErrorBoundary( this.props.addingNewAgent()} > Deploy new agent @@ -589,7 +661,7 @@ export const AgentsTable = withErrorBoundary( {formattedButton}
- +
); } @@ -602,12 +674,18 @@ export const AgentsTable = withErrorBoundary( noDeleteFiltersOnUpdateSuggests filters={this.state.filters} suggestions={this.suggestions} - onFiltersChange={(filters) => this.setState({ filters, pageIndex: 0 })} - placeholder="Filter or search agent" + onFiltersChange={filters => + this.setState({ filters, pageIndex: 0 }) + } + placeholder='Filter or search agent' /> - this.reloadAgents()}> + this.reloadAgents()} + > Refresh @@ -618,15 +696,15 @@ export const AgentsTable = withErrorBoundary( selectColumnsRender() { const columnsSelected = this.getTableColumnsSelected(); - const onChange = (optionId) => { - let item = columnsSelected.find((item) => item.field === optionId); + const onChange = optionId => { + let item = columnsSelected.find(item => item.field === optionId); item.show = !item.show; this.setTableColumnsSelected(columnsSelected); this.forceUpdate(); }; const options = () => { - return columnsSelected.map((item) => { + return columnsSelected.map(item => { return { id: item.field, label: item.name, @@ -641,7 +719,7 @@ export const AgentsTable = withErrorBoundary( @@ -652,12 +730,12 @@ export const AgentsTable = withErrorBoundary( } tableRender() { - const getRowProps = (item) => { + const getRowProps = item => { const { id } = item; return { 'data-test-subj': `row-${id}`, className: 'customRowClass', - onClick: () => { }, + onClick: () => {}, }; }; @@ -666,8 +744,11 @@ export const AgentsTable = withErrorBoundary( return; } return { - onMouseDown: (ev) => { - AppNavigate.navigateToModule(ev, 'agents', { tab: 'welcome', agent: item.id }); + onClick: ev => { + AppNavigate.navigateToModule(ev, 'agents', { + tab: 'welcome', + agent: item.id, + }); ev.stopPropagation(); }, }; @@ -686,11 +767,11 @@ export const AgentsTable = withErrorBoundary( const pagination = totalItems > 15 ? { - pageIndex: pageIndex, - pageSize: pageSize, - totalItemCount: totalItems, - pageSizeOptions: [15, 25, 50, 100], - } + pageIndex: pageIndex, + pageSize: pageSize, + totalItemCount: totalItems, + pageSizeOptions: [15, 25, 50, 100], + } : false; const sorting = { sort: { @@ -703,19 +784,19 @@ export const AgentsTable = withErrorBoundary( // Previously the tableLayout is set to "fixed" with percentage width for each column, but the use of space was not optimal. // Important: If all the columns have the truncateText property set to true, the table cannot adjust properly when the viewport size is small. return ( - + @@ -723,14 +804,16 @@ export const AgentsTable = withErrorBoundary( ); } - filterGroupBadge = (group) => { + filterGroupBadge = group => { const { filters } = this.state; - let auxFilters = filters.map((filter) => filter.value.match(/group=(.*S?)/)[1]); + let auxFilters = filters.map( + filter => filter.value.match(/group=(.*S?)/)[1], + ); if (filters.length > 0) { !auxFilters.includes(group) ? this.setState({ - filters: [...filters, { field: 'q', value: `group=${group}` }], - }) + filters: [...filters, { field: 'q', value: `group=${group}` }], + }) : false; } else { this.setState({ @@ -751,7 +834,6 @@ export const AgentsTable = withErrorBoundary( /> ); } - render() { const title = this.headRender(); const filter = this.filterBarRender(); @@ -763,8 +845,8 @@ export const AgentsTable = withErrorBoundary( return (
{filter} - - + + {title} {loadItems} {callOut} @@ -775,14 +857,13 @@ export const AgentsTable = withErrorBoundary(
); } - } + }, ); AgentsTable.propTypes = { wzReq: PropTypes.func, addingNewAgent: PropTypes.func, downloadCsv: PropTypes.func, - clickAction: PropTypes.func, timeService: PropTypes.func, reload: PropTypes.func, }; diff --git a/plugins/main/public/controllers/agent/components/register-agent.js b/plugins/main/public/controllers/agent/components/register-agent.js index 99b0a5b105..94a1b3db23 100644 --- a/plugins/main/public/controllers/agent/components/register-agent.js +++ b/plugins/main/public/controllers/agent/components/register-agent.js @@ -271,7 +271,7 @@ export const RegisterAgent = withErrorBoundary( this.state.selectedVersion === 'windowsserver2008' || this.state.selectedVersion === 'windows7' ) { - return 'NET START WazuhSvc'; + return 'NET START Wazuh'; } else { return ''; } @@ -1005,7 +1005,7 @@ export const RegisterAgent = withErrorBoundary( : ``; // Merge environment variables with installation script - const macOSInstallationScript = `curl -so wazuh-agent.pkg https://packages.wazuh.com/4.x/macos/wazuh-agent-${this.state.wazuhVersion}-1.pkg && ${macOSInstallationSetEnvVariablesScript}sudo installer -pkg ./wazuh-agent.pkg -target /`; + const macOSInstallationScript = `curl -so wazuh-agent.pkg https://packages.wazuh.com/4.x/macos/wazuh-agent-${this.state.wazuhVersion}-1.${this.state.selectedArchitecture}.pkg && ${macOSInstallationSetEnvVariablesScript}sudo installer -pkg ./wazuh-agent.pkg -target /`; /*** end macOS installation script customization ***/ diff --git a/plugins/main/public/controllers/agent/wazuh-config/index.ts b/plugins/main/public/controllers/agent/wazuh-config/index.ts index e555fb5517..f10c7994c1 100644 --- a/plugins/main/public/controllers/agent/wazuh-config/index.ts +++ b/plugins/main/public/controllers/agent/wazuh-config/index.ts @@ -103,8 +103,12 @@ const architectureButtonsSolaris = [ const architectureButtonsMacos = [ { - id: 'intel/applesilicon', - label: 'Intel/Apple Silicon', + id: 'intel64', + label: 'Intel', + }, + { + id: 'arm64', + label: 'Apple Silicon', }, ]; diff --git a/plugins/main/public/controllers/management/components/management/configuration/client/client.js b/plugins/main/public/controllers/management/components/management/configuration/client/client.js index 7c569fd5e6..d5e2ada8c6 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/client/client.js +++ b/plugins/main/public/controllers/management/components/management/configuration/client/client.js @@ -22,7 +22,7 @@ import WzConfigurationSettingsGroup from '../util-components/configuration-setti import { isString, renderValueOrDefault, - renderValueOrNoValue + renderValueOrNoValue, } from '../utils/utils'; import withWzConfig from '../util-hocs/wz-config'; import { webDocumentationLink } from '../../../../../../../common/services/web_documentation'; @@ -30,12 +30,12 @@ import { webDocumentationLink } from '../../../../../../../common/services/web_d const helpLinks = [ { text: 'Checking connection with manager', - href: webDocumentationLink('user-manual/agents/agent-connection.html') + href: webDocumentationLink('user-manual/agents/agent-connection.html'), }, { text: 'Client reference', - href: webDocumentationLink('user-manual/reference/ossec-conf/client.html') - } + href: webDocumentationLink('user-manual/reference/ossec-conf/client.html'), + }, ]; const mainSettings = [ @@ -44,29 +44,37 @@ const mainSettings = [ { field: 'auto_restart', label: - 'Auto-restart the agent when receiving valid configuration from manager' + 'Auto-restart the agent when receiving valid configuration from manager', }, { field: 'notify_time', - label: 'Time (in seconds) between agent checkings to the manager' + label: 'Time (in seconds) between agent checkings to the manager', }, { field: 'time-reconnect', - label: 'Time (in seconds) before attempting to reconnect' + label: 'Time (in seconds) before attempting to reconnect', }, { field: 'config-profile', label: 'Configuration profiles' }, { field: 'local_ip', - label: 'IP address used when the agent has multiple network interfaces' - } + label: 'IP address used when the agent has multiple network interfaces', + }, ]; const columns = [ { field: 'address', name: 'Address', render: renderValueOrNoValue }, { field: 'port', name: 'Port', render: renderValueOrDefault('1514') }, { field: 'protocol', name: 'Protocol', render: renderValueOrDefault('udp') }, - { field: 'max_retries', name: 'Maximum retries to connect', render: renderValueOrNoValue }, - { field: 'retry_interval', name: 'Retry interval to connect', render: renderValueOrNoValue }, + { + field: 'max_retries', + name: 'Maximum retries to connect', + render: renderValueOrNoValue, + }, + { + field: 'retry_interval', + name: 'Retry interval to connect', + render: renderValueOrNoValue, + }, ]; class WzConfigurationClient extends Component { @@ -87,8 +95,8 @@ class WzConfigurationClient extends Component { {currentConfig['agent-client'] && !isString(currentConfig['agent-client']) && ( )} diff --git a/plugins/main/public/controllers/management/components/management/configuration/global-configuration/global-configuration-remote.js b/plugins/main/public/controllers/management/components/management/configuration/global-configuration/global-configuration-remote.js index 2c0a2bddae..f72cd44fea 100644 --- a/plugins/main/public/controllers/management/components/management/configuration/global-configuration/global-configuration-remote.js +++ b/plugins/main/public/controllers/management/components/management/configuration/global-configuration/global-configuration-remote.js @@ -19,7 +19,7 @@ import WzNoConfig from '../util-components/no-config'; import { isString, renderValueOrNoValue, - renderValueOrDefault + renderValueOrDefault, } from '../utils/utils'; import { webDocumentationLink } from '../../../../../../../common/services/web_documentation'; @@ -40,12 +40,14 @@ const renderAllowedDeniedIPs = (items, label) => { const helpLinks = [ { text: 'Remote daemon reference', - href: webDocumentationLink('user-manual/reference/daemons/wazuh-remoted.html') + href: webDocumentationLink( + 'user-manual/reference/daemons/wazuh-remoted.html', + ), }, { text: 'Remote configuration reference', - href: webDocumentationLink('user-manual/reference/ossec-conf/remote.html') - } + href: webDocumentationLink('user-manual/reference/ossec-conf/remote.html'), + }, ]; class WzConfigurationGlobalConfigurationRemote extends Component { @@ -57,29 +59,29 @@ class WzConfigurationGlobalConfigurationRemote extends Component { { field: 'protocol', name: 'Protocol', - render: renderValueOrDefault('udp') + render: renderValueOrDefault('udp'), }, { field: 'ipv6', name: 'IPv6', render: renderValueOrNoValue }, { field: 'allowed-ips', name: 'Allowed IP addresses', - render: item => renderAllowedDeniedIPs(item, 'allowed') + render: item => renderAllowedDeniedIPs(item, 'allowed'), }, { field: 'denied-ips', name: 'Denied IP addresses', - render: item => renderAllowedDeniedIPs(item, 'denied') + render: item => renderAllowedDeniedIPs(item, 'denied'), }, { field: 'local_ip', name: 'Local IP address', - render: renderValueOrDefault('All interfaces') + render: renderValueOrDefault('All interfaces'), }, { field: 'queue_size', name: 'Queue size', - render: renderValueOrDefault('16384') - } + render: renderValueOrDefault('16384'), + }, ]; } render() { @@ -96,21 +98,22 @@ class WzConfigurationGlobalConfigurationRemote extends Component { {currentConfig['request-remote'] && !isString(currentConfig['request-remote']) && !currentConfig['request-remote'].remote && ( - + )} {currentConfig['request-remote'] && currentConfig['request-remote'].remote && ( - + )} diff --git a/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js b/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js index 534726d6aa..e7c4a11aba 100644 --- a/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js +++ b/plugins/main/public/controllers/management/components/management/groups/group-agents-table.js @@ -9,13 +9,11 @@ * * Find more information about this on the LICENSE file. */ -import React, { Component } from 'react'; -import { EuiCallOut } from '@elastic/eui'; +import React, { Component, Fragment } from 'react'; import { connect } from 'react-redux'; import GroupsHandler from './utils/groups-handler'; import { getToasts } from '../../../../../kibana-services'; - import { updateLoadingStatus, updateFileContent, @@ -27,16 +25,18 @@ import { updateSortFieldAgents, updateReload, } from '../../../../../redux/actions/groupsActions'; - +import { EuiCallOut } from '@elastic/eui'; import { getAgentFilterValues } from './get-agents-filters-values'; import { TableWzAPI } from '../../../../../components/common/tables'; import { WzButtonPermissions } from '../../../../../components/common/permissions/button'; import { WzButtonPermissionsModalConfirm } from '../../../../../components/common/buttons'; -import { UI_LOGGER_LEVELS, UI_ORDER_AGENT_STATUS } from '../../../../../../common/constants'; +import { + UI_LOGGER_LEVELS, + UI_ORDER_AGENT_STATUS, +} from '../../../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../../../react-services/common-services'; - class WzGroupAgentsTable extends Component { _isMounted = false; constructor(props) { @@ -54,7 +54,7 @@ class WzGroupAgentsTable extends Component { label: 'os.platform', description: 'Filter by operating system platform', operators: ['=', '!='], - values: async (value) => + values: async value => getAgentFilterValues('os.platform', value, { q: `group=${this.props.state.itemDetail.name}`, }), @@ -64,31 +64,37 @@ class WzGroupAgentsTable extends Component { label: 'ip', description: 'Filter by agent IP address', operators: ['=', '!='], - values: async (value) => - getAgentFilterValues('ip', value, { q: `group=${this.props.state.itemDetail.name}` }), + values: async value => + getAgentFilterValues('ip', value, { + q: `group=${this.props.state.itemDetail.name}`, + }), }, { type: 'q', label: 'name', description: 'Filter by agent name', operators: ['=', '!='], - values: async (value) => - getAgentFilterValues('name', value, { q: `group=${this.props.state.itemDetail.name}` }), + values: async value => + getAgentFilterValues('name', value, { + q: `group=${this.props.state.itemDetail.name}`, + }), }, { type: 'q', label: 'id', description: 'Filter by agent id', operators: ['=', '!='], - values: async (value) => - getAgentFilterValues('id', value, { q: `group=${this.props.state.itemDetail.name}` }), + values: async value => + getAgentFilterValues('id', value, { + q: `group=${this.props.state.itemDetail.name}`, + }), }, { type: 'q', label: 'node_name', description: 'Filter by node name', operators: ['=', '!='], - values: async (value) => + values: async value => getAgentFilterValues('node_name', value, { q: `group=${this.props.state.itemDetail.name}`, }), @@ -98,7 +104,7 @@ class WzGroupAgentsTable extends Component { label: 'manager', description: 'Filter by manager', operators: ['=', '!='], - values: async (value) => + values: async value => getAgentFilterValues('manager', value, { q: `group=${this.props.state.itemDetail.name}`, }), @@ -108,7 +114,7 @@ class WzGroupAgentsTable extends Component { label: 'version', description: 'Filter by agent version', operators: ['=', '!='], - values: async (value) => + values: async value => getAgentFilterValues('version', value, { q: `group=${this.props.state.itemDetail.name}`, }), @@ -118,7 +124,7 @@ class WzGroupAgentsTable extends Component { label: 'configSum', description: 'Filter by agent config sum', operators: ['=', '!='], - values: async (value) => + values: async value => getAgentFilterValues('configSum', value, { q: `group=${this.props.state.itemDetail.name}`, }), @@ -128,7 +134,7 @@ class WzGroupAgentsTable extends Component { label: 'mergedSum', description: 'Filter by agent merged sum', operators: ['=', '!='], - values: async (value) => + values: async value => getAgentFilterValues('mergedSum', value, { q: `group=${this.props.state.itemDetail.name}`, }), @@ -154,8 +160,8 @@ class WzGroupAgentsTable extends Component { { field: 'ip', name: 'IP address', - align: 'left', sortable: true, + show: true, }, { field: 'status', @@ -184,49 +190,57 @@ class WzGroupAgentsTable extends Component { { name: 'Actions', align: 'left', - render: (item) => { + render: item => { return (
({ + ...(item.group || []).map(group => ({ action: 'agent:read', resource: `agent:group:${group}`, })), ], ]} tooltip={{ position: 'top', content: 'Go to the agent' }} - aria-label="Go to the agent" - iconType="eye" + aria-label='Go to the agent' + iconType='eye' onClick={async () => { this.props.groupsProps.showAgent(item); }} - color="primary" + color='primary' /> {this.props?.state?.itemDetail?.name !== 'default' && ( ({ + { + action: 'agent:modify_group', + resource: `agent:id:${item.id}`, + }, + ...(item.group || []).map(group => ({ action: 'agent:modify_group', resource: `agent:group:${group}`, })), ], ]} - tooltip={{ position: 'top', content: 'Remove agent from this group' }} - aria-label="Remove agent from this group" - iconType="trash" + tooltip={{ + position: 'top', + content: 'Remove agent from this group', + }} + aria-label='Remove agent from this group' + iconType='trash' onConfirm={async () => { this.removeItems([item]); }} - color="danger" + color='danger' isDisabled={item.name === 'default'} - modalTitle={`Remove ${item.file || item.name} agent from this group?`} + modalTitle={`Remove ${ + item.file || item.name + } agent from this group?`} modalProps={{ buttonColor: 'danger', }} @@ -242,22 +256,22 @@ class WzGroupAgentsTable extends Component { componentWillUnmount() { this._isMounted = false; } - render() { const { error } = this.props.state; if (!error) { return ( ); } else { - return ; + return ; } } @@ -274,7 +288,11 @@ class WzGroupAgentsTable extends Component { const { itemDetail } = this.props.state; this.props.updateLoadingStatus(true); try { - await Promise.all(items.map(item => this.groupsHandler.deleteAgent(item.id, itemDetail.name))); + await Promise.all( + items.map(item => + this.groupsHandler.deleteAgent(item.id, itemDetail.name), + ), + ); this.props.updateIsProcessing(true); this.props.updateLoadingStatus(false); this.props.updateReload(); @@ -299,23 +317,27 @@ class WzGroupAgentsTable extends Component { } } -const mapStateToProps = (state) => { +const mapStateToProps = state => { return { state: state.groupsReducers, }; }; -const mapDispatchToProps = (dispatch) => { +const mapDispatchToProps = dispatch => { return { - updateLoadingStatus: (status) => dispatch(updateLoadingStatus(status)), - updateFileContent: (content) => dispatch(updateFileContent(content)), - updateIsProcessing: (isProcessing) => dispatch(updateIsProcessing(isProcessing)), - updatePageIndexAgents: (pageIndexAgents) => dispatch(updatePageIndexAgents(pageIndexAgents)), - updateShowModal: (showModal) => dispatch(updateShowModal(showModal)), - updateListItemsForRemove: (itemList) => dispatch(updateListItemsForRemove(itemList)), - updateSortDirectionAgents: (sortDirectionAgents) => + updateLoadingStatus: status => dispatch(updateLoadingStatus(status)), + updateFileContent: content => dispatch(updateFileContent(content)), + updateIsProcessing: isProcessing => + dispatch(updateIsProcessing(isProcessing)), + updatePageIndexAgents: pageIndexAgents => + dispatch(updatePageIndexAgents(pageIndexAgents)), + updateShowModal: showModal => dispatch(updateShowModal(showModal)), + updateListItemsForRemove: itemList => + dispatch(updateListItemsForRemove(itemList)), + updateSortDirectionAgents: sortDirectionAgents => dispatch(updateSortDirectionAgents(sortDirectionAgents)), - updateSortFieldAgents: (sortFieldAgents) => dispatch(updateSortFieldAgents(sortFieldAgents)), + updateSortFieldAgents: sortFieldAgents => + dispatch(updateSortFieldAgents(sortFieldAgents)), updateReload: () => dispatch(updateReload()), }; }; diff --git a/plugins/main/scripts/tag.py b/plugins/main/scripts/tag.py index 3bcbea3dd6..21ab704414 100644 --- a/plugins/main/scripts/tag.py +++ b/plugins/main/scripts/tag.py @@ -17,9 +17,9 @@ # Wazuh version: major.minor.patch version = '4.6.0' # App's revision number (previous rev + 1) -revision = '01' +revision = '02' # One of 'pre-alpha', 'alpha', 'beta', 'release-candidate', 'stable' -stage = 'alpha' +stage = 'stable' # Tag suffix. Usually set to stage + stage iteration. tag_suffix = '-alpha1' @@ -30,7 +30,7 @@ TAGS_FILE = 'tags.log' # Global variable. Will be set later branch = None -minor = ".".join(version.split('.')[:2]) +minor = version # Supported versions of Kibana kbn_versions = [ diff --git a/plugins/main/yarn.lock b/plugins/main/yarn.lock new file mode 100644 index 0000000000..d4d5bca1f3 --- /dev/null +++ b/plugins/main/yarn.lock @@ -0,0 +1,2888 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@babel/code-frame@^7.0.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== + +"@eslint/eslintrc@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" + integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" + integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== + +"@foliojs-fork/fontkit@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz#8124649168eb5273f580f66697a139fb5041296b" + integrity sha512-U589voc2/ROnvx1CyH9aNzOQWJp127JGU1QAylXGQ7LoEAF6hMmahZLQ4eqAcgHUw+uyW4PjtCItq9qudPkK3A== + dependencies: + "@foliojs-fork/restructure" "^2.0.2" + brfs "^2.0.0" + brotli "^1.2.0" + browserify-optional "^1.0.1" + clone "^1.0.4" + deep-equal "^1.0.0" + dfa "^1.2.0" + tiny-inflate "^1.0.2" + unicode-properties "^1.2.2" + unicode-trie "^2.0.0" + +"@foliojs-fork/linebreak@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@foliojs-fork/linebreak/-/linebreak-1.1.1.tgz#93ecd695b7d2bb0334b9481058c3e610e019a4eb" + integrity sha512-pgY/+53GqGQI+mvDiyprvPWgkTlVBS8cxqee03ejm6gKAQNsR1tCYCIvN9FHy7otZajzMqCgPOgC4cHdt4JPig== + dependencies: + base64-js "1.3.1" + brfs "^2.0.2" + unicode-trie "^2.0.0" + +"@foliojs-fork/pdfkit@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@foliojs-fork/pdfkit/-/pdfkit-0.13.0.tgz#54f5368d8cf74d8edc81a175ccda1fd9655f2db9" + integrity sha512-YXeG1fml9k97YNC9K8e292Pj2JzGt9uOIiBFuQFxHsdQ45BlxW+JU3RQK6JAvXU7kjhjP8rCcYvpk36JLD33sQ== + dependencies: + "@foliojs-fork/fontkit" "^1.9.1" + "@foliojs-fork/linebreak" "^1.1.1" + crypto-js "^4.0.0" + png-js "^1.0.0" + +"@foliojs-fork/restructure@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4" + integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA== + +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" + integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== + +"@types/hoist-non-react-statics@^3.0.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + +"@types/json-schema@^7.0.9": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/node-cron@^2.0.3": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-2.0.5.tgz#e244709a86d32453c5a702ced35b53db683fbc8e" + integrity sha512-rQ4kduTmgW11tbtx0/RsoybYHHPu4Vxw5v5ZS5qUKNerlEAI8r8P1F5UUZ2o2HTvzG759sbFxuRuqWxU8zc+EQ== + dependencies: + "@types/tz-offset" "*" + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/react@*": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" + integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + +"@types/semver@^7.3.12": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== + +"@types/triple-beam@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8" + integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g== + +"@types/tz-offset@*": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@types/tz-offset/-/tz-offset-0.0.0.tgz#d58f1cebd794148d245420f8f0660305d320e565" + integrity sha512-XLD/llTSB6EBe3thkN+/I0L+yCTB6sjrcVovQdx2Cnl6N6bTzHmwe/J8mWnsXFgxLrj/emzdv8IR4evKYG2qxQ== + +"@typescript-eslint/eslint-plugin@^5.38.1": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz#a1a5290cf33863b4db3fb79350b3c5275a7b1223" + integrity sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.61.0" + "@typescript-eslint/type-utils" "5.61.0" + "@typescript-eslint/utils" "5.61.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.38.1": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.61.0.tgz#7fbe3e2951904bb843f8932ebedd6e0635bffb70" + integrity sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg== + dependencies: + "@typescript-eslint/scope-manager" "5.61.0" + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/typescript-estree" "5.61.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.61.0.tgz#b670006d069c9abe6415c41f754b1b5d949ef2b2" + integrity sha512-W8VoMjoSg7f7nqAROEmTt6LoBpn81AegP7uKhhW5KzYlehs8VV0ZW0fIDVbcZRcaP3aPSW+JZFua+ysQN+m/Nw== + dependencies: + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/visitor-keys" "5.61.0" + +"@typescript-eslint/type-utils@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.61.0.tgz#e90799eb2045c4435ea8378cb31cd8a9fddca47a" + integrity sha512-kk8u//r+oVK2Aj3ph/26XdH0pbAkC2RiSjUYhKD+PExemG4XSjpGFeyZ/QM8lBOa7O8aGOU+/yEbMJgQv/DnCg== + dependencies: + "@typescript-eslint/typescript-estree" "5.61.0" + "@typescript-eslint/utils" "5.61.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.61.0.tgz#e99ff11b5792d791554abab0f0370936d8ca50c0" + integrity sha512-ldyueo58KjngXpzloHUog/h9REmHl59G1b3a5Sng1GfBo14BkS3ZbMEb3693gnP1k//97lh7bKsp6/V/0v1veQ== + +"@typescript-eslint/typescript-estree@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.61.0.tgz#4c7caca84ce95bb41aa585d46a764bcc050b92f3" + integrity sha512-Fud90PxONnnLZ36oR5ClJBLTLfU4pIWBmnvGwTbEa2cXIqj70AEDEmOmpkFComjBZ/037ueKrOdHuYmSFVD7Rw== + dependencies: + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/visitor-keys" "5.61.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.61.0.tgz#5064838a53e91c754fffbddd306adcca3fe0af36" + integrity sha512-mV6O+6VgQmVE6+xzlA91xifndPW9ElFW8vbSF0xCT/czPXVhwDewKila1jOyRwa9AE19zKnrr7Cg5S3pJVrTWQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.61.0" + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/typescript-estree" "5.61.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.61.0.tgz#c79414fa42158fd23bd2bb70952dc5cdbb298140" + integrity sha512-50XQ5VdbWrX06mQXhy93WywSFZZGsv3EOjq+lqp6WC2t+j3mb6A9xYVdrRxafvK88vg9k9u+CT4l6D8PEatjKg== + dependencies: + "@typescript-eslint/types" "5.61.0" + eslint-visitor-keys "^3.3.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-node@^1.3.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.0.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== + +angular-animate@1.7.8: + version "1.7.8" + resolved "https://registry.yarnpkg.com/angular-animate/-/angular-animate-1.7.8.tgz#c95f237efe7ecfe0e6003adb5e2c7ef0e5a2b9d4" + integrity sha512-bINtzizq7TbJzfVrDpwLfTxVl0Qd7fRNWFb5jKYI1vaFZobQNX/QONXlLow6ySsDbZ6eLECycB7mvWtfh1YYaw== + +angular-material@1.1.18: + version "1.1.18" + resolved "https://registry.yarnpkg.com/angular-material/-/angular-material-1.1.18.tgz#91976b9df06a66e6627c6bf4ce074c10daa3d998" + integrity sha512-a+9Jzg4WF10G3vMbLCp5LSbmroeEbEYvzQoYVpWcXIgOUmnuOjxsNDPL73uNWOE97wOtEcFWxKF2wcP8zgixbA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-from@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" + integrity sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg== + +array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +ast-transform@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/ast-transform/-/ast-transform-0.0.0.tgz#74944058887d8283e189d954600947bc98fe0062" + integrity sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A== + dependencies: + escodegen "~1.2.0" + esprima "~1.0.4" + through "~2.3.4" + +ast-types@^0.7.0: + version "0.7.8" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" + integrity sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q== + +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base64-js@^1.1.2, base64-js@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brfs@^2.0.0, brfs@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brfs/-/brfs-2.0.2.tgz#44237878fa82aa479ce4f5fe2c1796ec69f07845" + integrity sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ== + dependencies: + quote-stream "^1.0.1" + resolve "^1.1.5" + static-module "^3.0.2" + through2 "^2.0.0" + +brotli@^1.2.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" + integrity sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg== + dependencies: + base64-js "^1.1.2" + +browser-resolve@^1.8.1: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + +browserify-optional@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-optional/-/browserify-optional-1.0.1.tgz#1e13722cfde0d85f121676c2a72ced533a018869" + integrity sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ== + dependencies: + ast-transform "0.0.0" + ast-types "^0.7.0" + browser-resolve "^1.8.1" + +buffer-equal@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" + integrity sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^2.0.0, chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + +classnames@^2.2.5: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + +clone@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +codemirror@^5.18.2: + version "5.65.13" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.13.tgz#c098a6f409db8b5a7c5722788bd9fa3bb2367f2e" + integrity sha512-SVWEzKXmbHmTQQWaz03Shrh4nybG0wXx2MEu3FO4ezbPW8IbnZEd5iGHGEffSUaitKYa3i+pHpBsSvw8sPHtzg== + +color-convert@^1.9.0, color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + +commander@^2.12.1, commander@^2.15.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@~1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +convert-source-map@^1.5.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +cookie@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +create-react-class@^15.5.1: + version "15.7.0" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e" + integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng== + dependencies: + loose-envify "^1.3.1" + object-assign "^4.1.1" + +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + +crypto-js@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + +csstype@^3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dash-ast@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/dash-ast/-/dash-ast-2.0.1.tgz#8d0fd2e601c59bf874cc22877ee7dd889f54dee8" + integrity sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ== + +debug@^3.2.6, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-equal@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +dfa@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" + integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== + dependencies: + readable-stream "^2.0.2" + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== + dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.0" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-map@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + integrity sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A== + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@^0.1.5, es6-set@~0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.6.tgz#5669e3b2aa01d61a50ba79964f733673574983b8" + integrity sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + es6-iterator "~2.0.3" + es6-symbol "^3.1.3" + event-emitter "^0.3.5" + type "^2.7.2" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^1.11.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +escodegen@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.2.0.tgz#09de7967791cc958b7f89a2ddb6d23451af327e1" + integrity sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA== + dependencies: + esprima "~1.0.4" + estraverse "~1.5.0" + esutils "~1.0.0" + optionalDependencies: + source-map "~0.1.30" + +eslint-config-prettier@^8.5.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== + +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + +eslint-import-resolver-typescript@2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" + integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== + dependencies: + debug "^4.3.4" + glob "^7.2.0" + is-glob "^4.0.3" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-async-await@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-async-await/-/eslint-plugin-async-await-0.0.0.tgz#0f2ae17a3814780635d48f2409df9e37898ca09f" + integrity sha512-CNizhDO2f1dLaoA6wah3Yj8bSmsDC7wRTt4bsFBOUEYvzcd6XNhxBmz3EgqxD6a+V4Zjl8qTiDMZo3ug+qjojg== + +eslint-plugin-cypress@^2.12.1: + version "2.13.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.13.3.tgz#5fc1afdc939aaa7daa9181f651f2f35429733ff2" + integrity sha512-nAPjZE5WopCsgJwl3vHm5iafpV+ZRO76Z9hMyRygWhmg5ODXDPd+9MaPl7kdJ2azj+sO87H3P1PRnggIrz848g== + dependencies: + globals "^11.12.0" + +eslint-plugin-filenames-simple@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-filenames-simple/-/eslint-plugin-filenames-simple-0.7.0.tgz#cff3c48de89ff543ef8f724dde135daac7ae5714" + integrity sha512-CbiYl+XJtVI+JwIf573c9tENDYwngX7iYgPVJn6LExlXkdPhLKgBa4Rcht/lNBdgT1C5k3c+6eK7NePxs6Kakg== + dependencies: + pluralize "^8.0.0" + +eslint-plugin-import@^2.26.0: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-react-hooks@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + +eslint-plugin-react@^7.31.8: + version "7.32.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" + integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== + +eslint@^8.24.0: + version "8.44.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" + integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.1.0" + "@eslint/js" "8.44.0" + "@humanwhocodes/config-array" "^0.11.10" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.1" + espree "^9.6.0" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" + integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esprima@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + integrity sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA== + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estraverse@~1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" + integrity sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ== + +estree-is-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/estree-is-function/-/estree-is-function-1.0.0.tgz#c0adc29806d7f18a74db7df0f3b2666702e37ad2" + integrity sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +esutils@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" + integrity sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg== + +event-emitter@^0.3.5, event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.9: + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" + integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + +follow-redirects@^1.14.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functions-have-names@^1.2.2, functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-assigned-identifiers@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" + integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.1, glob@^7.1.3, glob@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.12.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +install@^0.10.1: + version "0.10.4" + resolved "https://registry.yarnpkg.com/install/-/install-0.10.4.tgz#9cb09115768b93a582d1450a6ba3f275975b49aa" + integrity sha512-+IRyOastuPmLVx9zlVXJoKErSqz1Ma5at9A7S8yfsj3W+Kg95faPoh3bPDtMrZ/grz4PRmXzrswmlzfLlYyLOw== + +internal-slot@^1.0.3, internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-regex@^1.0.4, is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +js2xmlparser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733" + integrity sha512-CSOkdn0/GhRFwxnipmhXfqJ+FG6+wkWBi46kKSsPx6+j65176ZiQcrCYpg6K8x3iLbO4k3zScBnZ7I/L80dAtw== + dependencies: + xmlcreate "^1.0.1" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json2csv@^4.1.2: + version "4.5.4" + resolved "https://registry.yarnpkg.com/json2csv/-/json2csv-4.5.4.tgz#2b59c2869a137ec48cd2e243e0180466155f773f" + integrity sha512-YxBhY4Lmn8IvVZ36nqg5omxneLy9JlorkqW1j/EDCeqvmi+CQ4uM+wsvXlcIqvGDewIPXMC/O/oF8DX9EH5aoA== + dependencies: + commander "^2.15.1" + jsonparse "^1.3.1" + lodash.get "^4.4.2" + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.4" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz#b896535fed5b867650acce5a9bd4135ffc7b3bf9" + integrity sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +jwt-decode@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79" + integrity sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ== + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg== + +logform@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" + integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== + dependencies: + "@colors/colors" "1.5.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + +loglevel@^1.7.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" + integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== + +loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e" + integrity sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg== + dependencies: + sourcemap-codec "^1.4.1" + +markdown-it-link-attributes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-link-attributes/-/markdown-it-link-attributes-3.0.0.tgz#12d6f403102ac22695ee2617bec109ee79426e45" + integrity sha512-B34ySxVeo6MuEGSPCWyIYryuXINOvngNZL87Mp7YYfKIf6DcD837+lXA8mo6EBbauKsnGz22ZH0zsbOiQRWTNg== + +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + +merge-source-map@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" + integrity sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA== + dependencies: + source-map "^0.5.6" + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +needle@^2.0.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-cron@^1.1.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-1.2.1.tgz#8c90bc5dc723a56289b0786655ab4a1c4cb60368" + integrity sha512-lgci/ub6KWL6SUnKOIiMkhfjmUk3jvEuO/Ypa2/CGSXiC8z4x9EkwMx7Dcu7Dt4LktcfOl8/WcLT2x7gInBa7g== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.12.3, object-inspect@^1.6.0, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.0.1: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +pako@^0.2.5: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pdfmake@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.7.tgz#a7a46532ffde032674929988393c20b075cf65e3" + integrity sha512-ClLpgx30H5G3EDvRW1MrA1Xih6YxEaSgIVFrOyBMgAAt62V+hxsyWAi6JNP7u1Fc5JKYAbpb4RRVw8Rhvmz5cQ== + dependencies: + "@foliojs-fork/linebreak" "^1.1.1" + "@foliojs-fork/pdfkit" "^0.13.0" + iconv-lite "^0.6.3" + xmldoc "^1.1.2" + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + +png-js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" + integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.7.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +prop-types@^15.5.4, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +querystring-browser@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/querystring-browser/-/querystring-browser-1.0.4.tgz#f2e35881840a819bc7b1bf597faf0979e6622dc6" + integrity sha512-oqPm3iZO4r4lEFM2YAJyMwCqAMIL0r3jO36ZohmHLUs9NpAfEGee7G5+PllGec/TkAnfI85FMmkPaW8UbZI0Uw== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quote-stream@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" + integrity sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ== + dependencies: + buffer-equal "0.0.1" + minimist "^1.1.3" + through2 "^2.0.0" + +react-codemirror@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-1.0.0.tgz#91467b53b1f5d80d916a2fd0b4c7adb85a9001ba" + integrity sha512-pPvL8b1vwLyfX5f3EMLyqZVXYY/qAKdqURYxi3izYfjWbnUdqVaFBA7z78o9eEM+UzgxuKjI864BJkPIRVS2JA== + dependencies: + classnames "^2.2.5" + codemirror "^5.18.2" + create-react-class "^15.5.1" + lodash.debounce "^4.0.8" + lodash.isequal "^4.5.0" + prop-types "^15.5.4" + +react-cookie@^4.0.3: + version "4.1.1" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-4.1.1.tgz#832e134ad720e0de3e03deaceaab179c4061a19d" + integrity sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A== + dependencies: + "@types/hoist-non-react-statics" "^3.0.1" + hoist-non-react-statics "^3.0.0" + universal-cookie "^4.0.0" + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +read-last-lines@^1.7.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/read-last-lines/-/read-last-lines-1.8.0.tgz#4f94d4345ece7b8083ebb71c5fcdf60bd7afb9cc" + integrity sha512-oPL0cnZkhsO2xF7DBrdzVhXSNajPP5TzzCim/2IAjeGb17ArLLTRriI/ceV6Rook3L27mvbrOvLlf9xYYnaftQ== + dependencies: + mz "^2.7.0" + +readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.3, readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +redux-mock-store@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.4.tgz#90d02495fd918ddbaa96b83aef626287c9ab5872" + integrity sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA== + dependencies: + lodash.isplainobject "^4.0.6" + +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== + +resolve@^1.1.5, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.2: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +scope-analyzer@^2.0.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/scope-analyzer/-/scope-analyzer-2.1.2.tgz#b958162feb59823c2835c7b0229187a97c77e9cd" + integrity sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ== + dependencies: + array-from "^2.1.1" + dash-ast "^2.0.1" + es6-map "^0.1.5" + es6-set "^0.1.5" + es6-symbol "^3.1.1" + estree-is-function "^1.0.0" + get-assigned-identifiers "^1.1.0" + +semver@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== + +semver@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.7: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shallow-copy@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + integrity sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@~0.1.30: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + integrity sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ== + dependencies: + amdefine ">=0.0.4" + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.1: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + +static-eval@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.1.0.tgz#a16dbe54522d7fa5ef1389129d813fd47b148014" + integrity sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw== + dependencies: + escodegen "^1.11.1" + +static-module@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/static-module/-/static-module-3.0.4.tgz#bfbd1d1c38dd1fbbf0bb4af0c1b3ae18a93a2b68" + integrity sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw== + dependencies: + acorn-node "^1.3.0" + concat-stream "~1.6.0" + convert-source-map "^1.5.1" + duplexer2 "~0.1.4" + escodegen "^1.11.1" + has "^1.0.1" + magic-string "0.25.1" + merge-source-map "1.0.4" + object-inspect "^1.6.0" + readable-stream "~2.3.3" + scope-analyzer "^2.0.1" + shallow-copy "~0.0.1" + static-eval "^2.0.5" + through2 "~2.0.3" + +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +through2@^2.0.0, through2@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A== + +tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" + integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +triple-beam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" + integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== + +tsconfig-paths@^3.14.1: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.0, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslint@^5.11.0: + version "5.20.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" + integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.1" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +typescript-eslint-parser@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-18.0.0.tgz#3e5055a44980d69e4154350fc5d8b1ab4e2332a8" + integrity sha512-Pn/A/Cw9ysiXSX5U1xjBmPQlxtWGV2o7jDNiH/u7KgBO2yC/y37wNFl2ogSrGZBQFuglLzGq0Xl0Bt31Jv44oA== + dependencies: + lodash.unescape "4.0.1" + semver "5.5.0" + +typescript@^4.4.2: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unicode-properties@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/unicode-properties/-/unicode-properties-1.4.1.tgz#96a9cffb7e619a0dc7368c28da27e05fc8f9be5f" + integrity sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg== + dependencies: + base64-js "^1.3.0" + unicode-trie "^2.0.0" + +unicode-trie@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-2.0.0.tgz#8fd8845696e2e14a8b67d78fa9e0dd2cad62fec8" + integrity sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ== + dependencies: + pako "^0.2.5" + tiny-inflate "^1.0.0" + +universal-cookie@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" + integrity sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw== + dependencies: + "@types/cookie" "^0.3.3" + cookie "^0.4.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +winston-transport@^4.4.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" + integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.5.1.tgz#b25cc899d015836dbf8c583dec8c4c4483a0da2e" + integrity sha512-tbRtVy+vsSSCLcZq/8nXZaOie/S2tPXPFt4be/Q3vI/WtYwm7rrwidxVw2GRa38FIXcJ1kUM6MOZ9Jmnk3F3UA== + dependencies: + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.3.2" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.4.2" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xmlcreate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f" + integrity sha512-Mbe56Dvj00onbnSo9J0qj/XlY5bfN9KidsOnpd5tRCsR3ekB3hyyNU9fGrTdqNT5ZNvv4BsA2TcQlignsZyVcw== + +xmldoc@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.3.0.tgz#7823225b096c74036347c9ec5924d06b6a3cebab" + integrity sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng== + dependencies: + sax "^1.2.4" + +xtend@^4.0.2, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 249eb8b5325234950ff91752d0ec404ca7ff8ebd Mon Sep 17 00:00:00 2001 From: Antonio <34042064+Desvelao@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:59:50 +0200 Subject: [PATCH 21/46] Remove unused embedded jquery-ui (#5592) * fix: remove unused embedded jquery-ui dependency * changelog: add pull request entry --------- Co-authored-by: Federico Rodriguez --- .eslintrc.json | 3 +- CHANGELOG.md | 1 + plugins/main/public/app.js | 54 +- plugins/main/public/utils/jquery-ui.js | 12675 ----------------------- 4 files changed, 32 insertions(+), 12701 deletions(-) delete mode 100644 plugins/main/public/utils/jquery-ui.js diff --git a/.eslintrc.json b/.eslintrc.json index 585dfa8951..7bdaeec615 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -39,8 +39,7 @@ "ignorePatterns": [ "node_modules/", "public/utils/codemirror/", - "public/kibana-integrations/", - "public/utils/jquery-ui.js" + "public/kibana-integrations/" ], "rules": { "filenames-simple/naming-convention": "error", diff --git a/CHANGELOG.md b/CHANGELOG.md index fea52d91a5..ec9d90f88e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Removed pretty parameter from cron job requests. [#5532](https://github.com/wazuh/wazuh-kibana-app/pull/5532) - Removed unnecessary requests in `Management/Status` section. [#5528](https://github.com/wazuh/wazuh-kibana-app/pull/5528) - Removed obsolete code that caused duplicate requests to the api in `Management`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) +- Removed unused embedded jquery-ui [#5592](https://github.com/wazuh/wazuh-kibana-app/pull/5592) ## Wazuh v4.5.1 - OpenSearch Dashboards 2.6.0 - Revision 01 diff --git a/plugins/main/public/app.js b/plugins/main/public/app.js index e1c8441bb6..c497f9d8d9 100644 --- a/plugins/main/public/app.js +++ b/plugins/main/public/app.js @@ -29,8 +29,6 @@ import './utils/fontawesome/scss/font-awesome.scss'; // Dev tools import './utils/codemirror'; -import './utils/jquery-ui'; - // Material import 'angular-material/angular-material.css'; import 'angular-aria/angular-aria'; @@ -60,7 +58,9 @@ const app = getAngularModule(); app.config([ '$compileProvider', function ($compileProvider) { - $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|data|blob):/); + $compileProvider.aHrefSanitizationWhitelist( + /^\s*(https?|ftp|mailto|data|blob):/, + ); }, ]); @@ -79,7 +79,7 @@ app.run([ // Set currentSecurity platform in Redux when app starts. checkCurrentSecurityPlatform() - .then((item) => { + .then(item => { store.dispatch(updateCurrentPlatform(item)); }) .catch(() => {}); @@ -96,7 +96,7 @@ app.run([ * Set trigger for logout */ app.run(function ($rootElement) { - $rootElement.append(` + $rootElement.append(`
@@ -109,23 +109,29 @@ app.run(function ($rootElement) { addHelpMenuToAppChrome(); // Bind deleteExistentToken on Log out component. - $('.euiHeaderSectionItem__button, .euiHeaderSectionItemButton').on('mouseleave', function () { - // opendistro - $('button:contains(Log out)').on('click', function () { - WzAuthentication.deleteExistentToken(); - }); - // x-pack - $('a:contains(Log out)').on('click', function (event) { - // Override href's behaviour and navigate programatically - // to the logout path once the token has been deleted. - event.preventDefault(); - WzAuthentication.deleteExistentToken() - .catch((err) => { - console.error('[ERROR] - User token could not be deprecated - ', err); - }) - .finally(() => { - window.location = event.currentTarget.href; - }); - }); - }); + $('.euiHeaderSectionItem__button, .euiHeaderSectionItemButton').on( + 'mouseleave', + function () { + // opendistro + $('button:contains(Log out)').on('click', function () { + WzAuthentication.deleteExistentToken(); + }); + // x-pack + $('a:contains(Log out)').on('click', function (event) { + // Override href's behaviour and navigate programatically + // to the logout path once the token has been deleted. + event.preventDefault(); + WzAuthentication.deleteExistentToken() + .catch(err => { + console.error( + '[ERROR] - User token could not be deprecated - ', + err, + ); + }) + .finally(() => { + window.location = event.currentTarget.href; + }); + }); + }, + ); }); diff --git a/plugins/main/public/utils/jquery-ui.js b/plugins/main/public/utils/jquery-ui.js deleted file mode 100644 index a5c9d985eb..0000000000 --- a/plugins/main/public/utils/jquery-ui.js +++ /dev/null @@ -1,12675 +0,0 @@ -/*! jQuery UI - v1.12.1 - 2016-09-14 - * http://jqueryui.com - * Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js - * Copyright jQuery Foundation and other contributors; Licensed MIT */ - -(function(t) { - 'function' == typeof define && define.amd ? define(['jquery'], t) : t(jQuery); -})(function(t) { - function e(t) { - for (var e = t.css('visibility'); 'inherit' === e; ) - (t = t.parent()), (e = t.css('visibility')); - return 'hidden' !== e; - } - function i(t) { - for (var e, i; t.length && t[0] !== document; ) { - if ( - ((e = t.css('position')), - ('absolute' === e || 'relative' === e || 'fixed' === e) && - ((i = parseInt(t.css('zIndex'), 10)), !isNaN(i) && 0 !== i)) - ) - return i; - t = t.parent(); - } - return 0; - } - function s() { - (this._curInst = null), - (this._keyEvent = !1), - (this._disabledInputs = []), - (this._datepickerShowing = !1), - (this._inDialog = !1), - (this._mainDivId = 'ui-datepicker-div'), - (this._inlineClass = 'ui-datepicker-inline'), - (this._appendClass = 'ui-datepicker-append'), - (this._triggerClass = 'ui-datepicker-trigger'), - (this._dialogClass = 'ui-datepicker-dialog'), - (this._disableClass = 'ui-datepicker-disabled'), - (this._unselectableClass = 'ui-datepicker-unselectable'), - (this._currentClass = 'ui-datepicker-current-day'), - (this._dayOverClass = 'ui-datepicker-days-cell-over'), - (this.regional = []), - (this.regional[''] = { - closeText: 'Done', - prevText: 'Prev', - nextText: 'Next', - currentText: 'Today', - monthNames: [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December' - ], - monthNamesShort: [ - 'Jan', - 'Feb', - 'Mar', - 'Apr', - 'May', - 'Jun', - 'Jul', - 'Aug', - 'Sep', - 'Oct', - 'Nov', - 'Dec' - ], - dayNames: [ - 'Sunday', - 'Monday', - 'Tuesday', - 'Wednesday', - 'Thursday', - 'Friday', - 'Saturday' - ], - dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], - dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], - weekHeader: 'Wk', - dateFormat: 'mm/dd/yy', - firstDay: 0, - isRTL: !1, - showMonthAfterYear: !1, - yearSuffix: '' - }), - (this._defaults = { - showOn: 'focus', - showAnim: 'fadeIn', - showOptions: {}, - defaultDate: null, - appendText: '', - buttonText: '...', - buttonImage: '', - buttonImageOnly: !1, - hideIfNoPrevNext: !1, - navigationAsDateFormat: !1, - gotoCurrent: !1, - changeMonth: !1, - changeYear: !1, - yearRange: 'c-10:c+10', - showOtherMonths: !1, - selectOtherMonths: !1, - showWeek: !1, - calculateWeek: this.iso8601Week, - shortYearCutoff: '+10', - minDate: null, - maxDate: null, - duration: 'fast', - beforeShowDay: null, - beforeShow: null, - onSelect: null, - onChangeMonthYear: null, - onClose: null, - numberOfMonths: 1, - showCurrentAtPos: 0, - stepMonths: 1, - stepBigMonths: 12, - altField: '', - altFormat: '', - constrainInput: !0, - showButtonPanel: !1, - autoSize: !1, - disabled: !1 - }), - t.extend(this._defaults, this.regional['']), - (this.regional.en = t.extend(!0, {}, this.regional[''])), - (this.regional['en-US'] = t.extend(!0, {}, this.regional.en)), - (this.dpDiv = n( - t( - "
" - ) - )); - } - function n(e) { - var i = - 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a'; - return e - .on('mouseout', i, function() { - t(this).removeClass('ui-state-hover'), - -1 !== this.className.indexOf('ui-datepicker-prev') && - t(this).removeClass('ui-datepicker-prev-hover'), - -1 !== this.className.indexOf('ui-datepicker-next') && - t(this).removeClass('ui-datepicker-next-hover'); - }) - .on('mouseover', i, o); - } - function o() { - t.datepicker._isDisabledDatepicker( - m.inline ? m.dpDiv.parent()[0] : m.input[0] - ) || - (t(this) - .parents('.ui-datepicker-calendar') - .find('a') - .removeClass('ui-state-hover'), - t(this).addClass('ui-state-hover'), - -1 !== this.className.indexOf('ui-datepicker-prev') && - t(this).addClass('ui-datepicker-prev-hover'), - -1 !== this.className.indexOf('ui-datepicker-next') && - t(this).addClass('ui-datepicker-next-hover')); - } - function a(e, i) { - t.extend(e, i); - for (var s in i) null == i[s] && (e[s] = i[s]); - return e; - } - function r(t) { - return function() { - var e = this.element.val(); - t.apply(this, arguments), - this._refresh(), - e !== this.element.val() && this._trigger('change'); - }; - } - (t.ui = t.ui || {}), (t.ui.version = '1.12.1'); - var h = 0, - l = Array.prototype.slice; - (t.cleanData = (function(e) { - return function(i) { - var s, n, o; - for (o = 0; null != (n = i[o]); o++) - try { - (s = t._data(n, 'events')), - s && s.remove && t(n).triggerHandler('remove'); - } catch (a) {} - e(i); - }; - })(t.cleanData)), - (t.widget = function(e, i, s) { - var n, - o, - a, - r = {}, - h = e.split('.')[0]; - e = e.split('.')[1]; - var l = h + '-' + e; - return ( - s || ((s = i), (i = t.Widget)), - t.isArray(s) && (s = t.extend.apply(null, [{}].concat(s))), - (t.expr[':'][l.toLowerCase()] = function(e) { - return !!t.data(e, l); - }), - (t[h] = t[h] || {}), - (n = t[h][e]), - (o = t[h][e] = function(t, e) { - return this._createWidget - ? (arguments.length && this._createWidget(t, e), void 0) - : new o(t, e); - }), - t.extend(o, n, { - version: s.version, - _proto: t.extend({}, s), - _childConstructors: [] - }), - (a = new i()), - (a.options = t.widget.extend({}, a.options)), - t.each(s, function(e, s) { - return t.isFunction(s) - ? ((r[e] = (function() { - function t() { - return i.prototype[e].apply(this, arguments); - } - function n(t) { - return i.prototype[e].apply(this, t); - } - return function() { - var e, - i = this._super, - o = this._superApply; - return ( - (this._super = t), - (this._superApply = n), - (e = s.apply(this, arguments)), - (this._super = i), - (this._superApply = o), - e - ); - }; - })()), - void 0) - : ((r[e] = s), void 0); - }), - (o.prototype = t.widget.extend( - a, - { widgetEventPrefix: n ? a.widgetEventPrefix || e : e }, - r, - { constructor: o, namespace: h, widgetName: e, widgetFullName: l } - )), - n - ? (t.each(n._childConstructors, function(e, i) { - var s = i.prototype; - t.widget(s.namespace + '.' + s.widgetName, o, i._proto); - }), - delete n._childConstructors) - : i._childConstructors.push(o), - t.widget.bridge(e, o), - o - ); - }), - (t.widget.extend = function(e) { - for (var i, s, n = l.call(arguments, 1), o = 0, a = n.length; a > o; o++) - for (i in n[o]) - (s = n[o][i]), - n[o].hasOwnProperty(i) && - void 0 !== s && - (e[i] = t.isPlainObject(s) - ? t.isPlainObject(e[i]) - ? t.widget.extend({}, e[i], s) - : t.widget.extend({}, s) - : s); - return e; - }), - (t.widget.bridge = function(e, i) { - var s = i.prototype.widgetFullName || e; - t.fn[e] = function(n) { - var o = 'string' == typeof n, - a = l.call(arguments, 1), - r = this; - return ( - o - ? this.length || 'instance' !== n - ? this.each(function() { - var i, - o = t.data(this, s); - return 'instance' === n - ? ((r = o), !1) - : o - ? t.isFunction(o[n]) && '_' !== n.charAt(0) - ? ((i = o[n].apply(o, a)), - i !== o && void 0 !== i - ? ((r = i && i.jquery ? r.pushStack(i.get()) : i), !1) - : void 0) - : t.error( - "no such method '" + - n + - "' for " + - e + - ' widget instance' - ) - : t.error( - 'cannot call methods on ' + - e + - ' prior to initialization; ' + - "attempted to call method '" + - n + - "'" - ); - }) - : (r = void 0) - : (a.length && (n = t.widget.extend.apply(null, [n].concat(a))), - this.each(function() { - var e = t.data(this, s); - e - ? (e.option(n || {}), e._init && e._init()) - : t.data(this, s, new i(n, this)); - })), - r - ); - }; - }), - (t.Widget = function() {}), - (t.Widget._childConstructors = []), - (t.Widget.prototype = { - widgetName: 'widget', - widgetEventPrefix: '', - defaultElement: '
', - options: { classes: {}, disabled: !1, create: null }, - _createWidget: function(e, i) { - (i = t(i || this.defaultElement || this)[0]), - (this.element = t(i)), - (this.uuid = h++), - (this.eventNamespace = '.' + this.widgetName + this.uuid), - (this.bindings = t()), - (this.hoverable = t()), - (this.focusable = t()), - (this.classesElementLookup = {}), - i !== this && - (t.data(i, this.widgetFullName, this), - this._on(!0, this.element, { - remove: function(t) { - t.target === i && this.destroy(); - } - }), - (this.document = t(i.style ? i.ownerDocument : i.document || i)), - (this.window = t( - this.document[0].defaultView || this.document[0].parentWindow - ))), - (this.options = t.widget.extend( - {}, - this.options, - this._getCreateOptions(), - e - )), - this._create(), - this.options.disabled && - this._setOptionDisabled(this.options.disabled), - this._trigger('create', null, this._getCreateEventData()), - this._init(); - }, - _getCreateOptions: function() { - return {}; - }, - _getCreateEventData: t.noop, - _create: t.noop, - _init: t.noop, - destroy: function() { - var e = this; - this._destroy(), - t.each(this.classesElementLookup, function(t, i) { - e._removeClass(i, t); - }), - this.element.off(this.eventNamespace).removeData(this.widgetFullName), - this.widget() - .off(this.eventNamespace) - .removeAttr('aria-disabled'), - this.bindings.off(this.eventNamespace); - }, - _destroy: t.noop, - widget: function() { - return this.element; - }, - option: function(e, i) { - var s, - n, - o, - a = e; - if (0 === arguments.length) return t.widget.extend({}, this.options); - if ('string' == typeof e) - if (((a = {}), (s = e.split('.')), (e = s.shift()), s.length)) { - for ( - n = a[e] = t.widget.extend({}, this.options[e]), o = 0; - s.length - 1 > o; - o++ - ) - (n[s[o]] = n[s[o]] || {}), (n = n[s[o]]); - if (((e = s.pop()), 1 === arguments.length)) - return void 0 === n[e] ? null : n[e]; - n[e] = i; - } else { - if (1 === arguments.length) - return void 0 === this.options[e] ? null : this.options[e]; - a[e] = i; - } - return this._setOptions(a), this; - }, - _setOptions: function(t) { - var e; - for (e in t) this._setOption(e, t[e]); - return this; - }, - _setOption: function(t, e) { - return ( - 'classes' === t && this._setOptionClasses(e), - (this.options[t] = e), - 'disabled' === t && this._setOptionDisabled(e), - this - ); - }, - _setOptionClasses: function(e) { - var i, s, n; - for (i in e) - (n = this.classesElementLookup[i]), - e[i] !== this.options.classes[i] && - n && - n.length && - ((s = t(n.get())), - this._removeClass(n, i), - s.addClass( - this._classes({ element: s, keys: i, classes: e, add: !0 }) - )); - }, - _setOptionDisabled: function(t) { - this._toggleClass( - this.widget(), - this.widgetFullName + '-disabled', - null, - !!t - ), - t && - (this._removeClass(this.hoverable, null, 'ui-state-hover'), - this._removeClass(this.focusable, null, 'ui-state-focus')); - }, - enable: function() { - return this._setOptions({ disabled: !1 }); - }, - disable: function() { - return this._setOptions({ disabled: !0 }); - }, - _classes: function(e) { - function i(i, o) { - var a, r; - for (r = 0; i.length > r; r++) - (a = n.classesElementLookup[i[r]] || t()), - (a = e.add - ? t(t.unique(a.get().concat(e.element.get()))) - : t(a.not(e.element).get())), - (n.classesElementLookup[i[r]] = a), - s.push(i[r]), - o && e.classes[i[r]] && s.push(e.classes[i[r]]); - } - var s = [], - n = this; - return ( - (e = t.extend( - { element: this.element, classes: this.options.classes || {} }, - e - )), - this._on(e.element, { remove: '_untrackClassesElement' }), - e.keys && i(e.keys.match(/\S+/g) || [], !0), - e.extra && i(e.extra.match(/\S+/g) || []), - s.join(' ') - ); - }, - _untrackClassesElement: function(e) { - var i = this; - t.each(i.classesElementLookup, function(s, n) { - -1 !== t.inArray(e.target, n) && - (i.classesElementLookup[s] = t(n.not(e.target).get())); - }); - }, - _removeClass: function(t, e, i) { - return this._toggleClass(t, e, i, !1); - }, - _addClass: function(t, e, i) { - return this._toggleClass(t, e, i, !0); - }, - _toggleClass: function(t, e, i, s) { - s = 'boolean' == typeof s ? s : i; - var n = 'string' == typeof t || null === t, - o = { - extra: n ? e : i, - keys: n ? t : e, - element: n ? this.element : t, - add: s - }; - return o.element.toggleClass(this._classes(o), s), this; - }, - _on: function(e, i, s) { - var n, - o = this; - 'boolean' != typeof e && ((s = i), (i = e), (e = !1)), - s - ? ((i = n = t(i)), (this.bindings = this.bindings.add(i))) - : ((s = i), (i = this.element), (n = this.widget())), - t.each(s, function(s, a) { - function r() { - return e || - (o.options.disabled !== !0 && - !t(this).hasClass('ui-state-disabled')) - ? ('string' == typeof a ? o[a] : a).apply(o, arguments) - : void 0; - } - 'string' != typeof a && - (r.guid = a.guid = a.guid || r.guid || t.guid++); - var h = s.match(/^([\w:-]*)\s*(.*)$/), - l = h[1] + o.eventNamespace, - c = h[2]; - c ? n.on(l, c, r) : i.on(l, r); - }); - }, - _off: function(e, i) { - (i = - (i || '').split(' ').join(this.eventNamespace + ' ') + - this.eventNamespace), - e.off(i).off(i), - (this.bindings = t(this.bindings.not(e).get())), - (this.focusable = t(this.focusable.not(e).get())), - (this.hoverable = t(this.hoverable.not(e).get())); - }, - _delay: function(t, e) { - function i() { - return ('string' == typeof t ? s[t] : t).apply(s, arguments); - } - var s = this; - return setTimeout(i, e || 0); - }, - _hoverable: function(e) { - (this.hoverable = this.hoverable.add(e)), - this._on(e, { - mouseenter: function(e) { - this._addClass(t(e.currentTarget), null, 'ui-state-hover'); - }, - mouseleave: function(e) { - this._removeClass(t(e.currentTarget), null, 'ui-state-hover'); - } - }); - }, - _focusable: function(e) { - (this.focusable = this.focusable.add(e)), - this._on(e, { - focusin: function(e) { - this._addClass(t(e.currentTarget), null, 'ui-state-focus'); - }, - focusout: function(e) { - this._removeClass(t(e.currentTarget), null, 'ui-state-focus'); - } - }); - }, - _trigger: function(e, i, s) { - var n, - o, - a = this.options[e]; - if ( - ((s = s || {}), - (i = t.Event(i)), - (i.type = (e === this.widgetEventPrefix - ? e - : this.widgetEventPrefix + e - ).toLowerCase()), - (i.target = this.element[0]), - (o = i.originalEvent)) - ) - for (n in o) n in i || (i[n] = o[n]); - return ( - this.element.trigger(i, s), - !( - (t.isFunction(a) && - a.apply(this.element[0], [i].concat(s)) === !1) || - i.isDefaultPrevented() - ) - ); - } - }), - t.each({ show: 'fadeIn', hide: 'fadeOut' }, function(e, i) { - t.Widget.prototype['_' + e] = function(s, n, o) { - 'string' == typeof n && (n = { effect: n }); - var a, - r = n ? (n === !0 || 'number' == typeof n ? i : n.effect || i) : e; - (n = n || {}), - 'number' == typeof n && (n = { duration: n }), - (a = !t.isEmptyObject(n)), - (n.complete = o), - n.delay && s.delay(n.delay), - a && t.effects && t.effects.effect[r] - ? s[e](n) - : r !== e && s[r] - ? s[r](n.duration, n.easing, o) - : s.queue(function(i) { - t(this)[e](), o && o.call(s[0]), i(); - }); - }; - }), - t.widget, - (function() { - function e(t, e, i) { - return [ - parseFloat(t[0]) * (u.test(t[0]) ? e / 100 : 1), - parseFloat(t[1]) * (u.test(t[1]) ? i / 100 : 1) - ]; - } - function i(e, i) { - return parseInt(t.css(e, i), 10) || 0; - } - function s(e) { - var i = e[0]; - return 9 === i.nodeType - ? { - width: e.width(), - height: e.height(), - offset: { top: 0, left: 0 } - } - : t.isWindow(i) - ? { - width: e.width(), - height: e.height(), - offset: { top: e.scrollTop(), left: e.scrollLeft() } - } - : i.preventDefault - ? { width: 0, height: 0, offset: { top: i.pageY, left: i.pageX } } - : { - width: e.outerWidth(), - height: e.outerHeight(), - offset: e.offset() - }; - } - var n, - o = Math.max, - a = Math.abs, - r = /left|center|right/, - h = /top|center|bottom/, - l = /[\+\-]\d+(\.[\d]+)?%?/, - c = /^\w+/, - u = /%$/, - d = t.fn.position; - (t.position = { - scrollbarWidth: function() { - if (void 0 !== n) return n; - var e, - i, - s = t( - "
" - ), - o = s.children()[0]; - return ( - t('body').append(s), - (e = o.offsetWidth), - s.css('overflow', 'scroll'), - (i = o.offsetWidth), - e === i && (i = s[0].clientWidth), - s.remove(), - (n = e - i) - ); - }, - getScrollInfo: function(e) { - var i = e.isWindow || e.isDocument ? '' : e.element.css('overflow-x'), - s = e.isWindow || e.isDocument ? '' : e.element.css('overflow-y'), - n = - 'scroll' === i || - ('auto' === i && e.width < e.element[0].scrollWidth), - o = - 'scroll' === s || - ('auto' === s && e.height < e.element[0].scrollHeight); - return { - width: o ? t.position.scrollbarWidth() : 0, - height: n ? t.position.scrollbarWidth() : 0 - }; - }, - getWithinInfo: function(e) { - var i = t(e || window), - s = t.isWindow(i[0]), - n = !!i[0] && 9 === i[0].nodeType, - o = !s && !n; - return { - element: i, - isWindow: s, - isDocument: n, - offset: o ? t(e).offset() : { left: 0, top: 0 }, - scrollLeft: i.scrollLeft(), - scrollTop: i.scrollTop(), - width: i.outerWidth(), - height: i.outerHeight() - }; - } - }), - (t.fn.position = function(n) { - if (!n || !n.of) return d.apply(this, arguments); - n = t.extend({}, n); - var u, - p, - f, - g, - m, - _, - v = t(n.of), - b = t.position.getWithinInfo(n.within), - y = t.position.getScrollInfo(b), - w = (n.collision || 'flip').split(' '), - k = {}; - return ( - (_ = s(v)), - v[0].preventDefault && (n.at = 'left top'), - (p = _.width), - (f = _.height), - (g = _.offset), - (m = t.extend({}, g)), - t.each(['my', 'at'], function() { - var t, - e, - i = (n[this] || '').split(' '); - 1 === i.length && - (i = r.test(i[0]) - ? i.concat(['center']) - : h.test(i[0]) - ? ['center'].concat(i) - : ['center', 'center']), - (i[0] = r.test(i[0]) ? i[0] : 'center'), - (i[1] = h.test(i[1]) ? i[1] : 'center'), - (t = l.exec(i[0])), - (e = l.exec(i[1])), - (k[this] = [t ? t[0] : 0, e ? e[0] : 0]), - (n[this] = [c.exec(i[0])[0], c.exec(i[1])[0]]); - }), - 1 === w.length && (w[1] = w[0]), - 'right' === n.at[0] - ? (m.left += p) - : 'center' === n.at[0] && (m.left += p / 2), - 'bottom' === n.at[1] - ? (m.top += f) - : 'center' === n.at[1] && (m.top += f / 2), - (u = e(k.at, p, f)), - (m.left += u[0]), - (m.top += u[1]), - this.each(function() { - var s, - r, - h = t(this), - l = h.outerWidth(), - c = h.outerHeight(), - d = i(this, 'marginLeft'), - _ = i(this, 'marginTop'), - x = l + d + i(this, 'marginRight') + y.width, - C = c + _ + i(this, 'marginBottom') + y.height, - D = t.extend({}, m), - I = e(k.my, h.outerWidth(), h.outerHeight()); - 'right' === n.my[0] - ? (D.left -= l) - : 'center' === n.my[0] && (D.left -= l / 2), - 'bottom' === n.my[1] - ? (D.top -= c) - : 'center' === n.my[1] && (D.top -= c / 2), - (D.left += I[0]), - (D.top += I[1]), - (s = { marginLeft: d, marginTop: _ }), - t.each(['left', 'top'], function(e, i) { - t.ui.position[w[e]] && - t.ui.position[w[e]][i](D, { - targetWidth: p, - targetHeight: f, - elemWidth: l, - elemHeight: c, - collisionPosition: s, - collisionWidth: x, - collisionHeight: C, - offset: [u[0] + I[0], u[1] + I[1]], - my: n.my, - at: n.at, - within: b, - elem: h - }); - }), - n.using && - (r = function(t) { - var e = g.left - D.left, - i = e + p - l, - s = g.top - D.top, - r = s + f - c, - u = { - target: { - element: v, - left: g.left, - top: g.top, - width: p, - height: f - }, - element: { - element: h, - left: D.left, - top: D.top, - width: l, - height: c - }, - horizontal: 0 > i ? 'left' : e > 0 ? 'right' : 'center', - vertical: 0 > r ? 'top' : s > 0 ? 'bottom' : 'middle' - }; - l > p && p > a(e + i) && (u.horizontal = 'center'), - c > f && f > a(s + r) && (u.vertical = 'middle'), - (u.important = - o(a(e), a(i)) > o(a(s), a(r)) - ? 'horizontal' - : 'vertical'), - n.using.call(this, t, u); - }), - h.offset(t.extend(D, { using: r })); - }) - ); - }), - (t.ui.position = { - fit: { - left: function(t, e) { - var i, - s = e.within, - n = s.isWindow ? s.scrollLeft : s.offset.left, - a = s.width, - r = t.left - e.collisionPosition.marginLeft, - h = n - r, - l = r + e.collisionWidth - a - n; - e.collisionWidth > a - ? h > 0 && 0 >= l - ? ((i = t.left + h + e.collisionWidth - a - n), - (t.left += h - i)) - : (t.left = - l > 0 && 0 >= h - ? n - : h > l - ? n + a - e.collisionWidth - : n) - : h > 0 - ? (t.left += h) - : l > 0 - ? (t.left -= l) - : (t.left = o(t.left - r, t.left)); - }, - top: function(t, e) { - var i, - s = e.within, - n = s.isWindow ? s.scrollTop : s.offset.top, - a = e.within.height, - r = t.top - e.collisionPosition.marginTop, - h = n - r, - l = r + e.collisionHeight - a - n; - e.collisionHeight > a - ? h > 0 && 0 >= l - ? ((i = t.top + h + e.collisionHeight - a - n), - (t.top += h - i)) - : (t.top = - l > 0 && 0 >= h - ? n - : h > l - ? n + a - e.collisionHeight - : n) - : h > 0 - ? (t.top += h) - : l > 0 - ? (t.top -= l) - : (t.top = o(t.top - r, t.top)); - } - }, - flip: { - left: function(t, e) { - var i, - s, - n = e.within, - o = n.offset.left + n.scrollLeft, - r = n.width, - h = n.isWindow ? n.scrollLeft : n.offset.left, - l = t.left - e.collisionPosition.marginLeft, - c = l - h, - u = l + e.collisionWidth - r - h, - d = - 'left' === e.my[0] - ? -e.elemWidth - : 'right' === e.my[0] - ? e.elemWidth - : 0, - p = - 'left' === e.at[0] - ? e.targetWidth - : 'right' === e.at[0] - ? -e.targetWidth - : 0, - f = -2 * e.offset[0]; - 0 > c - ? ((i = t.left + d + p + f + e.collisionWidth - r - o), - (0 > i || a(c) > i) && (t.left += d + p + f)) - : u > 0 && - ((s = - t.left - e.collisionPosition.marginLeft + d + p + f - h), - (s > 0 || u > a(s)) && (t.left += d + p + f)); - }, - top: function(t, e) { - var i, - s, - n = e.within, - o = n.offset.top + n.scrollTop, - r = n.height, - h = n.isWindow ? n.scrollTop : n.offset.top, - l = t.top - e.collisionPosition.marginTop, - c = l - h, - u = l + e.collisionHeight - r - h, - d = 'top' === e.my[1], - p = d ? -e.elemHeight : 'bottom' === e.my[1] ? e.elemHeight : 0, - f = - 'top' === e.at[1] - ? e.targetHeight - : 'bottom' === e.at[1] - ? -e.targetHeight - : 0, - g = -2 * e.offset[1]; - 0 > c - ? ((s = t.top + p + f + g + e.collisionHeight - r - o), - (0 > s || a(c) > s) && (t.top += p + f + g)) - : u > 0 && - ((i = t.top - e.collisionPosition.marginTop + p + f + g - h), - (i > 0 || u > a(i)) && (t.top += p + f + g)); - } - }, - flipfit: { - left: function() { - t.ui.position.flip.left.apply(this, arguments), - t.ui.position.fit.left.apply(this, arguments); - }, - top: function() { - t.ui.position.flip.top.apply(this, arguments), - t.ui.position.fit.top.apply(this, arguments); - } - } - }); - })(), - t.ui.position, - t.extend(t.expr[':'], { - data: t.expr.createPseudo - ? t.expr.createPseudo(function(e) { - return function(i) { - return !!t.data(i, e); - }; - }) - : function(e, i, s) { - return !!t.data(e, s[3]); - } - }), - t.fn.extend({ - disableSelection: (function() { - var t = - 'onselectstart' in document.createElement('div') - ? 'selectstart' - : 'mousedown'; - return function() { - return this.on(t + '.ui-disableSelection', function(t) { - t.preventDefault(); - }); - }; - })(), - enableSelection: function() { - return this.off('.ui-disableSelection'); - } - }); - var c = 'ui-effects-', - u = 'ui-effects-style', - d = 'ui-effects-animated', - p = t; - (t.effects = { effect: {} }), - (function(t, e) { - function i(t, e, i) { - var s = u[e.type] || {}; - return null == t - ? i || !e.def - ? null - : e.def - : ((t = s.floor ? ~~t : parseFloat(t)), - isNaN(t) - ? e.def - : s.mod - ? (t + s.mod) % s.mod - : 0 > t - ? 0 - : t > s.max - ? s.max - : t); - } - function s(i) { - var s = l(), - n = (s._rgba = []); - return ( - (i = i.toLowerCase()), - f(h, function(t, o) { - var a, - r = o.re.exec(i), - h = r && o.parse(r), - l = o.space || 'rgba'; - return h - ? ((a = s[l](h)), - (s[c[l].cache] = a[c[l].cache]), - (n = s._rgba = a._rgba), - !1) - : e; - }), - n.length - ? ('0,0,0,0' === n.join() && t.extend(n, o.transparent), s) - : o[i] - ); - } - function n(t, e, i) { - return ( - (i = (i + 1) % 1), - 1 > 6 * i - ? t + 6 * (e - t) * i - : 1 > 2 * i - ? e - : 2 > 3 * i - ? t + 6 * (e - t) * (2 / 3 - i) - : t - ); - } - var o, - a = - 'backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor', - r = /^([\-+])=\s*(\d+\.?\d*)/, - h = [ - { - re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, - parse: function(t) { - return [t[1], t[2], t[3], t[4]]; - } - }, - { - re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, - parse: function(t) { - return [2.55 * t[1], 2.55 * t[2], 2.55 * t[3], t[4]]; - } - }, - { - re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, - parse: function(t) { - return [ - parseInt(t[1], 16), - parseInt(t[2], 16), - parseInt(t[3], 16) - ]; - } - }, - { - re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, - parse: function(t) { - return [ - parseInt(t[1] + t[1], 16), - parseInt(t[2] + t[2], 16), - parseInt(t[3] + t[3], 16) - ]; - } - }, - { - re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, - space: 'hsla', - parse: function(t) { - return [t[1], t[2] / 100, t[3] / 100, t[4]]; - } - } - ], - l = (t.Color = function(e, i, s, n) { - return new t.Color.fn.parse(e, i, s, n); - }), - c = { - rgba: { - props: { - red: { idx: 0, type: 'byte' }, - green: { idx: 1, type: 'byte' }, - blue: { idx: 2, type: 'byte' } - } - }, - hsla: { - props: { - hue: { idx: 0, type: 'degrees' }, - saturation: { idx: 1, type: 'percent' }, - lightness: { idx: 2, type: 'percent' } - } - } - }, - u = { - byte: { floor: !0, max: 255 }, - percent: { max: 1 }, - degrees: { mod: 360, floor: !0 } - }, - d = (l.support = {}), - p = t('

')[0], - f = t.each; - (p.style.cssText = 'background-color:rgba(1,1,1,.5)'), - (d.rgba = p.style.backgroundColor.indexOf('rgba') > -1), - f(c, function(t, e) { - (e.cache = '_' + t), - (e.props.alpha = { idx: 3, type: 'percent', def: 1 }); - }), - (l.fn = t.extend(l.prototype, { - parse: function(n, a, r, h) { - if (n === e) return (this._rgba = [null, null, null, null]), this; - (n.jquery || n.nodeType) && ((n = t(n).css(a)), (a = e)); - var u = this, - d = t.type(n), - p = (this._rgba = []); - return ( - a !== e && ((n = [n, a, r, h]), (d = 'array')), - 'string' === d - ? this.parse(s(n) || o._default) - : 'array' === d - ? (f(c.rgba.props, function(t, e) { - p[e.idx] = i(n[e.idx], e); - }), - this) - : 'object' === d - ? (n instanceof l - ? f(c, function(t, e) { - n[e.cache] && (u[e.cache] = n[e.cache].slice()); - }) - : f(c, function(e, s) { - var o = s.cache; - f(s.props, function(t, e) { - if (!u[o] && s.to) { - if ('alpha' === t || null == n[t]) return; - u[o] = s.to(u._rgba); - } - u[o][e.idx] = i(n[t], e, !0); - }), - u[o] && - 0 > t.inArray(null, u[o].slice(0, 3)) && - ((u[o][3] = 1), s.from && (u._rgba = s.from(u[o]))); - }), - this) - : e - ); - }, - is: function(t) { - var i = l(t), - s = !0, - n = this; - return ( - f(c, function(t, o) { - var a, - r = i[o.cache]; - return ( - r && - ((a = n[o.cache] || (o.to && o.to(n._rgba)) || []), - f(o.props, function(t, i) { - return null != r[i.idx] ? (s = r[i.idx] === a[i.idx]) : e; - })), - s - ); - }), - s - ); - }, - _space: function() { - var t = [], - e = this; - return ( - f(c, function(i, s) { - e[s.cache] && t.push(i); - }), - t.pop() - ); - }, - transition: function(t, e) { - var s = l(t), - n = s._space(), - o = c[n], - a = 0 === this.alpha() ? l('transparent') : this, - r = a[o.cache] || o.to(a._rgba), - h = r.slice(); - return ( - (s = s[o.cache]), - f(o.props, function(t, n) { - var o = n.idx, - a = r[o], - l = s[o], - c = u[n.type] || {}; - null !== l && - (null === a - ? (h[o] = l) - : (c.mod && - (l - a > c.mod / 2 - ? (a += c.mod) - : a - l > c.mod / 2 && (a -= c.mod)), - (h[o] = i((l - a) * e + a, n)))); - }), - this[n](h) - ); - }, - blend: function(e) { - if (1 === this._rgba[3]) return this; - var i = this._rgba.slice(), - s = i.pop(), - n = l(e)._rgba; - return l( - t.map(i, function(t, e) { - return (1 - s) * n[e] + s * t; - }) - ); - }, - toRgbaString: function() { - var e = 'rgba(', - i = t.map(this._rgba, function(t, e) { - return null == t ? (e > 2 ? 1 : 0) : t; - }); - return 1 === i[3] && (i.pop(), (e = 'rgb(')), e + i.join() + ')'; - }, - toHslaString: function() { - var e = 'hsla(', - i = t.map(this.hsla(), function(t, e) { - return ( - null == t && (t = e > 2 ? 1 : 0), - e && 3 > e && (t = Math.round(100 * t) + '%'), - t - ); - }); - return 1 === i[3] && (i.pop(), (e = 'hsl(')), e + i.join() + ')'; - }, - toHexString: function(e) { - var i = this._rgba.slice(), - s = i.pop(); - return ( - e && i.push(~~(255 * s)), - '#' + - t - .map(i, function(t) { - return ( - (t = (t || 0).toString(16)), 1 === t.length ? '0' + t : t - ); - }) - .join('') - ); - }, - toString: function() { - return 0 === this._rgba[3] ? 'transparent' : this.toRgbaString(); - } - })), - (l.fn.parse.prototype = l.fn), - (c.hsla.to = function(t) { - if (null == t[0] || null == t[1] || null == t[2]) - return [null, null, null, t[3]]; - var e, - i, - s = t[0] / 255, - n = t[1] / 255, - o = t[2] / 255, - a = t[3], - r = Math.max(s, n, o), - h = Math.min(s, n, o), - l = r - h, - c = r + h, - u = 0.5 * c; - return ( - (e = - h === r - ? 0 - : s === r - ? (60 * (n - o)) / l + 360 - : n === r - ? (60 * (o - s)) / l + 120 - : (60 * (s - n)) / l + 240), - (i = 0 === l ? 0 : 0.5 >= u ? l / c : l / (2 - c)), - [Math.round(e) % 360, i, u, null == a ? 1 : a] - ); - }), - (c.hsla.from = function(t) { - if (null == t[0] || null == t[1] || null == t[2]) - return [null, null, null, t[3]]; - var e = t[0] / 360, - i = t[1], - s = t[2], - o = t[3], - a = 0.5 >= s ? s * (1 + i) : s + i - s * i, - r = 2 * s - a; - return [ - Math.round(255 * n(r, a, e + 1 / 3)), - Math.round(255 * n(r, a, e)), - Math.round(255 * n(r, a, e - 1 / 3)), - o - ]; - }), - f(c, function(s, n) { - var o = n.props, - a = n.cache, - h = n.to, - c = n.from; - (l.fn[s] = function(s) { - if ((h && !this[a] && (this[a] = h(this._rgba)), s === e)) - return this[a].slice(); - var n, - r = t.type(s), - u = 'array' === r || 'object' === r ? s : arguments, - d = this[a].slice(); - return ( - f(o, function(t, e) { - var s = u['object' === r ? t : e.idx]; - null == s && (s = d[e.idx]), (d[e.idx] = i(s, e)); - }), - c ? ((n = l(c(d))), (n[a] = d), n) : l(d) - ); - }), - f(o, function(e, i) { - l.fn[e] || - (l.fn[e] = function(n) { - var o, - a = t.type(n), - h = 'alpha' === e ? (this._hsla ? 'hsla' : 'rgba') : s, - l = this[h](), - c = l[i.idx]; - return 'undefined' === a - ? c - : ('function' === a && - ((n = n.call(this, c)), (a = t.type(n))), - null == n && i.empty - ? this - : ('string' === a && - ((o = r.exec(n)), - o && - (n = - c + - parseFloat(o[2]) * ('+' === o[1] ? 1 : -1))), - (l[i.idx] = n), - this[h](l))); - }); - }); - }), - (l.hook = function(e) { - var i = e.split(' '); - f(i, function(e, i) { - (t.cssHooks[i] = { - set: function(e, n) { - var o, - a, - r = ''; - if ( - 'transparent' !== n && - ('string' !== t.type(n) || (o = s(n))) - ) { - if (((n = l(o || n)), !d.rgba && 1 !== n._rgba[3])) { - for ( - a = 'backgroundColor' === i ? e.parentNode : e; - ('' === r || 'transparent' === r) && a && a.style; - - ) - try { - (r = t.css(a, 'backgroundColor')), (a = a.parentNode); - } catch (h) {} - n = n.blend(r && 'transparent' !== r ? r : '_default'); - } - n = n.toRgbaString(); - } - try { - e.style[i] = n; - } catch (h) {} - } - }), - (t.fx.step[i] = function(e) { - e.colorInit || - ((e.start = l(e.elem, i)), - (e.end = l(e.end)), - (e.colorInit = !0)), - t.cssHooks[i].set(e.elem, e.start.transition(e.end, e.pos)); - }); - }); - }), - l.hook(a), - (t.cssHooks.borderColor = { - expand: function(t) { - var e = {}; - return ( - f(['Top', 'Right', 'Bottom', 'Left'], function(i, s) { - e['border' + s + 'Color'] = t; - }), - e - ); - } - }), - (o = t.Color.names = { - aqua: '#00ffff', - black: '#000000', - blue: '#0000ff', - fuchsia: '#ff00ff', - gray: '#808080', - green: '#008000', - lime: '#00ff00', - maroon: '#800000', - navy: '#000080', - olive: '#808000', - purple: '#800080', - red: '#ff0000', - silver: '#c0c0c0', - teal: '#008080', - white: '#ffffff', - yellow: '#ffff00', - transparent: [null, null, null, 0], - _default: '#ffffff' - }); - })(p), - (function() { - function e(e) { - var i, - s, - n = e.ownerDocument.defaultView - ? e.ownerDocument.defaultView.getComputedStyle(e, null) - : e.currentStyle, - o = {}; - if (n && n.length && n[0] && n[n[0]]) - for (s = n.length; s--; ) - (i = n[s]), 'string' == typeof n[i] && (o[t.camelCase(i)] = n[i]); - else for (i in n) 'string' == typeof n[i] && (o[i] = n[i]); - return o; - } - function i(e, i) { - var s, - o, - a = {}; - for (s in i) - (o = i[s]), - e[s] !== o && - (n[s] || ((t.fx.step[s] || !isNaN(parseFloat(o))) && (a[s] = o))); - return a; - } - var s = ['add', 'remove', 'toggle'], - n = { - border: 1, - borderBottom: 1, - borderColor: 1, - borderLeft: 1, - borderRight: 1, - borderTop: 1, - borderWidth: 1, - margin: 1, - padding: 1 - }; - t.each( - [ - 'borderLeftStyle', - 'borderRightStyle', - 'borderBottomStyle', - 'borderTopStyle' - ], - function(e, i) { - t.fx.step[i] = function(t) { - (('none' !== t.end && !t.setAttr) || (1 === t.pos && !t.setAttr)) && - (p.style(t.elem, i, t.end), (t.setAttr = !0)); - }; - } - ), - t.fn.addBack || - (t.fn.addBack = function(t) { - return this.add( - null == t ? this.prevObject : this.prevObject.filter(t) - ); - }), - (t.effects.animateClass = function(n, o, a, r) { - var h = t.speed(o, a, r); - return this.queue(function() { - var o, - a = t(this), - r = a.attr('class') || '', - l = h.children ? a.find('*').addBack() : a; - (l = l.map(function() { - var i = t(this); - return { el: i, start: e(this) }; - })), - (o = function() { - t.each(s, function(t, e) { - n[e] && a[e + 'Class'](n[e]); - }); - }), - o(), - (l = l.map(function() { - return ( - (this.end = e(this.el[0])), - (this.diff = i(this.start, this.end)), - this - ); - })), - a.attr('class', r), - (l = l.map(function() { - var e = this, - i = t.Deferred(), - s = t.extend({}, h, { - queue: !1, - complete: function() { - i.resolve(e); - } - }); - return this.el.animate(this.diff, s), i.promise(); - })), - t.when.apply(t, l.get()).done(function() { - o(), - t.each(arguments, function() { - var e = this.el; - t.each(this.diff, function(t) { - e.css(t, ''); - }); - }), - h.complete.call(a[0]); - }); - }); - }), - t.fn.extend({ - addClass: (function(e) { - return function(i, s, n, o) { - return s - ? t.effects.animateClass.call(this, { add: i }, s, n, o) - : e.apply(this, arguments); - }; - })(t.fn.addClass), - removeClass: (function(e) { - return function(i, s, n, o) { - return arguments.length > 1 - ? t.effects.animateClass.call(this, { remove: i }, s, n, o) - : e.apply(this, arguments); - }; - })(t.fn.removeClass), - toggleClass: (function(e) { - return function(i, s, n, o, a) { - return 'boolean' == typeof s || void 0 === s - ? n - ? t.effects.animateClass.call( - this, - s ? { add: i } : { remove: i }, - n, - o, - a - ) - : e.apply(this, arguments) - : t.effects.animateClass.call(this, { toggle: i }, s, n, o); - }; - })(t.fn.toggleClass), - switchClass: function(e, i, s, n, o) { - return t.effects.animateClass.call( - this, - { add: i, remove: e }, - s, - n, - o - ); - } - }); - })(), - (function() { - function e(e, i, s, n) { - return ( - t.isPlainObject(e) && ((i = e), (e = e.effect)), - (e = { effect: e }), - null == i && (i = {}), - t.isFunction(i) && ((n = i), (s = null), (i = {})), - ('number' == typeof i || t.fx.speeds[i]) && - ((n = s), (s = i), (i = {})), - t.isFunction(s) && ((n = s), (s = null)), - i && t.extend(e, i), - (s = s || i.duration), - (e.duration = t.fx.off - ? 0 - : 'number' == typeof s - ? s - : s in t.fx.speeds - ? t.fx.speeds[s] - : t.fx.speeds._default), - (e.complete = n || i.complete), - e - ); - } - function i(e) { - return !e || 'number' == typeof e || t.fx.speeds[e] - ? !0 - : 'string' != typeof e || t.effects.effect[e] - ? t.isFunction(e) - ? !0 - : 'object' != typeof e || e.effect - ? !1 - : !0 - : !0; - } - function s(t, e) { - var i = e.outerWidth(), - s = e.outerHeight(), - n = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/, - o = n.exec(t) || ['', 0, i, s, 0]; - return { - top: parseFloat(o[1]) || 0, - right: 'auto' === o[2] ? i : parseFloat(o[2]), - bottom: 'auto' === o[3] ? s : parseFloat(o[3]), - left: parseFloat(o[4]) || 0 - }; - } - t.expr && - t.expr.filters && - t.expr.filters.animated && - (t.expr.filters.animated = (function(e) { - return function(i) { - return !!t(i).data(d) || e(i); - }; - })(t.expr.filters.animated)), - t.uiBackCompat !== !1 && - t.extend(t.effects, { - save: function(t, e) { - for (var i = 0, s = e.length; s > i; i++) - null !== e[i] && t.data(c + e[i], t[0].style[e[i]]); - }, - restore: function(t, e) { - for (var i, s = 0, n = e.length; n > s; s++) - null !== e[s] && ((i = t.data(c + e[s])), t.css(e[s], i)); - }, - setMode: function(t, e) { - return ( - 'toggle' === e && (e = t.is(':hidden') ? 'show' : 'hide'), e - ); - }, - createWrapper: function(e) { - if (e.parent().is('.ui-effects-wrapper')) return e.parent(); - var i = { - width: e.outerWidth(!0), - height: e.outerHeight(!0), - float: e.css('float') - }, - s = t('

') - .addClass('ui-effects-wrapper') - .css({ - fontSize: '100%', - background: 'transparent', - border: 'none', - margin: 0, - padding: 0 - }), - n = { width: e.width(), height: e.height() }, - o = document.activeElement; - try { - o.id; - } catch (a) { - o = document.body; - } - return ( - e.wrap(s), - (e[0] === o || t.contains(e[0], o)) && t(o).trigger('focus'), - (s = e.parent()), - 'static' === e.css('position') - ? (s.css({ position: 'relative' }), - e.css({ position: 'relative' })) - : (t.extend(i, { - position: e.css('position'), - zIndex: e.css('z-index') - }), - t.each(['top', 'left', 'bottom', 'right'], function(t, s) { - (i[s] = e.css(s)), - isNaN(parseInt(i[s], 10)) && (i[s] = 'auto'); - }), - e.css({ - position: 'relative', - top: 0, - left: 0, - right: 'auto', - bottom: 'auto' - })), - e.css(n), - s.css(i).show() - ); - }, - removeWrapper: function(e) { - var i = document.activeElement; - return ( - e.parent().is('.ui-effects-wrapper') && - (e.parent().replaceWith(e), - (e[0] === i || t.contains(e[0], i)) && t(i).trigger('focus')), - e - ); - } - }), - t.extend(t.effects, { - version: '1.12.1', - define: function(e, i, s) { - return ( - s || ((s = i), (i = 'effect')), - (t.effects.effect[e] = s), - (t.effects.effect[e].mode = i), - s - ); - }, - scaledDimensions: function(t, e, i) { - if (0 === e) - return { height: 0, width: 0, outerHeight: 0, outerWidth: 0 }; - var s = 'horizontal' !== i ? (e || 100) / 100 : 1, - n = 'vertical' !== i ? (e || 100) / 100 : 1; - return { - height: t.height() * n, - width: t.width() * s, - outerHeight: t.outerHeight() * n, - outerWidth: t.outerWidth() * s - }; - }, - clipToBox: function(t) { - return { - width: t.clip.right - t.clip.left, - height: t.clip.bottom - t.clip.top, - left: t.clip.left, - top: t.clip.top - }; - }, - unshift: function(t, e, i) { - var s = t.queue(); - e > 1 && s.splice.apply(s, [1, 0].concat(s.splice(e, i))), - t.dequeue(); - }, - saveStyle: function(t) { - t.data(u, t[0].style.cssText); - }, - restoreStyle: function(t) { - (t[0].style.cssText = t.data(u) || ''), t.removeData(u); - }, - mode: function(t, e) { - var i = t.is(':hidden'); - return ( - 'toggle' === e && (e = i ? 'show' : 'hide'), - (i ? 'hide' === e : 'show' === e) && (e = 'none'), - e - ); - }, - getBaseline: function(t, e) { - var i, s; - switch (t[0]) { - case 'top': - i = 0; - break; - case 'middle': - i = 0.5; - break; - case 'bottom': - i = 1; - break; - default: - i = t[0] / e.height; - } - switch (t[1]) { - case 'left': - s = 0; - break; - case 'center': - s = 0.5; - break; - case 'right': - s = 1; - break; - default: - s = t[1] / e.width; - } - return { x: s, y: i }; - }, - createPlaceholder: function(e) { - var i, - s = e.css('position'), - n = e.position(); - return ( - e - .css({ - marginTop: e.css('marginTop'), - marginBottom: e.css('marginBottom'), - marginLeft: e.css('marginLeft'), - marginRight: e.css('marginRight') - }) - .outerWidth(e.outerWidth()) - .outerHeight(e.outerHeight()), - /^(static|relative)/.test(s) && - ((s = 'absolute'), - (i = t('<' + e[0].nodeName + '>') - .insertAfter(e) - .css({ - display: /^(inline|ruby)/.test(e.css('display')) - ? 'inline-block' - : 'block', - visibility: 'hidden', - marginTop: e.css('marginTop'), - marginBottom: e.css('marginBottom'), - marginLeft: e.css('marginLeft'), - marginRight: e.css('marginRight'), - float: e.css('float') - }) - .outerWidth(e.outerWidth()) - .outerHeight(e.outerHeight()) - .addClass('ui-effects-placeholder')), - e.data(c + 'placeholder', i)), - e.css({ position: s, left: n.left, top: n.top }), - i - ); - }, - removePlaceholder: function(t) { - var e = c + 'placeholder', - i = t.data(e); - i && (i.remove(), t.removeData(e)); - }, - cleanUp: function(e) { - t.effects.restoreStyle(e), t.effects.removePlaceholder(e); - }, - setTransition: function(e, i, s, n) { - return ( - (n = n || {}), - t.each(i, function(t, i) { - var o = e.cssUnit(i); - o[0] > 0 && (n[i] = o[0] * s + o[1]); - }), - n - ); - } - }), - t.fn.extend({ - effect: function() { - function i(e) { - function i() { - r.removeData(d), - t.effects.cleanUp(r), - 'hide' === s.mode && r.hide(), - a(); - } - function a() { - t.isFunction(h) && h.call(r[0]), t.isFunction(e) && e(); - } - var r = t(this); - (s.mode = c.shift()), - t.uiBackCompat === !1 || o - ? 'none' === s.mode - ? (r[l](), a()) - : n.call(r[0], s, i) - : (r.is(':hidden') - ? 'hide' === l - : 'show' === l) - ? (r[l](), a()) - : n.call(r[0], s, a); - } - var s = e.apply(this, arguments), - n = t.effects.effect[s.effect], - o = n.mode, - a = s.queue, - r = a || 'fx', - h = s.complete, - l = s.mode, - c = [], - u = function(e) { - var i = t(this), - s = t.effects.mode(i, l) || o; - i.data(d, !0), - c.push(s), - o && ('show' === s || (s === o && 'hide' === s)) && i.show(), - (o && 'none' === s) || t.effects.saveStyle(i), - t.isFunction(e) && e(); - }; - return t.fx.off || !n - ? l - ? this[l](s.duration, h) - : this.each(function() { - h && h.call(this); - }) - : a === !1 - ? this.each(u).each(i) - : this.queue(r, u).queue(r, i); - }, - show: (function(t) { - return function(s) { - if (i(s)) return t.apply(this, arguments); - var n = e.apply(this, arguments); - return (n.mode = 'show'), this.effect.call(this, n); - }; - })(t.fn.show), - hide: (function(t) { - return function(s) { - if (i(s)) return t.apply(this, arguments); - var n = e.apply(this, arguments); - return (n.mode = 'hide'), this.effect.call(this, n); - }; - })(t.fn.hide), - toggle: (function(t) { - return function(s) { - if (i(s) || 'boolean' == typeof s) - return t.apply(this, arguments); - var n = e.apply(this, arguments); - return (n.mode = 'toggle'), this.effect.call(this, n); - }; - })(t.fn.toggle), - cssUnit: function(e) { - var i = this.css(e), - s = []; - return ( - t.each(['em', 'px', '%', 'pt'], function(t, e) { - i.indexOf(e) > 0 && (s = [parseFloat(i), e]); - }), - s - ); - }, - cssClip: function(t) { - return t - ? this.css( - 'clip', - 'rect(' + - t.top + - 'px ' + - t.right + - 'px ' + - t.bottom + - 'px ' + - t.left + - 'px)' - ) - : s(this.css('clip'), this); - }, - transfer: function(e, i) { - var s = t(this), - n = t(e.to), - o = 'fixed' === n.css('position'), - a = t('body'), - r = o ? a.scrollTop() : 0, - h = o ? a.scrollLeft() : 0, - l = n.offset(), - c = { - top: l.top - r, - left: l.left - h, - height: n.innerHeight(), - width: n.innerWidth() - }, - u = s.offset(), - d = t("
") - .appendTo('body') - .addClass(e.className) - .css({ - top: u.top - r, - left: u.left - h, - height: s.innerHeight(), - width: s.innerWidth(), - position: o ? 'fixed' : 'absolute' - }) - .animate(c, e.duration, e.easing, function() { - d.remove(), t.isFunction(i) && i(); - }); - } - }), - (t.fx.step.clip = function(e) { - e.clipInit || - ((e.start = t(e.elem).cssClip()), - 'string' == typeof e.end && (e.end = s(e.end, e.elem)), - (e.clipInit = !0)), - t(e.elem).cssClip({ - top: e.pos * (e.end.top - e.start.top) + e.start.top, - right: e.pos * (e.end.right - e.start.right) + e.start.right, - bottom: e.pos * (e.end.bottom - e.start.bottom) + e.start.bottom, - left: e.pos * (e.end.left - e.start.left) + e.start.left - }); - }); - })(), - (function() { - var e = {}; - t.each(['Quad', 'Cubic', 'Quart', 'Quint', 'Expo'], function(t, i) { - e[i] = function(e) { - return Math.pow(e, t + 2); - }; - }), - t.extend(e, { - Sine: function(t) { - return 1 - Math.cos((t * Math.PI) / 2); - }, - Circ: function(t) { - return 1 - Math.sqrt(1 - t * t); - }, - Elastic: function(t) { - return 0 === t || 1 === t - ? t - : -Math.pow(2, 8 * (t - 1)) * - Math.sin(((80 * (t - 1) - 7.5) * Math.PI) / 15); - }, - Back: function(t) { - return t * t * (3 * t - 2); - }, - Bounce: function(t) { - for (var e, i = 4; ((e = Math.pow(2, --i)) - 1) / 11 > t; ); - return ( - 1 / Math.pow(4, 3 - i) - - 7.5625 * Math.pow((3 * e - 2) / 22 - t, 2) - ); - } - }), - t.each(e, function(e, i) { - (t.easing['easeIn' + e] = i), - (t.easing['easeOut' + e] = function(t) { - return 1 - i(1 - t); - }), - (t.easing['easeInOut' + e] = function(t) { - return 0.5 > t ? i(2 * t) / 2 : 1 - i(-2 * t + 2) / 2; - }); - }); - })(); - var f = t.effects; - t.effects.define('blind', 'hide', function(e, i) { - var s = { - up: ['bottom', 'top'], - vertical: ['bottom', 'top'], - down: ['top', 'bottom'], - left: ['right', 'left'], - horizontal: ['right', 'left'], - right: ['left', 'right'] - }, - n = t(this), - o = e.direction || 'up', - a = n.cssClip(), - r = { clip: t.extend({}, a) }, - h = t.effects.createPlaceholder(n); - (r.clip[s[o][0]] = r.clip[s[o][1]]), - 'show' === e.mode && - (n.cssClip(r.clip), h && h.css(t.effects.clipToBox(r)), (r.clip = a)), - h && h.animate(t.effects.clipToBox(r), e.duration, e.easing), - n.animate(r, { - queue: !1, - duration: e.duration, - easing: e.easing, - complete: i - }); - }), - t.effects.define('bounce', function(e, i) { - var s, - n, - o, - a = t(this), - r = e.mode, - h = 'hide' === r, - l = 'show' === r, - c = e.direction || 'up', - u = e.distance, - d = e.times || 5, - p = 2 * d + (l || h ? 1 : 0), - f = e.duration / p, - g = e.easing, - m = 'up' === c || 'down' === c ? 'top' : 'left', - _ = 'up' === c || 'left' === c, - v = 0, - b = a.queue().length; - for ( - t.effects.createPlaceholder(a), - o = a.css(m), - u || (u = a['top' === m ? 'outerHeight' : 'outerWidth']() / 3), - l && - ((n = { opacity: 1 }), - (n[m] = o), - a - .css('opacity', 0) - .css(m, _ ? 2 * -u : 2 * u) - .animate(n, f, g)), - h && (u /= Math.pow(2, d - 1)), - n = {}, - n[m] = o; - d > v; - v++ - ) - (s = {}), - (s[m] = (_ ? '-=' : '+=') + u), - a.animate(s, f, g).animate(n, f, g), - (u = h ? 2 * u : u / 2); - h && - ((s = { opacity: 0 }), - (s[m] = (_ ? '-=' : '+=') + u), - a.animate(s, f, g)), - a.queue(i), - t.effects.unshift(a, b, p + 1); - }), - t.effects.define('clip', 'hide', function(e, i) { - var s, - n = {}, - o = t(this), - a = e.direction || 'vertical', - r = 'both' === a, - h = r || 'horizontal' === a, - l = r || 'vertical' === a; - (s = o.cssClip()), - (n.clip = { - top: l ? (s.bottom - s.top) / 2 : s.top, - right: h ? (s.right - s.left) / 2 : s.right, - bottom: l ? (s.bottom - s.top) / 2 : s.bottom, - left: h ? (s.right - s.left) / 2 : s.left - }), - t.effects.createPlaceholder(o), - 'show' === e.mode && (o.cssClip(n.clip), (n.clip = s)), - o.animate(n, { - queue: !1, - duration: e.duration, - easing: e.easing, - complete: i - }); - }), - t.effects.define('drop', 'hide', function(e, i) { - var s, - n = t(this), - o = e.mode, - a = 'show' === o, - r = e.direction || 'left', - h = 'up' === r || 'down' === r ? 'top' : 'left', - l = 'up' === r || 'left' === r ? '-=' : '+=', - c = '+=' === l ? '-=' : '+=', - u = { opacity: 0 }; - t.effects.createPlaceholder(n), - (s = - e.distance || n['top' === h ? 'outerHeight' : 'outerWidth'](!0) / 2), - (u[h] = l + s), - a && (n.css(u), (u[h] = c + s), (u.opacity = 1)), - n.animate(u, { - queue: !1, - duration: e.duration, - easing: e.easing, - complete: i - }); - }), - t.effects.define('explode', 'hide', function(e, i) { - function s() { - b.push(this), b.length === u * d && n(); - } - function n() { - p.css({ visibility: 'visible' }), t(b).remove(), i(); - } - var o, - a, - r, - h, - l, - c, - u = e.pieces ? Math.round(Math.sqrt(e.pieces)) : 3, - d = u, - p = t(this), - f = e.mode, - g = 'show' === f, - m = p - .show() - .css('visibility', 'hidden') - .offset(), - _ = Math.ceil(p.outerWidth() / d), - v = Math.ceil(p.outerHeight() / u), - b = []; - for (o = 0; u > o; o++) - for (h = m.top + o * v, c = o - (u - 1) / 2, a = 0; d > a; a++) - (r = m.left + a * _), - (l = a - (d - 1) / 2), - p - .clone() - .appendTo('body') - .wrap('
') - .css({ - position: 'absolute', - visibility: 'visible', - left: -a * _, - top: -o * v - }) - .parent() - .addClass('ui-effects-explode') - .css({ - position: 'absolute', - overflow: 'hidden', - width: _, - height: v, - left: r + (g ? l * _ : 0), - top: h + (g ? c * v : 0), - opacity: g ? 0 : 1 - }) - .animate( - { - left: r + (g ? 0 : l * _), - top: h + (g ? 0 : c * v), - opacity: g ? 1 : 0 - }, - e.duration || 500, - e.easing, - s - ); - }), - t.effects.define('fade', 'toggle', function(e, i) { - var s = 'show' === e.mode; - t(this) - .css('opacity', s ? 0 : 1) - .animate( - { opacity: s ? 1 : 0 }, - { queue: !1, duration: e.duration, easing: e.easing, complete: i } - ); - }), - t.effects.define('fold', 'hide', function(e, i) { - var s = t(this), - n = e.mode, - o = 'show' === n, - a = 'hide' === n, - r = e.size || 15, - h = /([0-9]+)%/.exec(r), - l = !!e.horizFirst, - c = l ? ['right', 'bottom'] : ['bottom', 'right'], - u = e.duration / 2, - d = t.effects.createPlaceholder(s), - p = s.cssClip(), - f = { clip: t.extend({}, p) }, - g = { clip: t.extend({}, p) }, - m = [p[c[0]], p[c[1]]], - _ = s.queue().length; - h && (r = (parseInt(h[1], 10) / 100) * m[a ? 0 : 1]), - (f.clip[c[0]] = r), - (g.clip[c[0]] = r), - (g.clip[c[1]] = 0), - o && - (s.cssClip(g.clip), d && d.css(t.effects.clipToBox(g)), (g.clip = p)), - s - .queue(function(i) { - d && - d - .animate(t.effects.clipToBox(f), u, e.easing) - .animate(t.effects.clipToBox(g), u, e.easing), - i(); - }) - .animate(f, u, e.easing) - .animate(g, u, e.easing) - .queue(i), - t.effects.unshift(s, _, 4); - }), - t.effects.define('highlight', 'show', function(e, i) { - var s = t(this), - n = { backgroundColor: s.css('backgroundColor') }; - 'hide' === e.mode && (n.opacity = 0), - t.effects.saveStyle(s), - s - .css({ - backgroundImage: 'none', - backgroundColor: e.color || '#ffff99' - }) - .animate(n, { - queue: !1, - duration: e.duration, - easing: e.easing, - complete: i - }); - }), - t.effects.define('size', function(e, i) { - var s, - n, - o, - a = t(this), - r = ['fontSize'], - h = [ - 'borderTopWidth', - 'borderBottomWidth', - 'paddingTop', - 'paddingBottom' - ], - l = [ - 'borderLeftWidth', - 'borderRightWidth', - 'paddingLeft', - 'paddingRight' - ], - c = e.mode, - u = 'effect' !== c, - d = e.scale || 'both', - p = e.origin || ['middle', 'center'], - f = a.css('position'), - g = a.position(), - m = t.effects.scaledDimensions(a), - _ = e.from || m, - v = e.to || t.effects.scaledDimensions(a, 0); - t.effects.createPlaceholder(a), - 'show' === c && ((o = _), (_ = v), (v = o)), - (n = { - from: { y: _.height / m.height, x: _.width / m.width }, - to: { y: v.height / m.height, x: v.width / m.width } - }), - ('box' === d || 'both' === d) && - (n.from.y !== n.to.y && - ((_ = t.effects.setTransition(a, h, n.from.y, _)), - (v = t.effects.setTransition(a, h, n.to.y, v))), - n.from.x !== n.to.x && - ((_ = t.effects.setTransition(a, l, n.from.x, _)), - (v = t.effects.setTransition(a, l, n.to.x, v)))), - ('content' === d || 'both' === d) && - n.from.y !== n.to.y && - ((_ = t.effects.setTransition(a, r, n.from.y, _)), - (v = t.effects.setTransition(a, r, n.to.y, v))), - p && - ((s = t.effects.getBaseline(p, m)), - (_.top = (m.outerHeight - _.outerHeight) * s.y + g.top), - (_.left = (m.outerWidth - _.outerWidth) * s.x + g.left), - (v.top = (m.outerHeight - v.outerHeight) * s.y + g.top), - (v.left = (m.outerWidth - v.outerWidth) * s.x + g.left)), - a.css(_), - ('content' === d || 'both' === d) && - ((h = h.concat(['marginTop', 'marginBottom']).concat(r)), - (l = l.concat(['marginLeft', 'marginRight'])), - a.find('*[width]').each(function() { - var i = t(this), - s = t.effects.scaledDimensions(i), - o = { - height: s.height * n.from.y, - width: s.width * n.from.x, - outerHeight: s.outerHeight * n.from.y, - outerWidth: s.outerWidth * n.from.x - }, - a = { - height: s.height * n.to.y, - width: s.width * n.to.x, - outerHeight: s.height * n.to.y, - outerWidth: s.width * n.to.x - }; - n.from.y !== n.to.y && - ((o = t.effects.setTransition(i, h, n.from.y, o)), - (a = t.effects.setTransition(i, h, n.to.y, a))), - n.from.x !== n.to.x && - ((o = t.effects.setTransition(i, l, n.from.x, o)), - (a = t.effects.setTransition(i, l, n.to.x, a))), - u && t.effects.saveStyle(i), - i.css(o), - i.animate(a, e.duration, e.easing, function() { - u && t.effects.restoreStyle(i); - }); - })), - a.animate(v, { - queue: !1, - duration: e.duration, - easing: e.easing, - complete: function() { - var e = a.offset(); - 0 === v.opacity && a.css('opacity', _.opacity), - u || - (a.css('position', 'static' === f ? 'relative' : f).offset(e), - t.effects.saveStyle(a)), - i(); - } - }); - }), - t.effects.define('scale', function(e, i) { - var s = t(this), - n = e.mode, - o = - parseInt(e.percent, 10) || - (0 === parseInt(e.percent, 10) ? 0 : 'effect' !== n ? 0 : 100), - a = t.extend( - !0, - { - from: t.effects.scaledDimensions(s), - to: t.effects.scaledDimensions(s, o, e.direction || 'both'), - origin: e.origin || ['middle', 'center'] - }, - e - ); - e.fade && ((a.from.opacity = 1), (a.to.opacity = 0)), - t.effects.effect.size.call(this, a, i); - }), - t.effects.define('puff', 'hide', function(e, i) { - var s = t.extend(!0, {}, e, { - fade: !0, - percent: parseInt(e.percent, 10) || 150 - }); - t.effects.effect.scale.call(this, s, i); - }), - t.effects.define('pulsate', 'show', function(e, i) { - var s = t(this), - n = e.mode, - o = 'show' === n, - a = 'hide' === n, - r = o || a, - h = 2 * (e.times || 5) + (r ? 1 : 0), - l = e.duration / h, - c = 0, - u = 1, - d = s.queue().length; - for ( - (o || !s.is(':visible')) && (s.css('opacity', 0).show(), (c = 1)); - h > u; - u++ - ) - s.animate({ opacity: c }, l, e.easing), (c = 1 - c); - s.animate({ opacity: c }, l, e.easing), - s.queue(i), - t.effects.unshift(s, d, h + 1); - }), - t.effects.define('shake', function(e, i) { - var s = 1, - n = t(this), - o = e.direction || 'left', - a = e.distance || 20, - r = e.times || 3, - h = 2 * r + 1, - l = Math.round(e.duration / h), - c = 'up' === o || 'down' === o ? 'top' : 'left', - u = 'up' === o || 'left' === o, - d = {}, - p = {}, - f = {}, - g = n.queue().length; - for ( - t.effects.createPlaceholder(n), - d[c] = (u ? '-=' : '+=') + a, - p[c] = (u ? '+=' : '-=') + 2 * a, - f[c] = (u ? '-=' : '+=') + 2 * a, - n.animate(d, l, e.easing); - r > s; - s++ - ) - n.animate(p, l, e.easing).animate(f, l, e.easing); - n - .animate(p, l, e.easing) - .animate(d, l / 2, e.easing) - .queue(i), - t.effects.unshift(n, g, h + 1); - }), - t.effects.define('slide', 'show', function(e, i) { - var s, - n, - o = t(this), - a = { - up: ['bottom', 'top'], - down: ['top', 'bottom'], - left: ['right', 'left'], - right: ['left', 'right'] - }, - r = e.mode, - h = e.direction || 'left', - l = 'up' === h || 'down' === h ? 'top' : 'left', - c = 'up' === h || 'left' === h, - u = e.distance || o['top' === l ? 'outerHeight' : 'outerWidth'](!0), - d = {}; - t.effects.createPlaceholder(o), - (s = o.cssClip()), - (n = o.position()[l]), - (d[l] = (c ? -1 : 1) * u + n), - (d.clip = o.cssClip()), - (d.clip[a[h][1]] = d.clip[a[h][0]]), - 'show' === r && - (o.cssClip(d.clip), o.css(l, d[l]), (d.clip = s), (d[l] = n)), - o.animate(d, { - queue: !1, - duration: e.duration, - easing: e.easing, - complete: i - }); - }); - var f; - t.uiBackCompat !== !1 && - (f = t.effects.define('transfer', function(e, i) { - t(this).transfer(e, i); - })), - (t.ui.focusable = function(i, s) { - var n, - o, - a, - r, - h, - l = i.nodeName.toLowerCase(); - return 'area' === l - ? ((n = i.parentNode), - (o = n.name), - i.href && o && 'map' === n.nodeName.toLowerCase() - ? ((a = t("img[usemap='#" + o + "']")), - a.length > 0 && a.is(':visible')) - : !1) - : (/^(input|select|textarea|button|object)$/.test(l) - ? ((r = !i.disabled), - r && ((h = t(i).closest('fieldset')[0]), h && (r = !h.disabled))) - : (r = 'a' === l ? i.href || s : s), - r && t(i).is(':visible') && e(t(i))); - }), - t.extend(t.expr[':'], { - focusable: function(e) { - return t.ui.focusable(e, null != t.attr(e, 'tabindex')); - } - }), - t.ui.focusable, - (t.fn.form = function() { - return 'string' == typeof this[0].form - ? this.closest('form') - : t(this[0].form); - }), - (t.ui.formResetMixin = { - _formResetHandler: function() { - var e = t(this); - setTimeout(function() { - var i = e.data('ui-form-reset-instances'); - t.each(i, function() { - this.refresh(); - }); - }); - }, - _bindFormResetHandler: function() { - if (((this.form = this.element.form()), this.form.length)) { - var t = this.form.data('ui-form-reset-instances') || []; - t.length || - this.form.on('reset.ui-form-reset', this._formResetHandler), - t.push(this), - this.form.data('ui-form-reset-instances', t); - } - }, - _unbindFormResetHandler: function() { - if (this.form.length) { - var e = this.form.data('ui-form-reset-instances'); - e.splice(t.inArray(this, e), 1), - e.length - ? this.form.data('ui-form-reset-instances', e) - : this.form - .removeData('ui-form-reset-instances') - .off('reset.ui-form-reset'); - } - } - }), - '1.7' === t.fn.jquery.substring(0, 3) && - (t.each(['Width', 'Height'], function(e, i) { - function s(e, i, s, o) { - return ( - t.each(n, function() { - (i -= parseFloat(t.css(e, 'padding' + this)) || 0), - s && - (i -= parseFloat(t.css(e, 'border' + this + 'Width')) || 0), - o && (i -= parseFloat(t.css(e, 'margin' + this)) || 0); - }), - i - ); - } - var n = 'Width' === i ? ['Left', 'Right'] : ['Top', 'Bottom'], - o = i.toLowerCase(), - a = { - innerWidth: t.fn.innerWidth, - innerHeight: t.fn.innerHeight, - outerWidth: t.fn.outerWidth, - outerHeight: t.fn.outerHeight - }; - (t.fn['inner' + i] = function(e) { - return void 0 === e - ? a['inner' + i].call(this) - : this.each(function() { - t(this).css(o, s(this, e) + 'px'); - }); - }), - (t.fn['outer' + i] = function(e, n) { - return 'number' != typeof e - ? a['outer' + i].call(this, e) - : this.each(function() { - t(this).css(o, s(this, e, !0, n) + 'px'); - }); - }); - }), - (t.fn.addBack = function(t) { - return this.add( - null == t ? this.prevObject : this.prevObject.filter(t) - ); - })), - (t.ui.keyCode = { - BACKSPACE: 8, - COMMA: 188, - DELETE: 46, - DOWN: 40, - END: 35, - ENTER: 13, - ESCAPE: 27, - HOME: 36, - LEFT: 37, - PAGE_DOWN: 34, - PAGE_UP: 33, - PERIOD: 190, - RIGHT: 39, - SPACE: 32, - TAB: 9, - UP: 38 - }), - (t.ui.escapeSelector = (function() { - var t = /([!"#$%&'()*+,.\/:;<=>?@[\]^`{|}~])/g; - return function(e) { - return e.replace(t, '\\$1'); - }; - })()), - (t.fn.labels = function() { - var e, i, s, n, o; - return this[0].labels && this[0].labels.length - ? this.pushStack(this[0].labels) - : ((n = this.eq(0).parents('label')), - (s = this.attr('id')), - s && - ((e = this.eq(0) - .parents() - .last()), - (o = e.add(e.length ? e.siblings() : this.siblings())), - (i = "label[for='" + t.ui.escapeSelector(s) + "']"), - (n = n.add(o.find(i).addBack(i)))), - this.pushStack(n)); - }), - (t.fn.scrollParent = function(e) { - var i = this.css('position'), - s = 'absolute' === i, - n = e ? /(auto|scroll|hidden)/ : /(auto|scroll)/, - o = this.parents() - .filter(function() { - var e = t(this); - return s && 'static' === e.css('position') - ? !1 - : n.test( - e.css('overflow') + e.css('overflow-y') + e.css('overflow-x') - ); - }) - .eq(0); - return 'fixed' !== i && o.length - ? o - : t(this[0].ownerDocument || document); - }), - t.extend(t.expr[':'], { - tabbable: function(e) { - var i = t.attr(e, 'tabindex'), - s = null != i; - return (!s || i >= 0) && t.ui.focusable(e, s); - } - }), - t.fn.extend({ - uniqueId: (function() { - var t = 0; - return function() { - return this.each(function() { - this.id || (this.id = 'ui-id-' + ++t); - }); - }; - })(), - removeUniqueId: function() { - return this.each(function() { - /^ui-id-\d+$/.test(this.id) && t(this).removeAttr('id'); - }); - } - }), - t.widget('ui.accordion', { - version: '1.12.1', - options: { - active: 0, - animate: {}, - classes: { - 'ui-accordion-header': 'ui-corner-top', - 'ui-accordion-header-collapsed': 'ui-corner-all', - 'ui-accordion-content': 'ui-corner-bottom' - }, - collapsible: !1, - event: 'click', - header: '> li > :first-child, > :not(li):even', - heightStyle: 'auto', - icons: { - activeHeader: 'ui-icon-triangle-1-s', - header: 'ui-icon-triangle-1-e' - }, - activate: null, - beforeActivate: null - }, - hideProps: { - borderTopWidth: 'hide', - borderBottomWidth: 'hide', - paddingTop: 'hide', - paddingBottom: 'hide', - height: 'hide' - }, - showProps: { - borderTopWidth: 'show', - borderBottomWidth: 'show', - paddingTop: 'show', - paddingBottom: 'show', - height: 'show' - }, - _create: function() { - var e = this.options; - (this.prevShow = this.prevHide = t()), - this._addClass('ui-accordion', 'ui-widget ui-helper-reset'), - this.element.attr('role', 'tablist'), - e.collapsible || - (e.active !== !1 && null != e.active) || - (e.active = 0), - this._processPanels(), - 0 > e.active && (e.active += this.headers.length), - this._refresh(); - }, - _getCreateEventData: function() { - return { - header: this.active, - panel: this.active.length ? this.active.next() : t() - }; - }, - _createIcons: function() { - var e, - i, - s = this.options.icons; - s && - ((e = t('')), - this._addClass(e, 'ui-accordion-header-icon', 'ui-icon ' + s.header), - e.prependTo(this.headers), - (i = this.active.children('.ui-accordion-header-icon')), - this._removeClass(i, s.header) - ._addClass(i, null, s.activeHeader) - ._addClass(this.headers, 'ui-accordion-icons')); - }, - _destroyIcons: function() { - this._removeClass(this.headers, 'ui-accordion-icons'), - this.headers.children('.ui-accordion-header-icon').remove(); - }, - _destroy: function() { - var t; - this.element.removeAttr('role'), - this.headers - .removeAttr( - 'role aria-expanded aria-selected aria-controls tabIndex' - ) - .removeUniqueId(), - this._destroyIcons(), - (t = this.headers - .next() - .css('display', '') - .removeAttr('role aria-hidden aria-labelledby') - .removeUniqueId()), - 'content' !== this.options.heightStyle && t.css('height', ''); - }, - _setOption: function(t, e) { - return 'active' === t - ? (this._activate(e), void 0) - : ('event' === t && - (this.options.event && - this._off(this.headers, this.options.event), - this._setupEvents(e)), - this._super(t, e), - 'collapsible' !== t || - e || - this.options.active !== !1 || - this._activate(0), - 'icons' === t && (this._destroyIcons(), e && this._createIcons()), - void 0); - }, - _setOptionDisabled: function(t) { - this._super(t), - this.element.attr('aria-disabled', t), - this._toggleClass(null, 'ui-state-disabled', !!t), - this._toggleClass( - this.headers.add(this.headers.next()), - null, - 'ui-state-disabled', - !!t - ); - }, - _keydown: function(e) { - if (!e.altKey && !e.ctrlKey) { - var i = t.ui.keyCode, - s = this.headers.length, - n = this.headers.index(e.target), - o = !1; - switch (e.keyCode) { - case i.RIGHT: - case i.DOWN: - o = this.headers[(n + 1) % s]; - break; - case i.LEFT: - case i.UP: - o = this.headers[(n - 1 + s) % s]; - break; - case i.SPACE: - case i.ENTER: - this._eventHandler(e); - break; - case i.HOME: - o = this.headers[0]; - break; - case i.END: - o = this.headers[s - 1]; - } - o && - (t(e.target).attr('tabIndex', -1), - t(o).attr('tabIndex', 0), - t(o).trigger('focus'), - e.preventDefault()); - } - }, - _panelKeyDown: function(e) { - e.keyCode === t.ui.keyCode.UP && - e.ctrlKey && - t(e.currentTarget) - .prev() - .trigger('focus'); - }, - refresh: function() { - var e = this.options; - this._processPanels(), - (e.active === !1 && e.collapsible === !0) || !this.headers.length - ? ((e.active = !1), (this.active = t())) - : e.active === !1 - ? this._activate(0) - : this.active.length && !t.contains(this.element[0], this.active[0]) - ? this.headers.length === - this.headers.find('.ui-state-disabled').length - ? ((e.active = !1), (this.active = t())) - : this._activate(Math.max(0, e.active - 1)) - : (e.active = this.headers.index(this.active)), - this._destroyIcons(), - this._refresh(); - }, - _processPanels: function() { - var t = this.headers, - e = this.panels; - (this.headers = this.element.find(this.options.header)), - this._addClass( - this.headers, - 'ui-accordion-header ui-accordion-header-collapsed', - 'ui-state-default' - ), - (this.panels = this.headers - .next() - .filter(':not(.ui-accordion-content-active)') - .hide()), - this._addClass( - this.panels, - 'ui-accordion-content', - 'ui-helper-reset ui-widget-content' - ), - e && (this._off(t.not(this.headers)), this._off(e.not(this.panels))); - }, - _refresh: function() { - var e, - i = this.options, - s = i.heightStyle, - n = this.element.parent(); - (this.active = this._findActive(i.active)), - this._addClass( - this.active, - 'ui-accordion-header-active', - 'ui-state-active' - )._removeClass(this.active, 'ui-accordion-header-collapsed'), - this._addClass(this.active.next(), 'ui-accordion-content-active'), - this.active.next().show(), - this.headers - .attr('role', 'tab') - .each(function() { - var e = t(this), - i = e.uniqueId().attr('id'), - s = e.next(), - n = s.uniqueId().attr('id'); - e.attr('aria-controls', n), s.attr('aria-labelledby', i); - }) - .next() - .attr('role', 'tabpanel'), - this.headers - .not(this.active) - .attr({ - 'aria-selected': 'false', - 'aria-expanded': 'false', - tabIndex: -1 - }) - .next() - .attr({ 'aria-hidden': 'true' }) - .hide(), - this.active.length - ? this.active - .attr({ - 'aria-selected': 'true', - 'aria-expanded': 'true', - tabIndex: 0 - }) - .next() - .attr({ 'aria-hidden': 'false' }) - : this.headers.eq(0).attr('tabIndex', 0), - this._createIcons(), - this._setupEvents(i.event), - 'fill' === s - ? ((e = n.height()), - this.element.siblings(':visible').each(function() { - var i = t(this), - s = i.css('position'); - 'absolute' !== s && 'fixed' !== s && (e -= i.outerHeight(!0)); - }), - this.headers.each(function() { - e -= t(this).outerHeight(!0); - }), - this.headers - .next() - .each(function() { - t(this).height( - Math.max(0, e - t(this).innerHeight() + t(this).height()) - ); - }) - .css('overflow', 'auto')) - : 'auto' === s && - ((e = 0), - this.headers - .next() - .each(function() { - var i = t(this).is(':visible'); - i || t(this).show(), - (e = Math.max( - e, - t(this) - .css('height', '') - .height() - )), - i || t(this).hide(); - }) - .height(e)); - }, - _activate: function(e) { - var i = this._findActive(e)[0]; - i !== this.active[0] && - ((i = i || this.active[0]), - this._eventHandler({ - target: i, - currentTarget: i, - preventDefault: t.noop - })); - }, - _findActive: function(e) { - return 'number' == typeof e ? this.headers.eq(e) : t(); - }, - _setupEvents: function(e) { - var i = { keydown: '_keydown' }; - e && - t.each(e.split(' '), function(t, e) { - i[e] = '_eventHandler'; - }), - this._off(this.headers.add(this.headers.next())), - this._on(this.headers, i), - this._on(this.headers.next(), { keydown: '_panelKeyDown' }), - this._hoverable(this.headers), - this._focusable(this.headers); - }, - _eventHandler: function(e) { - var i, - s, - n = this.options, - o = this.active, - a = t(e.currentTarget), - r = a[0] === o[0], - h = r && n.collapsible, - l = h ? t() : a.next(), - c = o.next(), - u = { - oldHeader: o, - oldPanel: c, - newHeader: h ? t() : a, - newPanel: l - }; - e.preventDefault(), - (r && !n.collapsible) || - this._trigger('beforeActivate', e, u) === !1 || - ((n.active = h ? !1 : this.headers.index(a)), - (this.active = r ? t() : a), - this._toggle(u), - this._removeClass( - o, - 'ui-accordion-header-active', - 'ui-state-active' - ), - n.icons && - ((i = o.children('.ui-accordion-header-icon')), - this._removeClass(i, null, n.icons.activeHeader)._addClass( - i, - null, - n.icons.header - )), - r || - (this._removeClass(a, 'ui-accordion-header-collapsed')._addClass( - a, - 'ui-accordion-header-active', - 'ui-state-active' - ), - n.icons && - ((s = a.children('.ui-accordion-header-icon')), - this._removeClass(s, null, n.icons.header)._addClass( - s, - null, - n.icons.activeHeader - )), - this._addClass(a.next(), 'ui-accordion-content-active'))); - }, - _toggle: function(e) { - var i = e.newPanel, - s = this.prevShow.length ? this.prevShow : e.oldPanel; - this.prevShow.add(this.prevHide).stop(!0, !0), - (this.prevShow = i), - (this.prevHide = s), - this.options.animate - ? this._animate(i, s, e) - : (s.hide(), i.show(), this._toggleComplete(e)), - s.attr({ 'aria-hidden': 'true' }), - s.prev().attr({ 'aria-selected': 'false', 'aria-expanded': 'false' }), - i.length && s.length - ? s.prev().attr({ tabIndex: -1, 'aria-expanded': 'false' }) - : i.length && - this.headers - .filter(function() { - return 0 === parseInt(t(this).attr('tabIndex'), 10); - }) - .attr('tabIndex', -1), - i - .attr('aria-hidden', 'false') - .prev() - .attr({ - 'aria-selected': 'true', - 'aria-expanded': 'true', - tabIndex: 0 - }); - }, - _animate: function(t, e, i) { - var s, - n, - o, - a = this, - r = 0, - h = t.css('box-sizing'), - l = t.length && (!e.length || t.index() < e.index()), - c = this.options.animate || {}, - u = (l && c.down) || c, - d = function() { - a._toggleComplete(i); - }; - return ( - 'number' == typeof u && (o = u), - 'string' == typeof u && (n = u), - (n = n || u.easing || c.easing), - (o = o || u.duration || c.duration), - e.length - ? t.length - ? ((s = t.show().outerHeight()), - e.animate(this.hideProps, { - duration: o, - easing: n, - step: function(t, e) { - e.now = Math.round(t); - } - }), - t.hide().animate(this.showProps, { - duration: o, - easing: n, - complete: d, - step: function(t, i) { - (i.now = Math.round(t)), - 'height' !== i.prop - ? 'content-box' === h && (r += i.now) - : 'content' !== a.options.heightStyle && - ((i.now = Math.round(s - e.outerHeight() - r)), - (r = 0)); - } - }), - void 0) - : e.animate(this.hideProps, o, n, d) - : t.animate(this.showProps, o, n, d) - ); - }, - _toggleComplete: function(t) { - var e = t.oldPanel, - i = e.prev(); - this._removeClass(e, 'ui-accordion-content-active'), - this._removeClass(i, 'ui-accordion-header-active')._addClass( - i, - 'ui-accordion-header-collapsed' - ), - e.length && (e.parent()[0].className = e.parent()[0].className), - this._trigger('activate', null, t); - } - }), - (t.ui.safeActiveElement = function(t) { - var e; - try { - e = t.activeElement; - } catch (i) { - e = t.body; - } - return e || (e = t.body), e.nodeName || (e = t.body), e; - }), - t.widget('ui.menu', { - version: '1.12.1', - defaultElement: '
    ', - delay: 300, - options: { - icons: { submenu: 'ui-icon-caret-1-e' }, - items: '> *', - menus: 'ul', - position: { my: 'left top', at: 'right top' }, - role: 'menu', - blur: null, - focus: null, - select: null - }, - _create: function() { - (this.activeMenu = this.element), - (this.mouseHandled = !1), - this.element - .uniqueId() - .attr({ role: this.options.role, tabIndex: 0 }), - this._addClass('ui-menu', 'ui-widget ui-widget-content'), - this._on({ - 'mousedown .ui-menu-item': function(t) { - t.preventDefault(); - }, - 'click .ui-menu-item': function(e) { - var i = t(e.target), - s = t(t.ui.safeActiveElement(this.document[0])); - !this.mouseHandled && - i.not('.ui-state-disabled').length && - (this.select(e), - e.isPropagationStopped() || (this.mouseHandled = !0), - i.has('.ui-menu').length - ? this.expand(e) - : !this.element.is(':focus') && - s.closest('.ui-menu').length && - (this.element.trigger('focus', [!0]), - this.active && - 1 === this.active.parents('.ui-menu').length && - clearTimeout(this.timer))); - }, - 'mouseenter .ui-menu-item': function(e) { - if (!this.previousFilter) { - var i = t(e.target).closest('.ui-menu-item'), - s = t(e.currentTarget); - i[0] === s[0] && - (this._removeClass( - s.siblings().children('.ui-state-active'), - null, - 'ui-state-active' - ), - this.focus(e, s)); - } - }, - mouseleave: 'collapseAll', - 'mouseleave .ui-menu': 'collapseAll', - focus: function(t, e) { - var i = - this.active || this.element.find(this.options.items).eq(0); - e || this.focus(t, i); - }, - blur: function(e) { - this._delay(function() { - var i = !t.contains( - this.element[0], - t.ui.safeActiveElement(this.document[0]) - ); - i && this.collapseAll(e); - }); - }, - keydown: '_keydown' - }), - this.refresh(), - this._on(this.document, { - click: function(t) { - this._closeOnDocumentClick(t) && this.collapseAll(t), - (this.mouseHandled = !1); - } - }); - }, - _destroy: function() { - var e = this.element - .find('.ui-menu-item') - .removeAttr('role aria-disabled'), - i = e - .children('.ui-menu-item-wrapper') - .removeUniqueId() - .removeAttr('tabIndex role aria-haspopup'); - this.element - .removeAttr('aria-activedescendant') - .find('.ui-menu') - .addBack() - .removeAttr( - 'role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex' - ) - .removeUniqueId() - .show(), - i.children().each(function() { - var e = t(this); - e.data('ui-menu-submenu-caret') && e.remove(); - }); - }, - _keydown: function(e) { - var i, - s, - n, - o, - a = !0; - switch (e.keyCode) { - case t.ui.keyCode.PAGE_UP: - this.previousPage(e); - break; - case t.ui.keyCode.PAGE_DOWN: - this.nextPage(e); - break; - case t.ui.keyCode.HOME: - this._move('first', 'first', e); - break; - case t.ui.keyCode.END: - this._move('last', 'last', e); - break; - case t.ui.keyCode.UP: - this.previous(e); - break; - case t.ui.keyCode.DOWN: - this.next(e); - break; - case t.ui.keyCode.LEFT: - this.collapse(e); - break; - case t.ui.keyCode.RIGHT: - this.active && - !this.active.is('.ui-state-disabled') && - this.expand(e); - break; - case t.ui.keyCode.ENTER: - case t.ui.keyCode.SPACE: - this._activate(e); - break; - case t.ui.keyCode.ESCAPE: - this.collapse(e); - break; - default: - (a = !1), - (s = this.previousFilter || ''), - (o = !1), - (n = - e.keyCode >= 96 && 105 >= e.keyCode - ? '' + (e.keyCode - 96) - : String.fromCharCode(e.keyCode)), - clearTimeout(this.filterTimer), - n === s ? (o = !0) : (n = s + n), - (i = this._filterMenuItems(n)), - (i = - o && -1 !== i.index(this.active.next()) - ? this.active.nextAll('.ui-menu-item') - : i), - i.length || - ((n = String.fromCharCode(e.keyCode)), - (i = this._filterMenuItems(n))), - i.length - ? (this.focus(e, i), - (this.previousFilter = n), - (this.filterTimer = this._delay(function() { - delete this.previousFilter; - }, 1e3))) - : delete this.previousFilter; - } - a && e.preventDefault(); - }, - _activate: function(t) { - this.active && - !this.active.is('.ui-state-disabled') && - (this.active.children("[aria-haspopup='true']").length - ? this.expand(t) - : this.select(t)); - }, - refresh: function() { - var e, - i, - s, - n, - o, - a = this, - r = this.options.icons.submenu, - h = this.element.find(this.options.menus); - this._toggleClass( - 'ui-menu-icons', - null, - !!this.element.find('.ui-icon').length - ), - (s = h - .filter(':not(.ui-menu)') - .hide() - .attr({ - role: this.options.role, - 'aria-hidden': 'true', - 'aria-expanded': 'false' - }) - .each(function() { - var e = t(this), - i = e.prev(), - s = t('').data('ui-menu-submenu-caret', !0); - a._addClass(s, 'ui-menu-icon', 'ui-icon ' + r), - i.attr('aria-haspopup', 'true').prepend(s), - e.attr('aria-labelledby', i.attr('id')); - })), - this._addClass(s, 'ui-menu', 'ui-widget ui-widget-content ui-front'), - (e = h.add(this.element)), - (i = e.find(this.options.items)), - i.not('.ui-menu-item').each(function() { - var e = t(this); - a._isDivider(e) && - a._addClass(e, 'ui-menu-divider', 'ui-widget-content'); - }), - (n = i.not('.ui-menu-item, .ui-menu-divider')), - (o = n - .children() - .not('.ui-menu') - .uniqueId() - .attr({ tabIndex: -1, role: this._itemRole() })), - this._addClass(n, 'ui-menu-item')._addClass( - o, - 'ui-menu-item-wrapper' - ), - i.filter('.ui-state-disabled').attr('aria-disabled', 'true'), - this.active && - !t.contains(this.element[0], this.active[0]) && - this.blur(); - }, - _itemRole: function() { - return { menu: 'menuitem', listbox: 'option' }[this.options.role]; - }, - _setOption: function(t, e) { - if ('icons' === t) { - var i = this.element.find('.ui-menu-icon'); - this._removeClass(i, null, this.options.icons.submenu)._addClass( - i, - null, - e.submenu - ); - } - this._super(t, e); - }, - _setOptionDisabled: function(t) { - this._super(t), - this.element.attr('aria-disabled', t + ''), - this._toggleClass(null, 'ui-state-disabled', !!t); - }, - focus: function(t, e) { - var i, s, n; - this.blur(t, t && 'focus' === t.type), - this._scrollIntoView(e), - (this.active = e.first()), - (s = this.active.children('.ui-menu-item-wrapper')), - this._addClass(s, null, 'ui-state-active'), - this.options.role && - this.element.attr('aria-activedescendant', s.attr('id')), - (n = this.active - .parent() - .closest('.ui-menu-item') - .children('.ui-menu-item-wrapper')), - this._addClass(n, null, 'ui-state-active'), - t && 'keydown' === t.type - ? this._close() - : (this.timer = this._delay(function() { - this._close(); - }, this.delay)), - (i = e.children('.ui-menu')), - i.length && t && /^mouse/.test(t.type) && this._startOpening(i), - (this.activeMenu = e.parent()), - this._trigger('focus', t, { item: e }); - }, - _scrollIntoView: function(e) { - var i, s, n, o, a, r; - this._hasScroll() && - ((i = parseFloat(t.css(this.activeMenu[0], 'borderTopWidth')) || 0), - (s = parseFloat(t.css(this.activeMenu[0], 'paddingTop')) || 0), - (n = e.offset().top - this.activeMenu.offset().top - i - s), - (o = this.activeMenu.scrollTop()), - (a = this.activeMenu.height()), - (r = e.outerHeight()), - 0 > n - ? this.activeMenu.scrollTop(o + n) - : n + r > a && this.activeMenu.scrollTop(o + n - a + r)); - }, - blur: function(t, e) { - e || clearTimeout(this.timer), - this.active && - (this._removeClass( - this.active.children('.ui-menu-item-wrapper'), - null, - 'ui-state-active' - ), - this._trigger('blur', t, { item: this.active }), - (this.active = null)); - }, - _startOpening: function(t) { - clearTimeout(this.timer), - 'true' === t.attr('aria-hidden') && - (this.timer = this._delay(function() { - this._close(), this._open(t); - }, this.delay)); - }, - _open: function(e) { - var i = t.extend({ of: this.active }, this.options.position); - clearTimeout(this.timer), - this.element - .find('.ui-menu') - .not(e.parents('.ui-menu')) - .hide() - .attr('aria-hidden', 'true'), - e - .show() - .removeAttr('aria-hidden') - .attr('aria-expanded', 'true') - .position(i); - }, - collapseAll: function(e, i) { - clearTimeout(this.timer), - (this.timer = this._delay(function() { - var s = i - ? this.element - : t(e && e.target).closest(this.element.find('.ui-menu')); - s.length || (s = this.element), - this._close(s), - this.blur(e), - this._removeClass( - s.find('.ui-state-active'), - null, - 'ui-state-active' - ), - (this.activeMenu = s); - }, this.delay)); - }, - _close: function(t) { - t || (t = this.active ? this.active.parent() : this.element), - t - .find('.ui-menu') - .hide() - .attr('aria-hidden', 'true') - .attr('aria-expanded', 'false'); - }, - _closeOnDocumentClick: function(e) { - return !t(e.target).closest('.ui-menu').length; - }, - _isDivider: function(t) { - return !/[^\-\u2014\u2013\s]/.test(t.text()); - }, - collapse: function(t) { - var e = - this.active && - this.active.parent().closest('.ui-menu-item', this.element); - e && e.length && (this._close(), this.focus(t, e)); - }, - expand: function(t) { - var e = - this.active && - this.active - .children('.ui-menu ') - .find(this.options.items) - .first(); - e && - e.length && - (this._open(e.parent()), - this._delay(function() { - this.focus(t, e); - })); - }, - next: function(t) { - this._move('next', 'first', t); - }, - previous: function(t) { - this._move('prev', 'last', t); - }, - isFirstItem: function() { - return this.active && !this.active.prevAll('.ui-menu-item').length; - }, - isLastItem: function() { - return this.active && !this.active.nextAll('.ui-menu-item').length; - }, - _move: function(t, e, i) { - var s; - this.active && - (s = - 'first' === t || 'last' === t - ? this.active['first' === t ? 'prevAll' : 'nextAll']( - '.ui-menu-item' - ).eq(-1) - : this.active[t + 'All']('.ui-menu-item').eq(0)), - (s && s.length && this.active) || - (s = this.activeMenu.find(this.options.items)[e]()), - this.focus(i, s); - }, - nextPage: function(e) { - var i, s, n; - return this.active - ? (this.isLastItem() || - (this._hasScroll() - ? ((s = this.active.offset().top), - (n = this.element.height()), - this.active.nextAll('.ui-menu-item').each(function() { - return (i = t(this)), 0 > i.offset().top - s - n; - }), - this.focus(e, i)) - : this.focus( - e, - this.activeMenu - .find(this.options.items) - [this.active ? 'last' : 'first']() - )), - void 0) - : (this.next(e), void 0); - }, - previousPage: function(e) { - var i, s, n; - return this.active - ? (this.isFirstItem() || - (this._hasScroll() - ? ((s = this.active.offset().top), - (n = this.element.height()), - this.active.prevAll('.ui-menu-item').each(function() { - return (i = t(this)), i.offset().top - s + n > 0; - }), - this.focus(e, i)) - : this.focus( - e, - this.activeMenu.find(this.options.items).first() - )), - void 0) - : (this.next(e), void 0); - }, - _hasScroll: function() { - return this.element.outerHeight() < this.element.prop('scrollHeight'); - }, - select: function(e) { - this.active = this.active || t(e.target).closest('.ui-menu-item'); - var i = { item: this.active }; - this.active.has('.ui-menu').length || this.collapseAll(e, !0), - this._trigger('select', e, i); - }, - _filterMenuItems: function(e) { - var i = e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'), - s = RegExp('^' + i, 'i'); - return this.activeMenu - .find(this.options.items) - .filter('.ui-menu-item') - .filter(function() { - return s.test( - t.trim( - t(this) - .children('.ui-menu-item-wrapper') - .text() - ) - ); - }); - } - }), - t.widget('ui.autocomplete', { - version: '1.12.1', - defaultElement: '', - options: { - appendTo: null, - autoFocus: !1, - delay: 300, - minLength: 1, - position: { my: 'left top', at: 'left bottom', collision: 'none' }, - source: null, - change: null, - close: null, - focus: null, - open: null, - response: null, - search: null, - select: null - }, - requestIndex: 0, - pending: 0, - _create: function() { - var e, - i, - s, - n = this.element[0].nodeName.toLowerCase(), - o = 'textarea' === n, - a = 'input' === n; - (this.isMultiLine = o || (!a && this._isContentEditable(this.element))), - (this.valueMethod = this.element[o || a ? 'val' : 'text']), - (this.isNewMenu = !0), - this._addClass('ui-autocomplete-input'), - this.element.attr('autocomplete', 'off'), - this._on(this.element, { - keydown: function(n) { - if (this.element.prop('readOnly')) - return (e = !0), (s = !0), (i = !0), void 0; - (e = !1), (s = !1), (i = !1); - var o = t.ui.keyCode; - switch (n.keyCode) { - case o.PAGE_UP: - (e = !0), this._move('previousPage', n); - break; - case o.PAGE_DOWN: - (e = !0), this._move('nextPage', n); - break; - case o.UP: - (e = !0), this._keyEvent('previous', n); - break; - case o.DOWN: - (e = !0), this._keyEvent('next', n); - break; - case o.ENTER: - this.menu.active && - ((e = !0), n.preventDefault(), this.menu.select(n)); - break; - case o.TAB: - this.menu.active && this.menu.select(n); - break; - case o.ESCAPE: - this.menu.element.is(':visible') && - (this.isMultiLine || this._value(this.term), - this.close(n), - n.preventDefault()); - break; - default: - (i = !0), this._searchTimeout(n); - } - }, - keypress: function(s) { - if (e) - return ( - (e = !1), - (!this.isMultiLine || this.menu.element.is(':visible')) && - s.preventDefault(), - void 0 - ); - if (!i) { - var n = t.ui.keyCode; - switch (s.keyCode) { - case n.PAGE_UP: - this._move('previousPage', s); - break; - case n.PAGE_DOWN: - this._move('nextPage', s); - break; - case n.UP: - this._keyEvent('previous', s); - break; - case n.DOWN: - this._keyEvent('next', s); - } - } - }, - input: function(t) { - return s - ? ((s = !1), t.preventDefault(), void 0) - : (this._searchTimeout(t), void 0); - }, - focus: function() { - (this.selectedItem = null), (this.previous = this._value()); - }, - blur: function(t) { - return this.cancelBlur - ? (delete this.cancelBlur, void 0) - : (clearTimeout(this.searching), - this.close(t), - this._change(t), - void 0); - } - }), - this._initSource(), - (this.menu = t('
      ') - .appendTo(this._appendTo()) - .menu({ role: null }) - .hide() - .menu('instance')), - this._addClass(this.menu.element, 'ui-autocomplete', 'ui-front'), - this._on(this.menu.element, { - mousedown: function(e) { - e.preventDefault(), - (this.cancelBlur = !0), - this._delay(function() { - delete this.cancelBlur, - this.element[0] !== - t.ui.safeActiveElement(this.document[0]) && - this.element.trigger('focus'); - }); - }, - menufocus: function(e, i) { - var s, n; - return this.isNewMenu && - ((this.isNewMenu = !1), - e.originalEvent && /^mouse/.test(e.originalEvent.type)) - ? (this.menu.blur(), - this.document.one('mousemove', function() { - t(e.target).trigger(e.originalEvent); - }), - void 0) - : ((n = i.item.data('ui-autocomplete-item')), - !1 !== this._trigger('focus', e, { item: n }) && - e.originalEvent && - /^key/.test(e.originalEvent.type) && - this._value(n.value), - (s = i.item.attr('aria-label') || n.value), - s && - t.trim(s).length && - (this.liveRegion.children().hide(), - t('
      ') - .text(s) - .appendTo(this.liveRegion)), - void 0); - }, - menuselect: function(e, i) { - var s = i.item.data('ui-autocomplete-item'), - n = this.previous; - this.element[0] !== t.ui.safeActiveElement(this.document[0]) && - (this.element.trigger('focus'), - (this.previous = n), - this._delay(function() { - (this.previous = n), (this.selectedItem = s); - })), - !1 !== this._trigger('select', e, { item: s }) && - this._value(s.value), - (this.term = this._value()), - this.close(e), - (this.selectedItem = s); - } - }), - (this.liveRegion = t('
      ', { - role: 'status', - 'aria-live': 'assertive', - 'aria-relevant': 'additions' - }).appendTo(this.document[0].body)), - this._addClass(this.liveRegion, null, 'ui-helper-hidden-accessible'), - this._on(this.window, { - beforeunload: function() { - this.element.removeAttr('autocomplete'); - } - }); - }, - _destroy: function() { - clearTimeout(this.searching), - this.element.removeAttr('autocomplete'), - this.menu.element.remove(), - this.liveRegion.remove(); - }, - _setOption: function(t, e) { - this._super(t, e), - 'source' === t && this._initSource(), - 'appendTo' === t && this.menu.element.appendTo(this._appendTo()), - 'disabled' === t && e && this.xhr && this.xhr.abort(); - }, - _isEventTargetInWidget: function(e) { - var i = this.menu.element[0]; - return ( - e.target === this.element[0] || - e.target === i || - t.contains(i, e.target) - ); - }, - _closeOnClickOutside: function(t) { - this._isEventTargetInWidget(t) || this.close(); - }, - _appendTo: function() { - var e = this.options.appendTo; - return ( - e && - (e = e.jquery || e.nodeType ? t(e) : this.document.find(e).eq(0)), - (e && e[0]) || (e = this.element.closest('.ui-front, dialog')), - e.length || (e = this.document[0].body), - e - ); - }, - _initSource: function() { - var e, - i, - s = this; - t.isArray(this.options.source) - ? ((e = this.options.source), - (this.source = function(i, s) { - s(t.ui.autocomplete.filter(e, i.term)); - })) - : 'string' == typeof this.options.source - ? ((i = this.options.source), - (this.source = function(e, n) { - s.xhr && s.xhr.abort(), - (s.xhr = t.ajax({ - url: i, - data: e, - dataType: 'json', - success: function(t) { - n(t); - }, - error: function() { - n([]); - } - })); - })) - : (this.source = this.options.source); - }, - _searchTimeout: function(t) { - clearTimeout(this.searching), - (this.searching = this._delay(function() { - var e = this.term === this._value(), - i = this.menu.element.is(':visible'), - s = t.altKey || t.ctrlKey || t.metaKey || t.shiftKey; - (!e || (e && !i && !s)) && - ((this.selectedItem = null), this.search(null, t)); - }, this.options.delay)); - }, - search: function(t, e) { - return ( - (t = null != t ? t : this._value()), - (this.term = this._value()), - t.length < this.options.minLength - ? this.close(e) - : this._trigger('search', e) !== !1 - ? this._search(t) - : void 0 - ); - }, - _search: function(t) { - this.pending++, - this._addClass('ui-autocomplete-loading'), - (this.cancelSearch = !1), - this.source({ term: t }, this._response()); - }, - _response: function() { - var e = ++this.requestIndex; - return t.proxy(function(t) { - e === this.requestIndex && this.__response(t), - this.pending--, - this.pending || this._removeClass('ui-autocomplete-loading'); - }, this); - }, - __response: function(t) { - t && (t = this._normalize(t)), - this._trigger('response', null, { content: t }), - !this.options.disabled && t && t.length && !this.cancelSearch - ? (this._suggest(t), this._trigger('open')) - : this._close(); - }, - close: function(t) { - (this.cancelSearch = !0), this._close(t); - }, - _close: function(t) { - this._off(this.document, 'mousedown'), - this.menu.element.is(':visible') && - (this.menu.element.hide(), - this.menu.blur(), - (this.isNewMenu = !0), - this._trigger('close', t)); - }, - _change: function(t) { - this.previous !== this._value() && - this._trigger('change', t, { item: this.selectedItem }); - }, - _normalize: function(e) { - return e.length && e[0].label && e[0].value - ? e - : t.map(e, function(e) { - return 'string' == typeof e - ? { label: e, value: e } - : t.extend({}, e, { - label: e.label || e.value, - value: e.value || e.label - }); - }); - }, - _suggest: function(e) { - var i = this.menu.element.empty(); - this._renderMenu(i, e), - (this.isNewMenu = !0), - this.menu.refresh(), - i.show(), - this._resizeMenu(), - i.position(t.extend({ of: this.element }, this.options.position)), - this.options.autoFocus && this.menu.next(), - this._on(this.document, { mousedown: '_closeOnClickOutside' }); - }, - _resizeMenu: function() { - var t = this.menu.element; - t.outerWidth( - Math.max(t.width('').outerWidth() + 1, this.element.outerWidth()) - ); - }, - _renderMenu: function(e, i) { - var s = this; - t.each(i, function(t, i) { - s._renderItemData(e, i); - }); - }, - _renderItemData: function(t, e) { - return this._renderItem(t, e).data('ui-autocomplete-item', e); - }, - _renderItem: function(e, i) { - return t('
    • ') - .append(t('
      ').text(i.label)) - .appendTo(e); - }, - _move: function(t, e) { - return this.menu.element.is(':visible') - ? (this.menu.isFirstItem() && /^previous/.test(t)) || - (this.menu.isLastItem() && /^next/.test(t)) - ? (this.isMultiLine || this._value(this.term), - this.menu.blur(), - void 0) - : (this.menu[t](e), void 0) - : (this.search(null, e), void 0); - }, - widget: function() { - return this.menu.element; - }, - _value: function() { - return this.valueMethod.apply(this.element, arguments); - }, - _keyEvent: function(t, e) { - (!this.isMultiLine || this.menu.element.is(':visible')) && - (this._move(t, e), e.preventDefault()); - }, - _isContentEditable: function(t) { - if (!t.length) return !1; - var e = t.prop('contentEditable'); - return 'inherit' === e - ? this._isContentEditable(t.parent()) - : 'true' === e; - } - }), - t.extend(t.ui.autocomplete, { - escapeRegex: function(t) { - return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); - }, - filter: function(e, i) { - var s = RegExp(t.ui.autocomplete.escapeRegex(i), 'i'); - return t.grep(e, function(t) { - return s.test(t.label || t.value || t); - }); - } - }), - t.widget('ui.autocomplete', t.ui.autocomplete, { - options: { - messages: { - noResults: 'No search results.', - results: function(t) { - return ( - t + - (t > 1 ? ' results are' : ' result is') + - ' available, use up and down arrow keys to navigate.' - ); - } - } - }, - __response: function(e) { - var i; - this._superApply(arguments), - this.options.disabled || - this.cancelSearch || - ((i = - e && e.length - ? this.options.messages.results(e.length) - : this.options.messages.noResults), - this.liveRegion.children().hide(), - t('
      ') - .text(i) - .appendTo(this.liveRegion)); - } - }), - t.ui.autocomplete; - var g = /ui-corner-([a-z]){2,6}/g; - t.widget('ui.controlgroup', { - version: '1.12.1', - defaultElement: '
      ', - options: { - direction: 'horizontal', - disabled: null, - onlyVisible: !0, - items: { - button: - 'input[type=button], input[type=submit], input[type=reset], button, a', - controlgroupLabel: '.ui-controlgroup-label', - checkboxradio: "input[type='checkbox'], input[type='radio']", - selectmenu: 'select', - spinner: '.ui-spinner-input' - } - }, - _create: function() { - this._enhance(); - }, - _enhance: function() { - this.element.attr('role', 'toolbar'), this.refresh(); - }, - _destroy: function() { - this._callChildMethod('destroy'), - this.childWidgets.removeData('ui-controlgroup-data'), - this.element.removeAttr('role'), - this.options.items.controlgroupLabel && - this.element - .find(this.options.items.controlgroupLabel) - .find('.ui-controlgroup-label-contents') - .contents() - .unwrap(); - }, - _initWidgets: function() { - var e = this, - i = []; - t.each(this.options.items, function(s, n) { - var o, - a = {}; - return n - ? 'controlgroupLabel' === s - ? ((o = e.element.find(n)), - o.each(function() { - var e = t(this); - e.children('.ui-controlgroup-label-contents').length || - e - .contents() - .wrapAll( - "" - ); - }), - e._addClass( - o, - null, - 'ui-widget ui-widget-content ui-state-default' - ), - (i = i.concat(o.get())), - void 0) - : (t.fn[s] && - ((a = e['_' + s + 'Options'] - ? e['_' + s + 'Options']('middle') - : { classes: {} }), - e.element.find(n).each(function() { - var n = t(this), - o = n[s]('instance'), - r = t.widget.extend({}, a); - if ('button' !== s || !n.parent('.ui-spinner').length) { - o || (o = n[s]()[s]('instance')), - o && (r.classes = e._resolveClassesValues(r.classes, o)), - n[s](r); - var h = n[s]('widget'); - t.data( - h[0], - 'ui-controlgroup-data', - o ? o : n[s]('instance') - ), - i.push(h[0]); - } - })), - void 0) - : void 0; - }), - (this.childWidgets = t(t.unique(i))), - this._addClass(this.childWidgets, 'ui-controlgroup-item'); - }, - _callChildMethod: function(e) { - this.childWidgets.each(function() { - var i = t(this), - s = i.data('ui-controlgroup-data'); - s && s[e] && s[e](); - }); - }, - _updateCornerClass: function(t, e) { - var i = - 'ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all', - s = this._buildSimpleOptions(e, 'label').classes.label; - this._removeClass(t, null, i), this._addClass(t, null, s); - }, - _buildSimpleOptions: function(t, e) { - var i = 'vertical' === this.options.direction, - s = { classes: {} }; - return ( - (s.classes[e] = { - middle: '', - first: 'ui-corner-' + (i ? 'top' : 'left'), - last: 'ui-corner-' + (i ? 'bottom' : 'right'), - only: 'ui-corner-all' - }[t]), - s - ); - }, - _spinnerOptions: function(t) { - var e = this._buildSimpleOptions(t, 'ui-spinner'); - return ( - (e.classes['ui-spinner-up'] = ''), - (e.classes['ui-spinner-down'] = ''), - e - ); - }, - _buttonOptions: function(t) { - return this._buildSimpleOptions(t, 'ui-button'); - }, - _checkboxradioOptions: function(t) { - return this._buildSimpleOptions(t, 'ui-checkboxradio-label'); - }, - _selectmenuOptions: function(t) { - var e = 'vertical' === this.options.direction; - return { - width: e ? 'auto' : !1, - classes: { - middle: { - 'ui-selectmenu-button-open': '', - 'ui-selectmenu-button-closed': '' - }, - first: { - 'ui-selectmenu-button-open': 'ui-corner-' + (e ? 'top' : 'tl'), - 'ui-selectmenu-button-closed': 'ui-corner-' + (e ? 'top' : 'left') - }, - last: { - 'ui-selectmenu-button-open': e ? '' : 'ui-corner-tr', - 'ui-selectmenu-button-closed': - 'ui-corner-' + (e ? 'bottom' : 'right') - }, - only: { - 'ui-selectmenu-button-open': 'ui-corner-top', - 'ui-selectmenu-button-closed': 'ui-corner-all' - } - }[t] - }; - }, - _resolveClassesValues: function(e, i) { - var s = {}; - return ( - t.each(e, function(n) { - var o = i.options.classes[n] || ''; - (o = t.trim(o.replace(g, ''))), - (s[n] = (o + ' ' + e[n]).replace(/\s+/g, ' ')); - }), - s - ); - }, - _setOption: function(t, e) { - return ( - 'direction' === t && - this._removeClass('ui-controlgroup-' + this.options.direction), - this._super(t, e), - 'disabled' === t - ? (this._callChildMethod(e ? 'disable' : 'enable'), void 0) - : (this.refresh(), void 0) - ); - }, - refresh: function() { - var e, - i = this; - this._addClass( - 'ui-controlgroup ui-controlgroup-' + this.options.direction - ), - 'horizontal' === this.options.direction && - this._addClass(null, 'ui-helper-clearfix'), - this._initWidgets(), - (e = this.childWidgets), - this.options.onlyVisible && (e = e.filter(':visible')), - e.length && - (t.each(['first', 'last'], function(t, s) { - var n = e[s]().data('ui-controlgroup-data'); - if (n && i['_' + n.widgetName + 'Options']) { - var o = i['_' + n.widgetName + 'Options']( - 1 === e.length ? 'only' : s - ); - (o.classes = i._resolveClassesValues(o.classes, n)), - n.element[n.widgetName](o); - } else i._updateCornerClass(e[s](), s); - }), - this._callChildMethod('refresh')); - } - }), - t.widget('ui.checkboxradio', [ - t.ui.formResetMixin, - { - version: '1.12.1', - options: { - disabled: null, - label: null, - icon: !0, - classes: { - 'ui-checkboxradio-label': 'ui-corner-all', - 'ui-checkboxradio-icon': 'ui-corner-all' - } - }, - _getCreateOptions: function() { - var e, - i, - s = this, - n = this._super() || {}; - return ( - this._readType(), - (i = this.element.labels()), - (this.label = t(i[i.length - 1])), - this.label.length || - t.error('No label found for checkboxradio widget'), - (this.originalLabel = ''), - this.label - .contents() - .not(this.element[0]) - .each(function() { - s.originalLabel += - 3 === this.nodeType ? t(this).text() : this.outerHTML; - }), - this.originalLabel && (n.label = this.originalLabel), - (e = this.element[0].disabled), - null != e && (n.disabled = e), - n - ); - }, - _create: function() { - var t = this.element[0].checked; - this._bindFormResetHandler(), - null == this.options.disabled && - (this.options.disabled = this.element[0].disabled), - this._setOption('disabled', this.options.disabled), - this._addClass('ui-checkboxradio', 'ui-helper-hidden-accessible'), - this._addClass( - this.label, - 'ui-checkboxradio-label', - 'ui-button ui-widget' - ), - 'radio' === this.type && - this._addClass(this.label, 'ui-checkboxradio-radio-label'), - this.options.label && this.options.label !== this.originalLabel - ? this._updateLabel() - : this.originalLabel && (this.options.label = this.originalLabel), - this._enhance(), - t && - (this._addClass( - this.label, - 'ui-checkboxradio-checked', - 'ui-state-active' - ), - this.icon && this._addClass(this.icon, null, 'ui-state-hover')), - this._on({ - change: '_toggleClasses', - focus: function() { - this._addClass( - this.label, - null, - 'ui-state-focus ui-visual-focus' - ); - }, - blur: function() { - this._removeClass( - this.label, - null, - 'ui-state-focus ui-visual-focus' - ); - } - }); - }, - _readType: function() { - var e = this.element[0].nodeName.toLowerCase(); - (this.type = this.element[0].type), - ('input' === e && /radio|checkbox/.test(this.type)) || - t.error( - "Can't create checkboxradio on element.nodeName=" + - e + - ' and element.type=' + - this.type - ); - }, - _enhance: function() { - this._updateIcon(this.element[0].checked); - }, - widget: function() { - return this.label; - }, - _getRadioGroup: function() { - var e, - i = this.element[0].name, - s = "input[name='" + t.ui.escapeSelector(i) + "']"; - return i - ? ((e = this.form.length - ? t(this.form[0].elements).filter(s) - : t(s).filter(function() { - return 0 === t(this).form().length; - })), - e.not(this.element)) - : t([]); - }, - _toggleClasses: function() { - var e = this.element[0].checked; - this._toggleClass( - this.label, - 'ui-checkboxradio-checked', - 'ui-state-active', - e - ), - this.options.icon && - 'checkbox' === this.type && - this._toggleClass( - this.icon, - null, - 'ui-icon-check ui-state-checked', - e - )._toggleClass(this.icon, null, 'ui-icon-blank', !e), - 'radio' === this.type && - this._getRadioGroup().each(function() { - var e = t(this).checkboxradio('instance'); - e && - e._removeClass( - e.label, - 'ui-checkboxradio-checked', - 'ui-state-active' - ); - }); - }, - _destroy: function() { - this._unbindFormResetHandler(), - this.icon && (this.icon.remove(), this.iconSpace.remove()); - }, - _setOption: function(t, e) { - return 'label' !== t || e - ? (this._super(t, e), - 'disabled' === t - ? (this._toggleClass(this.label, null, 'ui-state-disabled', e), - (this.element[0].disabled = e), - void 0) - : (this.refresh(), void 0)) - : void 0; - }, - _updateIcon: function(e) { - var i = 'ui-icon ui-icon-background '; - this.options.icon - ? (this.icon || - ((this.icon = t('')), - (this.iconSpace = t(' ')), - this._addClass(this.iconSpace, 'ui-checkboxradio-icon-space')), - 'checkbox' === this.type - ? ((i += e - ? 'ui-icon-check ui-state-checked' - : 'ui-icon-blank'), - this._removeClass( - this.icon, - null, - e ? 'ui-icon-blank' : 'ui-icon-check' - )) - : (i += 'ui-icon-blank'), - this._addClass(this.icon, 'ui-checkboxradio-icon', i), - e || - this._removeClass( - this.icon, - null, - 'ui-icon-check ui-state-checked' - ), - this.icon.prependTo(this.label).after(this.iconSpace)) - : void 0 !== this.icon && - (this.icon.remove(), this.iconSpace.remove(), delete this.icon); - }, - _updateLabel: function() { - var t = this.label.contents().not(this.element[0]); - this.icon && (t = t.not(this.icon[0])), - this.iconSpace && (t = t.not(this.iconSpace[0])), - t.remove(), - this.label.append(this.options.label); - }, - refresh: function() { - var t = this.element[0].checked, - e = this.element[0].disabled; - this._updateIcon(t), - this._toggleClass( - this.label, - 'ui-checkboxradio-checked', - 'ui-state-active', - t - ), - null !== this.options.label && this._updateLabel(), - e !== this.options.disabled && this._setOptions({ disabled: e }); - } - } - ]), - t.ui.checkboxradio, - t.widget('ui.button', { - version: '1.12.1', - defaultElement: '") - .addClass(this._triggerClass) - .html(o ? t('').attr({ src: o, alt: n, title: n }) : n) - )), - e[r ? 'before' : 'after'](i.trigger), - i.trigger.on('click', function() { - return ( - t.datepicker._datepickerShowing && - t.datepicker._lastInput === e[0] - ? t.datepicker._hideDatepicker() - : t.datepicker._datepickerShowing && - t.datepicker._lastInput !== e[0] - ? (t.datepicker._hideDatepicker(), - t.datepicker._showDatepicker(e[0])) - : t.datepicker._showDatepicker(e[0]), - !1 - ); - })); - }, - _autoSize: function(t) { - if (this._get(t, 'autoSize') && !t.inline) { - var e, - i, - s, - n, - o = new Date(2009, 11, 20), - a = this._get(t, 'dateFormat'); - a.match(/[DM]/) && - ((e = function(t) { - for (i = 0, s = 0, n = 0; t.length > n; n++) - t[n].length > i && ((i = t[n].length), (s = n)); - return s; - }), - o.setMonth( - e(this._get(t, a.match(/MM/) ? 'monthNames' : 'monthNamesShort')) - ), - o.setDate( - e(this._get(t, a.match(/DD/) ? 'dayNames' : 'dayNamesShort')) + - 20 - - o.getDay() - )), - t.input.attr('size', this._formatDate(t, o).length); - } - }, - _inlineDatepicker: function(e, i) { - var s = t(e); - s.hasClass(this.markerClassName) || - (s.addClass(this.markerClassName).append(i.dpDiv), - t.data(e, 'datepicker', i), - this._setDate(i, this._getDefaultDate(i), !0), - this._updateDatepicker(i), - this._updateAlternate(i), - i.settings.disabled && this._disableDatepicker(e), - i.dpDiv.css('display', 'block')); - }, - _dialogDatepicker: function(e, i, s, n, o) { - var r, - h, - l, - c, - u, - d = this._dialogInst; - return ( - d || - ((this.uuid += 1), - (r = 'dp' + this.uuid), - (this._dialogInput = t( - "" - )), - this._dialogInput.on('keydown', this._doKeyDown), - t('body').append(this._dialogInput), - (d = this._dialogInst = this._newInst(this._dialogInput, !1)), - (d.settings = {}), - t.data(this._dialogInput[0], 'datepicker', d)), - a(d.settings, n || {}), - (i = i && i.constructor === Date ? this._formatDate(d, i) : i), - this._dialogInput.val(i), - (this._pos = o ? (o.length ? o : [o.pageX, o.pageY]) : null), - this._pos || - ((h = document.documentElement.clientWidth), - (l = document.documentElement.clientHeight), - (c = document.documentElement.scrollLeft || document.body.scrollLeft), - (u = document.documentElement.scrollTop || document.body.scrollTop), - (this._pos = [h / 2 - 100 + c, l / 2 - 150 + u])), - this._dialogInput - .css('left', this._pos[0] + 20 + 'px') - .css('top', this._pos[1] + 'px'), - (d.settings.onSelect = s), - (this._inDialog = !0), - this.dpDiv.addClass(this._dialogClass), - this._showDatepicker(this._dialogInput[0]), - t.blockUI && t.blockUI(this.dpDiv), - t.data(this._dialogInput[0], 'datepicker', d), - this - ); - }, - _destroyDatepicker: function(e) { - var i, - s = t(e), - n = t.data(e, 'datepicker'); - s.hasClass(this.markerClassName) && - ((i = e.nodeName.toLowerCase()), - t.removeData(e, 'datepicker'), - 'input' === i - ? (n.append.remove(), - n.trigger.remove(), - s - .removeClass(this.markerClassName) - .off('focus', this._showDatepicker) - .off('keydown', this._doKeyDown) - .off('keypress', this._doKeyPress) - .off('keyup', this._doKeyUp)) - : ('div' === i || 'span' === i) && - s.removeClass(this.markerClassName).empty(), - m === n && (m = null)); - }, - _enableDatepicker: function(e) { - var i, - s, - n = t(e), - o = t.data(e, 'datepicker'); - n.hasClass(this.markerClassName) && - ((i = e.nodeName.toLowerCase()), - 'input' === i - ? ((e.disabled = !1), - o.trigger - .filter('button') - .each(function() { - this.disabled = !1; - }) - .end() - .filter('img') - .css({ opacity: '1.0', cursor: '' })) - : ('div' === i || 'span' === i) && - ((s = n.children('.' + this._inlineClass)), - s.children().removeClass('ui-state-disabled'), - s - .find('select.ui-datepicker-month, select.ui-datepicker-year') - .prop('disabled', !1)), - (this._disabledInputs = t.map(this._disabledInputs, function(t) { - return t === e ? null : t; - }))); - }, - _disableDatepicker: function(e) { - var i, - s, - n = t(e), - o = t.data(e, 'datepicker'); - n.hasClass(this.markerClassName) && - ((i = e.nodeName.toLowerCase()), - 'input' === i - ? ((e.disabled = !0), - o.trigger - .filter('button') - .each(function() { - this.disabled = !0; - }) - .end() - .filter('img') - .css({ opacity: '0.5', cursor: 'default' })) - : ('div' === i || 'span' === i) && - ((s = n.children('.' + this._inlineClass)), - s.children().addClass('ui-state-disabled'), - s - .find('select.ui-datepicker-month, select.ui-datepicker-year') - .prop('disabled', !0)), - (this._disabledInputs = t.map(this._disabledInputs, function(t) { - return t === e ? null : t; - })), - (this._disabledInputs[this._disabledInputs.length] = e)); - }, - _isDisabledDatepicker: function(t) { - if (!t) return !1; - for (var e = 0; this._disabledInputs.length > e; e++) - if (this._disabledInputs[e] === t) return !0; - return !1; - }, - _getInst: function(e) { - try { - return t.data(e, 'datepicker'); - } catch (i) { - throw 'Missing instance data for this datepicker'; - } - }, - _optionDatepicker: function(e, i, s) { - var n, - o, - r, - h, - l = this._getInst(e); - return 2 === arguments.length && 'string' == typeof i - ? 'defaults' === i - ? t.extend({}, t.datepicker._defaults) - : l - ? 'all' === i - ? t.extend({}, l.settings) - : this._get(l, i) - : null - : ((n = i || {}), - 'string' == typeof i && ((n = {}), (n[i] = s)), - l && - (this._curInst === l && this._hideDatepicker(), - (o = this._getDateDatepicker(e, !0)), - (r = this._getMinMaxDate(l, 'min')), - (h = this._getMinMaxDate(l, 'max')), - a(l.settings, n), - null !== r && - void 0 !== n.dateFormat && - void 0 === n.minDate && - (l.settings.minDate = this._formatDate(l, r)), - null !== h && - void 0 !== n.dateFormat && - void 0 === n.maxDate && - (l.settings.maxDate = this._formatDate(l, h)), - 'disabled' in n && - (n.disabled - ? this._disableDatepicker(e) - : this._enableDatepicker(e)), - this._attachments(t(e), l), - this._autoSize(l), - this._setDate(l, o), - this._updateAlternate(l), - this._updateDatepicker(l)), - void 0); - }, - _changeDatepicker: function(t, e, i) { - this._optionDatepicker(t, e, i); - }, - _refreshDatepicker: function(t) { - var e = this._getInst(t); - e && this._updateDatepicker(e); - }, - _setDateDatepicker: function(t, e) { - var i = this._getInst(t); - i && - (this._setDate(i, e), - this._updateDatepicker(i), - this._updateAlternate(i)); - }, - _getDateDatepicker: function(t, e) { - var i = this._getInst(t); - return ( - i && !i.inline && this._setDateFromField(i, e), - i ? this._getDate(i) : null - ); - }, - _doKeyDown: function(e) { - var i, - s, - n, - o = t.datepicker._getInst(e.target), - a = !0, - r = o.dpDiv.is('.ui-datepicker-rtl'); - if (((o._keyEvent = !0), t.datepicker._datepickerShowing)) - switch (e.keyCode) { - case 9: - t.datepicker._hideDatepicker(), (a = !1); - break; - case 13: - return ( - (n = t( - 'td.' + - t.datepicker._dayOverClass + - ':not(.' + - t.datepicker._currentClass + - ')', - o.dpDiv - )), - n[0] && - t.datepicker._selectDay( - e.target, - o.selectedMonth, - o.selectedYear, - n[0] - ), - (i = t.datepicker._get(o, 'onSelect')), - i - ? ((s = t.datepicker._formatDate(o)), - i.apply(o.input ? o.input[0] : null, [s, o])) - : t.datepicker._hideDatepicker(), - !1 - ); - case 27: - t.datepicker._hideDatepicker(); - break; - case 33: - t.datepicker._adjustDate( - e.target, - e.ctrlKey - ? -t.datepicker._get(o, 'stepBigMonths') - : -t.datepicker._get(o, 'stepMonths'), - 'M' - ); - break; - case 34: - t.datepicker._adjustDate( - e.target, - e.ctrlKey - ? +t.datepicker._get(o, 'stepBigMonths') - : +t.datepicker._get(o, 'stepMonths'), - 'M' - ); - break; - case 35: - (e.ctrlKey || e.metaKey) && t.datepicker._clearDate(e.target), - (a = e.ctrlKey || e.metaKey); - break; - case 36: - (e.ctrlKey || e.metaKey) && t.datepicker._gotoToday(e.target), - (a = e.ctrlKey || e.metaKey); - break; - case 37: - (e.ctrlKey || e.metaKey) && - t.datepicker._adjustDate(e.target, r ? 1 : -1, 'D'), - (a = e.ctrlKey || e.metaKey), - e.originalEvent.altKey && - t.datepicker._adjustDate( - e.target, - e.ctrlKey - ? -t.datepicker._get(o, 'stepBigMonths') - : -t.datepicker._get(o, 'stepMonths'), - 'M' - ); - break; - case 38: - (e.ctrlKey || e.metaKey) && - t.datepicker._adjustDate(e.target, -7, 'D'), - (a = e.ctrlKey || e.metaKey); - break; - case 39: - (e.ctrlKey || e.metaKey) && - t.datepicker._adjustDate(e.target, r ? -1 : 1, 'D'), - (a = e.ctrlKey || e.metaKey), - e.originalEvent.altKey && - t.datepicker._adjustDate( - e.target, - e.ctrlKey - ? +t.datepicker._get(o, 'stepBigMonths') - : +t.datepicker._get(o, 'stepMonths'), - 'M' - ); - break; - case 40: - (e.ctrlKey || e.metaKey) && - t.datepicker._adjustDate(e.target, 7, 'D'), - (a = e.ctrlKey || e.metaKey); - break; - default: - a = !1; - } - else - 36 === e.keyCode && e.ctrlKey - ? t.datepicker._showDatepicker(this) - : (a = !1); - a && (e.preventDefault(), e.stopPropagation()); - }, - _doKeyPress: function(e) { - var i, - s, - n = t.datepicker._getInst(e.target); - return t.datepicker._get(n, 'constrainInput') - ? ((i = t.datepicker._possibleChars( - t.datepicker._get(n, 'dateFormat') - )), - (s = String.fromCharCode( - null == e.charCode ? e.keyCode : e.charCode - )), - e.ctrlKey || e.metaKey || ' ' > s || !i || i.indexOf(s) > -1) - : void 0; - }, - _doKeyUp: function(e) { - var i, - s = t.datepicker._getInst(e.target); - if (s.input.val() !== s.lastVal) - try { - (i = t.datepicker.parseDate( - t.datepicker._get(s, 'dateFormat'), - s.input ? s.input.val() : null, - t.datepicker._getFormatConfig(s) - )), - i && - (t.datepicker._setDateFromField(s), - t.datepicker._updateAlternate(s), - t.datepicker._updateDatepicker(s)); - } catch (n) {} - return !0; - }, - _showDatepicker: function(e) { - if ( - ((e = e.target || e), - 'input' !== e.nodeName.toLowerCase() && - (e = t('input', e.parentNode)[0]), - !t.datepicker._isDisabledDatepicker(e) && t.datepicker._lastInput !== e) - ) { - var s, n, o, r, h, l, c; - (s = t.datepicker._getInst(e)), - t.datepicker._curInst && - t.datepicker._curInst !== s && - (t.datepicker._curInst.dpDiv.stop(!0, !0), - s && - t.datepicker._datepickerShowing && - t.datepicker._hideDatepicker(t.datepicker._curInst.input[0])), - (n = t.datepicker._get(s, 'beforeShow')), - (o = n ? n.apply(e, [e, s]) : {}), - o !== !1 && - (a(s.settings, o), - (s.lastVal = null), - (t.datepicker._lastInput = e), - t.datepicker._setDateFromField(s), - t.datepicker._inDialog && (e.value = ''), - t.datepicker._pos || - ((t.datepicker._pos = t.datepicker._findPos(e)), - (t.datepicker._pos[1] += e.offsetHeight)), - (r = !1), - t(e) - .parents() - .each(function() { - return (r |= 'fixed' === t(this).css('position')), !r; - }), - (h = { left: t.datepicker._pos[0], top: t.datepicker._pos[1] }), - (t.datepicker._pos = null), - s.dpDiv.empty(), - s.dpDiv.css({ - position: 'absolute', - display: 'block', - top: '-1000px' - }), - t.datepicker._updateDatepicker(s), - (h = t.datepicker._checkOffset(s, h, r)), - s.dpDiv.css({ - position: - t.datepicker._inDialog && t.blockUI - ? 'static' - : r - ? 'fixed' - : 'absolute', - display: 'none', - left: h.left + 'px', - top: h.top + 'px' - }), - s.inline || - ((l = t.datepicker._get(s, 'showAnim')), - (c = t.datepicker._get(s, 'duration')), - s.dpDiv.css('z-index', i(t(e)) + 1), - (t.datepicker._datepickerShowing = !0), - t.effects && t.effects.effect[l] - ? s.dpDiv.show(l, t.datepicker._get(s, 'showOptions'), c) - : s.dpDiv[l || 'show'](l ? c : null), - t.datepicker._shouldFocusInput(s) && s.input.trigger('focus'), - (t.datepicker._curInst = s))); - } - }, - _updateDatepicker: function(e) { - (this.maxRows = 4), - (m = e), - e.dpDiv.empty().append(this._generateHTML(e)), - this._attachHandlers(e); - var i, - s = this._getNumberOfMonths(e), - n = s[1], - a = 17, - r = e.dpDiv.find('.' + this._dayOverClass + ' a'); - r.length > 0 && o.apply(r.get(0)), - e.dpDiv - .removeClass( - 'ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4' - ) - .width(''), - n > 1 && - e.dpDiv - .addClass('ui-datepicker-multi-' + n) - .css('width', a * n + 'em'), - e.dpDiv[(1 !== s[0] || 1 !== s[1] ? 'add' : 'remove') + 'Class']( - 'ui-datepicker-multi' - ), - e.dpDiv[(this._get(e, 'isRTL') ? 'add' : 'remove') + 'Class']( - 'ui-datepicker-rtl' - ), - e === t.datepicker._curInst && - t.datepicker._datepickerShowing && - t.datepicker._shouldFocusInput(e) && - e.input.trigger('focus'), - e.yearshtml && - ((i = e.yearshtml), - setTimeout(function() { - i === e.yearshtml && - e.yearshtml && - e.dpDiv - .find('select.ui-datepicker-year:first') - .replaceWith(e.yearshtml), - (i = e.yearshtml = null); - }, 0)); - }, - _shouldFocusInput: function(t) { - return ( - t.input && - t.input.is(':visible') && - !t.input.is(':disabled') && - !t.input.is(':focus') - ); - }, - _checkOffset: function(e, i, s) { - var n = e.dpDiv.outerWidth(), - o = e.dpDiv.outerHeight(), - a = e.input ? e.input.outerWidth() : 0, - r = e.input ? e.input.outerHeight() : 0, - h = - document.documentElement.clientWidth + - (s ? 0 : t(document).scrollLeft()), - l = - document.documentElement.clientHeight + - (s ? 0 : t(document).scrollTop()); - return ( - (i.left -= this._get(e, 'isRTL') ? n - a : 0), - (i.left -= - s && i.left === e.input.offset().left ? t(document).scrollLeft() : 0), - (i.top -= - s && i.top === e.input.offset().top + r - ? t(document).scrollTop() - : 0), - (i.left -= Math.min( - i.left, - i.left + n > h && h > n ? Math.abs(i.left + n - h) : 0 - )), - (i.top -= Math.min( - i.top, - i.top + o > l && l > o ? Math.abs(o + r) : 0 - )), - i - ); - }, - _findPos: function(e) { - for ( - var i, s = this._getInst(e), n = this._get(s, 'isRTL'); - e && - ('hidden' === e.type || 1 !== e.nodeType || t.expr.filters.hidden(e)); - - ) - e = e[n ? 'previousSibling' : 'nextSibling']; - return (i = t(e).offset()), [i.left, i.top]; - }, - _hideDatepicker: function(e) { - var i, - s, - n, - o, - a = this._curInst; - !a || - (e && a !== t.data(e, 'datepicker')) || - (this._datepickerShowing && - ((i = this._get(a, 'showAnim')), - (s = this._get(a, 'duration')), - (n = function() { - t.datepicker._tidyDialog(a); - }), - t.effects && (t.effects.effect[i] || t.effects[i]) - ? a.dpDiv.hide(i, t.datepicker._get(a, 'showOptions'), s, n) - : a.dpDiv[ - 'slideDown' === i - ? 'slideUp' - : 'fadeIn' === i - ? 'fadeOut' - : 'hide' - ](i ? s : null, n), - i || n(), - (this._datepickerShowing = !1), - (o = this._get(a, 'onClose')), - o && - o.apply(a.input ? a.input[0] : null, [ - a.input ? a.input.val() : '', - a - ]), - (this._lastInput = null), - this._inDialog && - (this._dialogInput.css({ - position: 'absolute', - left: '0', - top: '-100px' - }), - t.blockUI && (t.unblockUI(), t('body').append(this.dpDiv))), - (this._inDialog = !1))); - }, - _tidyDialog: function(t) { - t.dpDiv.removeClass(this._dialogClass).off('.ui-datepicker-calendar'); - }, - _checkExternalClick: function(e) { - if (t.datepicker._curInst) { - var i = t(e.target), - s = t.datepicker._getInst(i[0]); - ((i[0].id !== t.datepicker._mainDivId && - 0 === i.parents('#' + t.datepicker._mainDivId).length && - !i.hasClass(t.datepicker.markerClassName) && - !i.closest('.' + t.datepicker._triggerClass).length && - t.datepicker._datepickerShowing && - (!t.datepicker._inDialog || !t.blockUI)) || - (i.hasClass(t.datepicker.markerClassName) && - t.datepicker._curInst !== s)) && - t.datepicker._hideDatepicker(); - } - }, - _adjustDate: function(e, i, s) { - var n = t(e), - o = this._getInst(n[0]); - this._isDisabledDatepicker(n[0]) || - (this._adjustInstDate( - o, - i + ('M' === s ? this._get(o, 'showCurrentAtPos') : 0), - s - ), - this._updateDatepicker(o)); - }, - _gotoToday: function(e) { - var i, - s = t(e), - n = this._getInst(s[0]); - this._get(n, 'gotoCurrent') && n.currentDay - ? ((n.selectedDay = n.currentDay), - (n.drawMonth = n.selectedMonth = n.currentMonth), - (n.drawYear = n.selectedYear = n.currentYear)) - : ((i = new Date()), - (n.selectedDay = i.getDate()), - (n.drawMonth = n.selectedMonth = i.getMonth()), - (n.drawYear = n.selectedYear = i.getFullYear())), - this._notifyChange(n), - this._adjustDate(s); - }, - _selectMonthYear: function(e, i, s) { - var n = t(e), - o = this._getInst(n[0]); - (o['selected' + ('M' === s ? 'Month' : 'Year')] = o[ - 'draw' + ('M' === s ? 'Month' : 'Year') - ] = parseInt(i.options[i.selectedIndex].value, 10)), - this._notifyChange(o), - this._adjustDate(n); - }, - _selectDay: function(e, i, s, n) { - var o, - a = t(e); - t(n).hasClass(this._unselectableClass) || - this._isDisabledDatepicker(a[0]) || - ((o = this._getInst(a[0])), - (o.selectedDay = o.currentDay = t('a', n).html()), - (o.selectedMonth = o.currentMonth = i), - (o.selectedYear = o.currentYear = s), - this._selectDate( - e, - this._formatDate(o, o.currentDay, o.currentMonth, o.currentYear) - )); - }, - _clearDate: function(e) { - var i = t(e); - this._selectDate(i, ''); - }, - _selectDate: function(e, i) { - var s, - n = t(e), - o = this._getInst(n[0]); - (i = null != i ? i : this._formatDate(o)), - o.input && o.input.val(i), - this._updateAlternate(o), - (s = this._get(o, 'onSelect')), - s - ? s.apply(o.input ? o.input[0] : null, [i, o]) - : o.input && o.input.trigger('change'), - o.inline - ? this._updateDatepicker(o) - : (this._hideDatepicker(), - (this._lastInput = o.input[0]), - 'object' != typeof o.input[0] && o.input.trigger('focus'), - (this._lastInput = null)); - }, - _updateAlternate: function(e) { - var i, - s, - n, - o = this._get(e, 'altField'); - o && - ((i = this._get(e, 'altFormat') || this._get(e, 'dateFormat')), - (s = this._getDate(e)), - (n = this.formatDate(i, s, this._getFormatConfig(e))), - t(o).val(n)); - }, - noWeekends: function(t) { - var e = t.getDay(); - return [e > 0 && 6 > e, '']; - }, - iso8601Week: function(t) { - var e, - i = new Date(t.getTime()); - return ( - i.setDate(i.getDate() + 4 - (i.getDay() || 7)), - (e = i.getTime()), - i.setMonth(0), - i.setDate(1), - Math.floor(Math.round((e - i) / 864e5) / 7) + 1 - ); - }, - parseDate: function(e, i, s) { - if (null == e || null == i) throw 'Invalid arguments'; - if (((i = 'object' == typeof i ? '' + i : i + ''), '' === i)) return null; - var n, - o, - a, - r, - h = 0, - l = (s ? s.shortYearCutoff : null) || this._defaults.shortYearCutoff, - c = - 'string' != typeof l - ? l - : (new Date().getFullYear() % 100) + parseInt(l, 10), - u = (s ? s.dayNamesShort : null) || this._defaults.dayNamesShort, - d = (s ? s.dayNames : null) || this._defaults.dayNames, - p = (s ? s.monthNamesShort : null) || this._defaults.monthNamesShort, - f = (s ? s.monthNames : null) || this._defaults.monthNames, - g = -1, - m = -1, - _ = -1, - v = -1, - b = !1, - y = function(t) { - var i = e.length > n + 1 && e.charAt(n + 1) === t; - return i && n++, i; - }, - w = function(t) { - var e = y(t), - s = - '@' === t - ? 14 - : '!' === t - ? 20 - : 'y' === t && e - ? 4 - : 'o' === t - ? 3 - : 2, - n = 'y' === t ? s : 1, - o = RegExp('^\\d{' + n + ',' + s + '}'), - a = i.substring(h).match(o); - if (!a) throw 'Missing number at position ' + h; - return (h += a[0].length), parseInt(a[0], 10); - }, - k = function(e, s, n) { - var o = -1, - a = t - .map(y(e) ? n : s, function(t, e) { - return [[e, t]]; - }) - .sort(function(t, e) { - return -(t[1].length - e[1].length); - }); - if ( - (t.each(a, function(t, e) { - var s = e[1]; - return i.substr(h, s.length).toLowerCase() === s.toLowerCase() - ? ((o = e[0]), (h += s.length), !1) - : void 0; - }), - -1 !== o) - ) - return o + 1; - throw 'Unknown name at position ' + h; - }, - x = function() { - if (i.charAt(h) !== e.charAt(n)) - throw 'Unexpected literal at position ' + h; - h++; - }; - for (n = 0; e.length > n; n++) - if (b) "'" !== e.charAt(n) || y("'") ? x() : (b = !1); - else - switch (e.charAt(n)) { - case 'd': - _ = w('d'); - break; - case 'D': - k('D', u, d); - break; - case 'o': - v = w('o'); - break; - case 'm': - m = w('m'); - break; - case 'M': - m = k('M', p, f); - break; - case 'y': - g = w('y'); - break; - case '@': - (r = new Date(w('@'))), - (g = r.getFullYear()), - (m = r.getMonth() + 1), - (_ = r.getDate()); - break; - case '!': - (r = new Date((w('!') - this._ticksTo1970) / 1e4)), - (g = r.getFullYear()), - (m = r.getMonth() + 1), - (_ = r.getDate()); - break; - case "'": - y("'") ? x() : (b = !0); - break; - default: - x(); - } - if (i.length > h && ((a = i.substr(h)), !/^\s+/.test(a))) - throw 'Extra/unparsed characters found in date: ' + a; - if ( - (-1 === g - ? (g = new Date().getFullYear()) - : 100 > g && - (g += - new Date().getFullYear() - - (new Date().getFullYear() % 100) + - (c >= g ? 0 : -100)), - v > -1) - ) - for (m = 1, _ = v; ; ) { - if (((o = this._getDaysInMonth(g, m - 1)), o >= _)) break; - m++, (_ -= o); - } - if ( - ((r = this._daylightSavingAdjust(new Date(g, m - 1, _))), - r.getFullYear() !== g || r.getMonth() + 1 !== m || r.getDate() !== _) - ) - throw 'Invalid date'; - return r; - }, - ATOM: 'yy-mm-dd', - COOKIE: 'D, dd M yy', - ISO_8601: 'yy-mm-dd', - RFC_822: 'D, d M y', - RFC_850: 'DD, dd-M-y', - RFC_1036: 'D, d M y', - RFC_1123: 'D, d M yy', - RFC_2822: 'D, d M yy', - RSS: 'D, d M y', - TICKS: '!', - TIMESTAMP: '@', - W3C: 'yy-mm-dd', - _ticksTo1970: - 1e7 * - 60 * - 60 * - 24 * - (718685 + Math.floor(492.5) - Math.floor(19.7) + Math.floor(4.925)), - formatDate: function(t, e, i) { - if (!e) return ''; - var s, - n = (i ? i.dayNamesShort : null) || this._defaults.dayNamesShort, - o = (i ? i.dayNames : null) || this._defaults.dayNames, - a = (i ? i.monthNamesShort : null) || this._defaults.monthNamesShort, - r = (i ? i.monthNames : null) || this._defaults.monthNames, - h = function(e) { - var i = t.length > s + 1 && t.charAt(s + 1) === e; - return i && s++, i; - }, - l = function(t, e, i) { - var s = '' + e; - if (h(t)) for (; i > s.length; ) s = '0' + s; - return s; - }, - c = function(t, e, i, s) { - return h(t) ? s[e] : i[e]; - }, - u = '', - d = !1; - if (e) - for (s = 0; t.length > s; s++) - if (d) "'" !== t.charAt(s) || h("'") ? (u += t.charAt(s)) : (d = !1); - else - switch (t.charAt(s)) { - case 'd': - u += l('d', e.getDate(), 2); - break; - case 'D': - u += c('D', e.getDay(), n, o); - break; - case 'o': - u += l( - 'o', - Math.round( - (new Date( - e.getFullYear(), - e.getMonth(), - e.getDate() - ).getTime() - - new Date(e.getFullYear(), 0, 0).getTime()) / - 864e5 - ), - 3 - ); - break; - case 'm': - u += l('m', e.getMonth() + 1, 2); - break; - case 'M': - u += c('M', e.getMonth(), a, r); - break; - case 'y': - u += h('y') - ? e.getFullYear() - : (10 > e.getFullYear() % 100 ? '0' : '') + - (e.getFullYear() % 100); - break; - case '@': - u += e.getTime(); - break; - case '!': - u += 1e4 * e.getTime() + this._ticksTo1970; - break; - case "'": - h("'") ? (u += "'") : (d = !0); - break; - default: - u += t.charAt(s); - } - return u; - }, - _possibleChars: function(t) { - var e, - i = '', - s = !1, - n = function(i) { - var s = t.length > e + 1 && t.charAt(e + 1) === i; - return s && e++, s; - }; - for (e = 0; t.length > e; e++) - if (s) "'" !== t.charAt(e) || n("'") ? (i += t.charAt(e)) : (s = !1); - else - switch (t.charAt(e)) { - case 'd': - case 'm': - case 'y': - case '@': - i += '0123456789'; - break; - case 'D': - case 'M': - return null; - case "'": - n("'") ? (i += "'") : (s = !0); - break; - default: - i += t.charAt(e); - } - return i; - }, - _get: function(t, e) { - return void 0 !== t.settings[e] ? t.settings[e] : this._defaults[e]; - }, - _setDateFromField: function(t, e) { - if (t.input.val() !== t.lastVal) { - var i = this._get(t, 'dateFormat'), - s = (t.lastVal = t.input ? t.input.val() : null), - n = this._getDefaultDate(t), - o = n, - a = this._getFormatConfig(t); - try { - o = this.parseDate(i, s, a) || n; - } catch (r) { - s = e ? '' : s; - } - (t.selectedDay = o.getDate()), - (t.drawMonth = t.selectedMonth = o.getMonth()), - (t.drawYear = t.selectedYear = o.getFullYear()), - (t.currentDay = s ? o.getDate() : 0), - (t.currentMonth = s ? o.getMonth() : 0), - (t.currentYear = s ? o.getFullYear() : 0), - this._adjustInstDate(t); - } - }, - _getDefaultDate: function(t) { - return this._restrictMinMax( - t, - this._determineDate(t, this._get(t, 'defaultDate'), new Date()) - ); - }, - _determineDate: function(e, i, s) { - var n = function(t) { - var e = new Date(); - return e.setDate(e.getDate() + t), e; - }, - o = function(i) { - try { - return t.datepicker.parseDate( - t.datepicker._get(e, 'dateFormat'), - i, - t.datepicker._getFormatConfig(e) - ); - } catch (s) {} - for ( - var n = - (i.toLowerCase().match(/^c/) - ? t.datepicker._getDate(e) - : null) || new Date(), - o = n.getFullYear(), - a = n.getMonth(), - r = n.getDate(), - h = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g, - l = h.exec(i); - l; - - ) { - switch (l[2] || 'd') { - case 'd': - case 'D': - r += parseInt(l[1], 10); - break; - case 'w': - case 'W': - r += 7 * parseInt(l[1], 10); - break; - case 'm': - case 'M': - (a += parseInt(l[1], 10)), - (r = Math.min(r, t.datepicker._getDaysInMonth(o, a))); - break; - case 'y': - case 'Y': - (o += parseInt(l[1], 10)), - (r = Math.min(r, t.datepicker._getDaysInMonth(o, a))); - } - l = h.exec(i); - } - return new Date(o, a, r); - }, - a = - null == i || '' === i - ? s - : 'string' == typeof i - ? o(i) - : 'number' == typeof i - ? isNaN(i) - ? s - : n(i) - : new Date(i.getTime()); - return ( - (a = a && 'Invalid Date' == '' + a ? s : a), - a && - (a.setHours(0), - a.setMinutes(0), - a.setSeconds(0), - a.setMilliseconds(0)), - this._daylightSavingAdjust(a) - ); - }, - _daylightSavingAdjust: function(t) { - return t - ? (t.setHours(t.getHours() > 12 ? t.getHours() + 2 : 0), t) - : null; - }, - _setDate: function(t, e, i) { - var s = !e, - n = t.selectedMonth, - o = t.selectedYear, - a = this._restrictMinMax(t, this._determineDate(t, e, new Date())); - (t.selectedDay = t.currentDay = a.getDate()), - (t.drawMonth = t.selectedMonth = t.currentMonth = a.getMonth()), - (t.drawYear = t.selectedYear = t.currentYear = a.getFullYear()), - (n === t.selectedMonth && o === t.selectedYear) || - i || - this._notifyChange(t), - this._adjustInstDate(t), - t.input && t.input.val(s ? '' : this._formatDate(t)); - }, - _getDate: function(t) { - var e = - !t.currentYear || (t.input && '' === t.input.val()) - ? null - : this._daylightSavingAdjust( - new Date(t.currentYear, t.currentMonth, t.currentDay) - ); - return e; - }, - _attachHandlers: function(e) { - var i = this._get(e, 'stepMonths'), - s = '#' + e.id.replace(/\\\\/g, '\\'); - e.dpDiv.find('[data-handler]').map(function() { - var e = { - prev: function() { - t.datepicker._adjustDate(s, -i, 'M'); - }, - next: function() { - t.datepicker._adjustDate(s, +i, 'M'); - }, - hide: function() { - t.datepicker._hideDatepicker(); - }, - today: function() { - t.datepicker._gotoToday(s); - }, - selectDay: function() { - return ( - t.datepicker._selectDay( - s, - +this.getAttribute('data-month'), - +this.getAttribute('data-year'), - this - ), - !1 - ); - }, - selectMonth: function() { - return t.datepicker._selectMonthYear(s, this, 'M'), !1; - }, - selectYear: function() { - return t.datepicker._selectMonthYear(s, this, 'Y'), !1; - } - }; - t(this).on( - this.getAttribute('data-event'), - e[this.getAttribute('data-handler')] - ); - }); - }, - _generateHTML: function(t) { - var e, - i, - s, - n, - o, - a, - r, - h, - l, - c, - u, - d, - p, - f, - g, - m, - _, - v, - b, - y, - w, - k, - x, - C, - D, - I, - T, - P, - M, - S, - H, - z, - O, - A, - N, - W, - E, - F, - L, - R = new Date(), - B = this._daylightSavingAdjust( - new Date(R.getFullYear(), R.getMonth(), R.getDate()) - ), - Y = this._get(t, 'isRTL'), - j = this._get(t, 'showButtonPanel'), - q = this._get(t, 'hideIfNoPrevNext'), - K = this._get(t, 'navigationAsDateFormat'), - U = this._getNumberOfMonths(t), - V = this._get(t, 'showCurrentAtPos'), - $ = this._get(t, 'stepMonths'), - X = 1 !== U[0] || 1 !== U[1], - G = this._daylightSavingAdjust( - t.currentDay - ? new Date(t.currentYear, t.currentMonth, t.currentDay) - : new Date(9999, 9, 9) - ), - Q = this._getMinMaxDate(t, 'min'), - J = this._getMinMaxDate(t, 'max'), - Z = t.drawMonth - V, - te = t.drawYear; - if ((0 > Z && ((Z += 12), te--), J)) - for ( - e = this._daylightSavingAdjust( - new Date( - J.getFullYear(), - J.getMonth() - U[0] * U[1] + 1, - J.getDate() - ) - ), - e = Q && Q > e ? Q : e; - this._daylightSavingAdjust(new Date(te, Z, 1)) > e; - - ) - Z--, 0 > Z && ((Z = 11), te--); - for ( - t.drawMonth = Z, - t.drawYear = te, - i = this._get(t, 'prevText'), - i = K - ? this.formatDate( - i, - this._daylightSavingAdjust(new Date(te, Z - $, 1)), - this._getFormatConfig(t) - ) - : i, - s = this._canAdjustMonth(t, -1, te, Z) - ? "" + - i + - '' - : q - ? '' - : "" + - i + - '', - n = this._get(t, 'nextText'), - n = K - ? this.formatDate( - n, - this._daylightSavingAdjust(new Date(te, Z + $, 1)), - this._getFormatConfig(t) - ) - : n, - o = this._canAdjustMonth(t, 1, te, Z) - ? "" + - n + - '' - : q - ? '' - : "" + - n + - '', - a = this._get(t, 'currentText'), - r = this._get(t, 'gotoCurrent') && t.currentDay ? G : B, - a = K ? this.formatDate(a, r, this._getFormatConfig(t)) : a, - h = t.inline - ? '' - : "', - l = j - ? "
      " + - (Y ? h : '') + - (this._isInRange(t, r) - ? "' - : '') + - (Y ? '' : h) + - '
      ' - : '', - c = parseInt(this._get(t, 'firstDay'), 10), - c = isNaN(c) ? 0 : c, - u = this._get(t, 'showWeek'), - d = this._get(t, 'dayNames'), - p = this._get(t, 'dayNamesMin'), - f = this._get(t, 'monthNames'), - g = this._get(t, 'monthNamesShort'), - m = this._get(t, 'beforeShowDay'), - _ = this._get(t, 'showOtherMonths'), - v = this._get(t, 'selectOtherMonths'), - b = this._getDefaultDate(t), - y = '', - k = 0; - U[0] > k; - k++ - ) { - for (x = '', this.maxRows = 4, C = 0; U[1] > C; C++) { - if ( - ((D = this._daylightSavingAdjust(new Date(te, Z, t.selectedDay))), - (I = ' ui-corner-all'), - (T = ''), - X) - ) { - if (((T += "
      " + - (/all|left/.test(I) && 0 === k ? (Y ? o : s) : '') + - (/all|right/.test(I) && 0 === k ? (Y ? s : o) : '') + - this._generateMonthYearHeader( - t, - Z, - te, - Q, - J, - k > 0 || C > 0, - f, - g - ) + - "
      " + - '', - P = u - ? "' - : '', - w = 0; - 7 > w; - w++ - ) - (M = (w + c) % 7), - (P += - "'); - for ( - T += P + '', - S = this._getDaysInMonth(te, Z), - te === t.selectedYear && - Z === t.selectedMonth && - (t.selectedDay = Math.min(t.selectedDay, S)), - H = (this._getFirstDayOfMonth(te, Z) - c + 7) % 7, - z = Math.ceil((H + S) / 7), - O = X ? (this.maxRows > z ? this.maxRows : z) : z, - this.maxRows = O, - A = this._daylightSavingAdjust(new Date(te, Z, 1 - H)), - N = 0; - O > N; - N++ - ) { - for ( - T += '', - W = u - ? "' - : '', - w = 0; - 7 > w; - w++ - ) - (E = m ? m.apply(t.input ? t.input[0] : null, [A]) : [!0, '']), - (F = A.getMonth() !== Z), - (L = (F && !v) || !E[0] || (Q && Q > A) || (J && A > J)), - (W += - "'), - A.setDate(A.getDate() + 1), - (A = this._daylightSavingAdjust(A)); - T += W + ''; - } - Z++, - Z > 11 && ((Z = 0), te++), - (T += - '
      " + - this._get(t, 'weekHeader') + - '= 5 - ? " class='ui-datepicker-week-end'" - : '') + - '>' + - "" + - p[M] + - '
      " + - this._get(t, 'calculateWeek')(A) + - '' + - (F && !_ - ? ' ' - : L - ? "" + - A.getDate() + - '' - : "" + - A.getDate() + - '') + - '
      ' + - (X - ? '
      ' + - (U[0] > 0 && C === U[1] - 1 - ? "
      " - : '') - : '')), - (x += T); - } - y += x; - } - return (y += l), (t._keyEvent = !1), y; - }, - _generateMonthYearHeader: function(t, e, i, s, n, o, a, r) { - var h, - l, - c, - u, - d, - p, - f, - g, - m = this._get(t, 'changeMonth'), - _ = this._get(t, 'changeYear'), - v = this._get(t, 'showMonthAfterYear'), - b = "
      ", - y = ''; - if (o || !m) y += "" + a[e] + ''; - else { - for ( - h = s && s.getFullYear() === i, - l = n && n.getFullYear() === i, - y += - "'; - } - if ((v || (b += y + (!o && m && _ ? '' : ' ')), !t.yearshtml)) - if (((t.yearshtml = ''), o || !_)) - b += "" + i + ''; - else { - for ( - u = this._get(t, 'yearRange').split(':'), - d = new Date().getFullYear(), - p = function(t) { - var e = t.match(/c[+\-].*/) - ? i + parseInt(t.substring(1), 10) - : t.match(/[+\-].*/) - ? d + parseInt(t, 10) - : parseInt(t, 10); - return isNaN(e) ? d : e; - }, - f = p(u[0]), - g = Math.max(f, p(u[1] || '')), - f = s ? Math.max(f, s.getFullYear()) : f, - g = n ? Math.min(g, n.getFullYear()) : g, - t.yearshtml += - "'), - (b += t.yearshtml), - (t.yearshtml = null); - } - return ( - (b += this._get(t, 'yearSuffix')), - v && (b += (!o && m && _ ? '' : ' ') + y), - (b += '
      ') - ); - }, - _adjustInstDate: function(t, e, i) { - var s = t.selectedYear + ('Y' === i ? e : 0), - n = t.selectedMonth + ('M' === i ? e : 0), - o = - Math.min(t.selectedDay, this._getDaysInMonth(s, n)) + - ('D' === i ? e : 0), - a = this._restrictMinMax( - t, - this._daylightSavingAdjust(new Date(s, n, o)) - ); - (t.selectedDay = a.getDate()), - (t.drawMonth = t.selectedMonth = a.getMonth()), - (t.drawYear = t.selectedYear = a.getFullYear()), - ('M' === i || 'Y' === i) && this._notifyChange(t); - }, - _restrictMinMax: function(t, e) { - var i = this._getMinMaxDate(t, 'min'), - s = this._getMinMaxDate(t, 'max'), - n = i && i > e ? i : e; - return s && n > s ? s : n; - }, - _notifyChange: function(t) { - var e = this._get(t, 'onChangeMonthYear'); - e && - e.apply(t.input ? t.input[0] : null, [ - t.selectedYear, - t.selectedMonth + 1, - t - ]); - }, - _getNumberOfMonths: function(t) { - var e = this._get(t, 'numberOfMonths'); - return null == e ? [1, 1] : 'number' == typeof e ? [1, e] : e; - }, - _getMinMaxDate: function(t, e) { - return this._determineDate(t, this._get(t, e + 'Date'), null); - }, - _getDaysInMonth: function(t, e) { - return 32 - this._daylightSavingAdjust(new Date(t, e, 32)).getDate(); - }, - _getFirstDayOfMonth: function(t, e) { - return new Date(t, e, 1).getDay(); - }, - _canAdjustMonth: function(t, e, i, s) { - var n = this._getNumberOfMonths(t), - o = this._daylightSavingAdjust( - new Date(i, s + (0 > e ? e : n[0] * n[1]), 1) - ); - return ( - 0 > e && o.setDate(this._getDaysInMonth(o.getFullYear(), o.getMonth())), - this._isInRange(t, o) - ); - }, - _isInRange: function(t, e) { - var i, - s, - n = this._getMinMaxDate(t, 'min'), - o = this._getMinMaxDate(t, 'max'), - a = null, - r = null, - h = this._get(t, 'yearRange'); - return ( - h && - ((i = h.split(':')), - (s = new Date().getFullYear()), - (a = parseInt(i[0], 10)), - (r = parseInt(i[1], 10)), - i[0].match(/[+\-].*/) && (a += s), - i[1].match(/[+\-].*/) && (r += s)), - (!n || e.getTime() >= n.getTime()) && - (!o || e.getTime() <= o.getTime()) && - (!a || e.getFullYear() >= a) && - (!r || r >= e.getFullYear()) - ); - }, - _getFormatConfig: function(t) { - var e = this._get(t, 'shortYearCutoff'); - return ( - (e = - 'string' != typeof e - ? e - : (new Date().getFullYear() % 100) + parseInt(e, 10)), - { - shortYearCutoff: e, - dayNamesShort: this._get(t, 'dayNamesShort'), - dayNames: this._get(t, 'dayNames'), - monthNamesShort: this._get(t, 'monthNamesShort'), - monthNames: this._get(t, 'monthNames') - } - ); - }, - _formatDate: function(t, e, i, s) { - e || - ((t.currentDay = t.selectedDay), - (t.currentMonth = t.selectedMonth), - (t.currentYear = t.selectedYear)); - var n = e - ? 'object' == typeof e - ? e - : this._daylightSavingAdjust(new Date(s, i, e)) - : this._daylightSavingAdjust( - new Date(t.currentYear, t.currentMonth, t.currentDay) - ); - return this.formatDate( - this._get(t, 'dateFormat'), - n, - this._getFormatConfig(t) - ); - } - }), - (t.fn.datepicker = function(e) { - if (!this.length) return this; - t.datepicker.initialized || - (t(document).on('mousedown', t.datepicker._checkExternalClick), - (t.datepicker.initialized = !0)), - 0 === t('#' + t.datepicker._mainDivId).length && - t('body').append(t.datepicker.dpDiv); - var i = Array.prototype.slice.call(arguments, 1); - return 'string' != typeof e || - ('isDisabled' !== e && 'getDate' !== e && 'widget' !== e) - ? 'option' === e && - 2 === arguments.length && - 'string' == typeof arguments[1] - ? t.datepicker['_' + e + 'Datepicker'].apply( - t.datepicker, - [this[0]].concat(i) - ) - : this.each(function() { - 'string' == typeof e - ? t.datepicker['_' + e + 'Datepicker'].apply( - t.datepicker, - [this].concat(i) - ) - : t.datepicker._attachDatepicker(this, e); - }) - : t.datepicker['_' + e + 'Datepicker'].apply( - t.datepicker, - [this[0]].concat(i) - ); - }), - (t.datepicker = new s()), - (t.datepicker.initialized = !1), - (t.datepicker.uuid = new Date().getTime()), - (t.datepicker.version = '1.12.1'), - t.datepicker, - (t.ui.ie = !!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase())); - var _ = !1; - t(document).on('mouseup', function() { - _ = !1; - }), - t.widget('ui.mouse', { - version: '1.12.1', - options: { - cancel: 'input, textarea, button, select, option', - distance: 1, - delay: 0 - }, - _mouseInit: function() { - var e = this; - this.element - .on('mousedown.' + this.widgetName, function(t) { - return e._mouseDown(t); - }) - .on('click.' + this.widgetName, function(i) { - return !0 === t.data(i.target, e.widgetName + '.preventClickEvent') - ? (t.removeData(i.target, e.widgetName + '.preventClickEvent'), - i.stopImmediatePropagation(), - !1) - : void 0; - }), - (this.started = !1); - }, - _mouseDestroy: function() { - this.element.off('.' + this.widgetName), - this._mouseMoveDelegate && - this.document - .off('mousemove.' + this.widgetName, this._mouseMoveDelegate) - .off('mouseup.' + this.widgetName, this._mouseUpDelegate); - }, - _mouseDown: function(e) { - if (!_) { - (this._mouseMoved = !1), - this._mouseStarted && this._mouseUp(e), - (this._mouseDownEvent = e); - var i = this, - s = 1 === e.which, - n = - 'string' == typeof this.options.cancel && e.target.nodeName - ? t(e.target).closest(this.options.cancel).length - : !1; - return s && !n && this._mouseCapture(e) - ? ((this.mouseDelayMet = !this.options.delay), - this.mouseDelayMet || - (this._mouseDelayTimer = setTimeout(function() { - i.mouseDelayMet = !0; - }, this.options.delay)), - this._mouseDistanceMet(e) && - this._mouseDelayMet(e) && - ((this._mouseStarted = this._mouseStart(e) !== !1), - !this._mouseStarted) - ? (e.preventDefault(), !0) - : (!0 === - t.data(e.target, this.widgetName + '.preventClickEvent') && - t.removeData( - e.target, - this.widgetName + '.preventClickEvent' - ), - (this._mouseMoveDelegate = function(t) { - return i._mouseMove(t); - }), - (this._mouseUpDelegate = function(t) { - return i._mouseUp(t); - }), - this.document - .on('mousemove.' + this.widgetName, this._mouseMoveDelegate) - .on('mouseup.' + this.widgetName, this._mouseUpDelegate), - e.preventDefault(), - (_ = !0), - !0)) - : !0; - } - }, - _mouseMove: function(e) { - if (this._mouseMoved) { - if ( - t.ui.ie && - (!document.documentMode || 9 > document.documentMode) && - !e.button - ) - return this._mouseUp(e); - if (!e.which) - if ( - e.originalEvent.altKey || - e.originalEvent.ctrlKey || - e.originalEvent.metaKey || - e.originalEvent.shiftKey - ) - this.ignoreMissingWhich = !0; - else if (!this.ignoreMissingWhich) return this._mouseUp(e); - } - return ( - (e.which || e.button) && (this._mouseMoved = !0), - this._mouseStarted - ? (this._mouseDrag(e), e.preventDefault()) - : (this._mouseDistanceMet(e) && - this._mouseDelayMet(e) && - ((this._mouseStarted = - this._mouseStart(this._mouseDownEvent, e) !== !1), - this._mouseStarted ? this._mouseDrag(e) : this._mouseUp(e)), - !this._mouseStarted) - ); - }, - _mouseUp: function(e) { - this.document - .off('mousemove.' + this.widgetName, this._mouseMoveDelegate) - .off('mouseup.' + this.widgetName, this._mouseUpDelegate), - this._mouseStarted && - ((this._mouseStarted = !1), - e.target === this._mouseDownEvent.target && - t.data(e.target, this.widgetName + '.preventClickEvent', !0), - this._mouseStop(e)), - this._mouseDelayTimer && - (clearTimeout(this._mouseDelayTimer), delete this._mouseDelayTimer), - (this.ignoreMissingWhich = !1), - (_ = !1), - e.preventDefault(); - }, - _mouseDistanceMet: function(t) { - return ( - Math.max( - Math.abs(this._mouseDownEvent.pageX - t.pageX), - Math.abs(this._mouseDownEvent.pageY - t.pageY) - ) >= this.options.distance - ); - }, - _mouseDelayMet: function() { - return this.mouseDelayMet; - }, - _mouseStart: function() {}, - _mouseDrag: function() {}, - _mouseStop: function() {}, - _mouseCapture: function() { - return !0; - } - }), - (t.ui.plugin = { - add: function(e, i, s) { - var n, - o = t.ui[e].prototype; - for (n in s) - (o.plugins[n] = o.plugins[n] || []), o.plugins[n].push([i, s[n]]); - }, - call: function(t, e, i, s) { - var n, - o = t.plugins[e]; - if ( - o && - (s || - (t.element[0].parentNode && - 11 !== t.element[0].parentNode.nodeType)) - ) - for (n = 0; o.length > n; n++) - t.options[o[n][0]] && o[n][1].apply(t.element, i); - } - }), - (t.ui.safeBlur = function(e) { - e && 'body' !== e.nodeName.toLowerCase() && t(e).trigger('blur'); - }), - t.widget('ui.draggable', t.ui.mouse, { - version: '1.12.1', - widgetEventPrefix: 'drag', - options: { - addClasses: !0, - appendTo: 'parent', - axis: !1, - connectToSortable: !1, - containment: !1, - cursor: 'auto', - cursorAt: !1, - grid: !1, - handle: !1, - helper: 'original', - iframeFix: !1, - opacity: !1, - refreshPositions: !1, - revert: !1, - revertDuration: 500, - scope: 'default', - scroll: !0, - scrollSensitivity: 20, - scrollSpeed: 20, - snap: !1, - snapMode: 'both', - snapTolerance: 20, - stack: !1, - zIndex: !1, - drag: null, - start: null, - stop: null - }, - _create: function() { - 'original' === this.options.helper && this._setPositionRelative(), - this.options.addClasses && this._addClass('ui-draggable'), - this._setHandleClassName(), - this._mouseInit(); - }, - _setOption: function(t, e) { - this._super(t, e), - 'handle' === t && - (this._removeHandleClassName(), this._setHandleClassName()); - }, - _destroy: function() { - return (this.helper || this.element).is('.ui-draggable-dragging') - ? ((this.destroyOnClear = !0), void 0) - : (this._removeHandleClassName(), this._mouseDestroy(), void 0); - }, - _mouseCapture: function(e) { - var i = this.options; - return this.helper || - i.disabled || - t(e.target).closest('.ui-resizable-handle').length > 0 - ? !1 - : ((this.handle = this._getHandle(e)), - this.handle - ? (this._blurActiveElement(e), - this._blockFrames(i.iframeFix === !0 ? 'iframe' : i.iframeFix), - !0) - : !1); - }, - _blockFrames: function(e) { - this.iframeBlocks = this.document.find(e).map(function() { - var e = t(this); - return t('
      ') - .css('position', 'absolute') - .appendTo(e.parent()) - .outerWidth(e.outerWidth()) - .outerHeight(e.outerHeight()) - .offset(e.offset())[0]; - }); - }, - _unblockFrames: function() { - this.iframeBlocks && - (this.iframeBlocks.remove(), delete this.iframeBlocks); - }, - _blurActiveElement: function(e) { - var i = t.ui.safeActiveElement(this.document[0]), - s = t(e.target); - s.closest(i).length || t.ui.safeBlur(i); - }, - _mouseStart: function(e) { - var i = this.options; - return ( - (this.helper = this._createHelper(e)), - this._addClass(this.helper, 'ui-draggable-dragging'), - this._cacheHelperProportions(), - t.ui.ddmanager && (t.ui.ddmanager.current = this), - this._cacheMargins(), - (this.cssPosition = this.helper.css('position')), - (this.scrollParent = this.helper.scrollParent(!0)), - (this.offsetParent = this.helper.offsetParent()), - (this.hasFixedAncestor = - this.helper.parents().filter(function() { - return 'fixed' === t(this).css('position'); - }).length > 0), - (this.positionAbs = this.element.offset()), - this._refreshOffsets(e), - (this.originalPosition = this.position = this._generatePosition( - e, - !1 - )), - (this.originalPageX = e.pageX), - (this.originalPageY = e.pageY), - i.cursorAt && this._adjustOffsetFromHelper(i.cursorAt), - this._setContainment(), - this._trigger('start', e) === !1 - ? (this._clear(), !1) - : (this._cacheHelperProportions(), - t.ui.ddmanager && - !i.dropBehaviour && - t.ui.ddmanager.prepareOffsets(this, e), - this._mouseDrag(e, !0), - t.ui.ddmanager && t.ui.ddmanager.dragStart(this, e), - !0) - ); - }, - _refreshOffsets: function(t) { - (this.offset = { - top: this.positionAbs.top - this.margins.top, - left: this.positionAbs.left - this.margins.left, - scroll: !1, - parent: this._getParentOffset(), - relative: this._getRelativeOffset() - }), - (this.offset.click = { - left: t.pageX - this.offset.left, - top: t.pageY - this.offset.top - }); - }, - _mouseDrag: function(e, i) { - if ( - (this.hasFixedAncestor && - (this.offset.parent = this._getParentOffset()), - (this.position = this._generatePosition(e, !0)), - (this.positionAbs = this._convertPositionTo('absolute')), - !i) - ) { - var s = this._uiHash(); - if (this._trigger('drag', e, s) === !1) - return this._mouseUp(new t.Event('mouseup', e)), !1; - this.position = s.position; - } - return ( - (this.helper[0].style.left = this.position.left + 'px'), - (this.helper[0].style.top = this.position.top + 'px'), - t.ui.ddmanager && t.ui.ddmanager.drag(this, e), - !1 - ); - }, - _mouseStop: function(e) { - var i = this, - s = !1; - return ( - t.ui.ddmanager && - !this.options.dropBehaviour && - (s = t.ui.ddmanager.drop(this, e)), - this.dropped && ((s = this.dropped), (this.dropped = !1)), - ('invalid' === this.options.revert && !s) || - ('valid' === this.options.revert && s) || - this.options.revert === !0 || - (t.isFunction(this.options.revert) && - this.options.revert.call(this.element, s)) - ? t(this.helper).animate( - this.originalPosition, - parseInt(this.options.revertDuration, 10), - function() { - i._trigger('stop', e) !== !1 && i._clear(); - } - ) - : this._trigger('stop', e) !== !1 && this._clear(), - !1 - ); - }, - _mouseUp: function(e) { - return ( - this._unblockFrames(), - t.ui.ddmanager && t.ui.ddmanager.dragStop(this, e), - this.handleElement.is(e.target) && this.element.trigger('focus'), - t.ui.mouse.prototype._mouseUp.call(this, e) - ); - }, - cancel: function() { - return ( - this.helper.is('.ui-draggable-dragging') - ? this._mouseUp(new t.Event('mouseup', { target: this.element[0] })) - : this._clear(), - this - ); - }, - _getHandle: function(e) { - return this.options.handle - ? !!t(e.target).closest(this.element.find(this.options.handle)).length - : !0; - }, - _setHandleClassName: function() { - (this.handleElement = this.options.handle - ? this.element.find(this.options.handle) - : this.element), - this._addClass(this.handleElement, 'ui-draggable-handle'); - }, - _removeHandleClassName: function() { - this._removeClass(this.handleElement, 'ui-draggable-handle'); - }, - _createHelper: function(e) { - var i = this.options, - s = t.isFunction(i.helper), - n = s - ? t(i.helper.apply(this.element[0], [e])) - : 'clone' === i.helper - ? this.element.clone().removeAttr('id') - : this.element; - return ( - n.parents('body').length || - n.appendTo( - 'parent' === i.appendTo ? this.element[0].parentNode : i.appendTo - ), - s && n[0] === this.element[0] && this._setPositionRelative(), - n[0] === this.element[0] || - /(fixed|absolute)/.test(n.css('position')) || - n.css('position', 'absolute'), - n - ); - }, - _setPositionRelative: function() { - /^(?:r|a|f)/.test(this.element.css('position')) || - (this.element[0].style.position = 'relative'); - }, - _adjustOffsetFromHelper: function(e) { - 'string' == typeof e && (e = e.split(' ')), - t.isArray(e) && (e = { left: +e[0], top: +e[1] || 0 }), - 'left' in e && (this.offset.click.left = e.left + this.margins.left), - 'right' in e && - (this.offset.click.left = - this.helperProportions.width - e.right + this.margins.left), - 'top' in e && (this.offset.click.top = e.top + this.margins.top), - 'bottom' in e && - (this.offset.click.top = - this.helperProportions.height - e.bottom + this.margins.top); - }, - _isRootNode: function(t) { - return /(html|body)/i.test(t.tagName) || t === this.document[0]; - }, - _getParentOffset: function() { - var e = this.offsetParent.offset(), - i = this.document[0]; - return ( - 'absolute' === this.cssPosition && - this.scrollParent[0] !== i && - t.contains(this.scrollParent[0], this.offsetParent[0]) && - ((e.left += this.scrollParent.scrollLeft()), - (e.top += this.scrollParent.scrollTop())), - this._isRootNode(this.offsetParent[0]) && (e = { top: 0, left: 0 }), - { - top: - e.top + - (parseInt(this.offsetParent.css('borderTopWidth'), 10) || 0), - left: - e.left + - (parseInt(this.offsetParent.css('borderLeftWidth'), 10) || 0) - } - ); - }, - _getRelativeOffset: function() { - if ('relative' !== this.cssPosition) return { top: 0, left: 0 }; - var t = this.element.position(), - e = this._isRootNode(this.scrollParent[0]); - return { - top: - t.top - - (parseInt(this.helper.css('top'), 10) || 0) + - (e ? 0 : this.scrollParent.scrollTop()), - left: - t.left - - (parseInt(this.helper.css('left'), 10) || 0) + - (e ? 0 : this.scrollParent.scrollLeft()) - }; - }, - _cacheMargins: function() { - this.margins = { - left: parseInt(this.element.css('marginLeft'), 10) || 0, - top: parseInt(this.element.css('marginTop'), 10) || 0, - right: parseInt(this.element.css('marginRight'), 10) || 0, - bottom: parseInt(this.element.css('marginBottom'), 10) || 0 - }; - }, - _cacheHelperProportions: function() { - this.helperProportions = { - width: this.helper.outerWidth(), - height: this.helper.outerHeight() - }; - }, - _setContainment: function() { - var e, - i, - s, - n = this.options, - o = this.document[0]; - return ( - (this.relativeContainer = null), - n.containment - ? 'window' === n.containment - ? ((this.containment = [ - t(window).scrollLeft() - - this.offset.relative.left - - this.offset.parent.left, - t(window).scrollTop() - - this.offset.relative.top - - this.offset.parent.top, - t(window).scrollLeft() + - t(window).width() - - this.helperProportions.width - - this.margins.left, - t(window).scrollTop() + - (t(window).height() || o.body.parentNode.scrollHeight) - - this.helperProportions.height - - this.margins.top - ]), - void 0) - : 'document' === n.containment - ? ((this.containment = [ - 0, - 0, - t(o).width() - - this.helperProportions.width - - this.margins.left, - (t(o).height() || o.body.parentNode.scrollHeight) - - this.helperProportions.height - - this.margins.top - ]), - void 0) - : n.containment.constructor === Array - ? ((this.containment = n.containment), void 0) - : ('parent' === n.containment && - (n.containment = this.helper[0].parentNode), - (i = t(n.containment)), - (s = i[0]), - s && - ((e = /(scroll|auto)/.test(i.css('overflow'))), - (this.containment = [ - (parseInt(i.css('borderLeftWidth'), 10) || 0) + - (parseInt(i.css('paddingLeft'), 10) || 0), - (parseInt(i.css('borderTopWidth'), 10) || 0) + - (parseInt(i.css('paddingTop'), 10) || 0), - (e - ? Math.max(s.scrollWidth, s.offsetWidth) - : s.offsetWidth) - - (parseInt(i.css('borderRightWidth'), 10) || 0) - - (parseInt(i.css('paddingRight'), 10) || 0) - - this.helperProportions.width - - this.margins.left - - this.margins.right, - (e - ? Math.max(s.scrollHeight, s.offsetHeight) - : s.offsetHeight) - - (parseInt(i.css('borderBottomWidth'), 10) || 0) - - (parseInt(i.css('paddingBottom'), 10) || 0) - - this.helperProportions.height - - this.margins.top - - this.margins.bottom - ]), - (this.relativeContainer = i)), - void 0) - : ((this.containment = null), void 0) - ); - }, - _convertPositionTo: function(t, e) { - e || (e = this.position); - var i = 'absolute' === t ? 1 : -1, - s = this._isRootNode(this.scrollParent[0]); - return { - top: - e.top + - this.offset.relative.top * i + - this.offset.parent.top * i - - ('fixed' === this.cssPosition - ? -this.offset.scroll.top - : s - ? 0 - : this.offset.scroll.top) * - i, - left: - e.left + - this.offset.relative.left * i + - this.offset.parent.left * i - - ('fixed' === this.cssPosition - ? -this.offset.scroll.left - : s - ? 0 - : this.offset.scroll.left) * - i - }; - }, - _generatePosition: function(t, e) { - var i, - s, - n, - o, - a = this.options, - r = this._isRootNode(this.scrollParent[0]), - h = t.pageX, - l = t.pageY; - return ( - (r && this.offset.scroll) || - (this.offset.scroll = { - top: this.scrollParent.scrollTop(), - left: this.scrollParent.scrollLeft() - }), - e && - (this.containment && - (this.relativeContainer - ? ((s = this.relativeContainer.offset()), - (i = [ - this.containment[0] + s.left, - this.containment[1] + s.top, - this.containment[2] + s.left, - this.containment[3] + s.top - ])) - : (i = this.containment), - t.pageX - this.offset.click.left < i[0] && - (h = i[0] + this.offset.click.left), - t.pageY - this.offset.click.top < i[1] && - (l = i[1] + this.offset.click.top), - t.pageX - this.offset.click.left > i[2] && - (h = i[2] + this.offset.click.left), - t.pageY - this.offset.click.top > i[3] && - (l = i[3] + this.offset.click.top)), - a.grid && - ((n = a.grid[1] - ? this.originalPageY + - Math.round((l - this.originalPageY) / a.grid[1]) * a.grid[1] - : this.originalPageY), - (l = i - ? n - this.offset.click.top >= i[1] || - n - this.offset.click.top > i[3] - ? n - : n - this.offset.click.top >= i[1] - ? n - a.grid[1] - : n + a.grid[1] - : n), - (o = a.grid[0] - ? this.originalPageX + - Math.round((h - this.originalPageX) / a.grid[0]) * a.grid[0] - : this.originalPageX), - (h = i - ? o - this.offset.click.left >= i[0] || - o - this.offset.click.left > i[2] - ? o - : o - this.offset.click.left >= i[0] - ? o - a.grid[0] - : o + a.grid[0] - : o)), - 'y' === a.axis && (h = this.originalPageX), - 'x' === a.axis && (l = this.originalPageY)), - { - top: - l - - this.offset.click.top - - this.offset.relative.top - - this.offset.parent.top + - ('fixed' === this.cssPosition - ? -this.offset.scroll.top - : r - ? 0 - : this.offset.scroll.top), - left: - h - - this.offset.click.left - - this.offset.relative.left - - this.offset.parent.left + - ('fixed' === this.cssPosition - ? -this.offset.scroll.left - : r - ? 0 - : this.offset.scroll.left) - } - ); - }, - _clear: function() { - this._removeClass(this.helper, 'ui-draggable-dragging'), - this.helper[0] === this.element[0] || - this.cancelHelperRemoval || - this.helper.remove(), - (this.helper = null), - (this.cancelHelperRemoval = !1), - this.destroyOnClear && this.destroy(); - }, - _trigger: function(e, i, s) { - return ( - (s = s || this._uiHash()), - t.ui.plugin.call(this, e, [i, s, this], !0), - /^(drag|start|stop)/.test(e) && - ((this.positionAbs = this._convertPositionTo('absolute')), - (s.offset = this.positionAbs)), - t.Widget.prototype._trigger.call(this, e, i, s) - ); - }, - plugins: {}, - _uiHash: function() { - return { - helper: this.helper, - position: this.position, - originalPosition: this.originalPosition, - offset: this.positionAbs - }; - } - }), - t.ui.plugin.add('draggable', 'connectToSortable', { - start: function(e, i, s) { - var n = t.extend({}, i, { item: s.element }); - (s.sortables = []), - t(s.options.connectToSortable).each(function() { - var i = t(this).sortable('instance'); - i && - !i.options.disabled && - (s.sortables.push(i), - i.refreshPositions(), - i._trigger('activate', e, n)); - }); - }, - stop: function(e, i, s) { - var n = t.extend({}, i, { item: s.element }); - (s.cancelHelperRemoval = !1), - t.each(s.sortables, function() { - var t = this; - t.isOver - ? ((t.isOver = 0), - (s.cancelHelperRemoval = !0), - (t.cancelHelperRemoval = !1), - (t._storedCSS = { - position: t.placeholder.css('position'), - top: t.placeholder.css('top'), - left: t.placeholder.css('left') - }), - t._mouseStop(e), - (t.options.helper = t.options._helper)) - : ((t.cancelHelperRemoval = !0), t._trigger('deactivate', e, n)); - }); - }, - drag: function(e, i, s) { - t.each(s.sortables, function() { - var n = !1, - o = this; - (o.positionAbs = s.positionAbs), - (o.helperProportions = s.helperProportions), - (o.offset.click = s.offset.click), - o._intersectsWith(o.containerCache) && - ((n = !0), - t.each(s.sortables, function() { - return ( - (this.positionAbs = s.positionAbs), - (this.helperProportions = s.helperProportions), - (this.offset.click = s.offset.click), - this !== o && - this._intersectsWith(this.containerCache) && - t.contains(o.element[0], this.element[0]) && - (n = !1), - n - ); - })), - n - ? (o.isOver || - ((o.isOver = 1), - (s._parent = i.helper.parent()), - (o.currentItem = i.helper - .appendTo(o.element) - .data('ui-sortable-item', !0)), - (o.options._helper = o.options.helper), - (o.options.helper = function() { - return i.helper[0]; - }), - (e.target = o.currentItem[0]), - o._mouseCapture(e, !0), - o._mouseStart(e, !0, !0), - (o.offset.click.top = s.offset.click.top), - (o.offset.click.left = s.offset.click.left), - (o.offset.parent.left -= - s.offset.parent.left - o.offset.parent.left), - (o.offset.parent.top -= - s.offset.parent.top - o.offset.parent.top), - s._trigger('toSortable', e), - (s.dropped = o.element), - t.each(s.sortables, function() { - this.refreshPositions(); - }), - (s.currentItem = s.element), - (o.fromOutside = s)), - o.currentItem && (o._mouseDrag(e), (i.position = o.position))) - : o.isOver && - ((o.isOver = 0), - (o.cancelHelperRemoval = !0), - (o.options._revert = o.options.revert), - (o.options.revert = !1), - o._trigger('out', e, o._uiHash(o)), - o._mouseStop(e, !0), - (o.options.revert = o.options._revert), - (o.options.helper = o.options._helper), - o.placeholder && o.placeholder.remove(), - i.helper.appendTo(s._parent), - s._refreshOffsets(e), - (i.position = s._generatePosition(e, !0)), - s._trigger('fromSortable', e), - (s.dropped = !1), - t.each(s.sortables, function() { - this.refreshPositions(); - })); - }); - } - }), - t.ui.plugin.add('draggable', 'cursor', { - start: function(e, i, s) { - var n = t('body'), - o = s.options; - n.css('cursor') && (o._cursor = n.css('cursor')), - n.css('cursor', o.cursor); - }, - stop: function(e, i, s) { - var n = s.options; - n._cursor && t('body').css('cursor', n._cursor); - } - }), - t.ui.plugin.add('draggable', 'opacity', { - start: function(e, i, s) { - var n = t(i.helper), - o = s.options; - n.css('opacity') && (o._opacity = n.css('opacity')), - n.css('opacity', o.opacity); - }, - stop: function(e, i, s) { - var n = s.options; - n._opacity && t(i.helper).css('opacity', n._opacity); - } - }), - t.ui.plugin.add('draggable', 'scroll', { - start: function(t, e, i) { - i.scrollParentNotHidden || - (i.scrollParentNotHidden = i.helper.scrollParent(!1)), - i.scrollParentNotHidden[0] !== i.document[0] && - 'HTML' !== i.scrollParentNotHidden[0].tagName && - (i.overflowOffset = i.scrollParentNotHidden.offset()); - }, - drag: function(e, i, s) { - var n = s.options, - o = !1, - a = s.scrollParentNotHidden[0], - r = s.document[0]; - a !== r && 'HTML' !== a.tagName - ? ((n.axis && 'x' === n.axis) || - (s.overflowOffset.top + a.offsetHeight - e.pageY < - n.scrollSensitivity - ? (a.scrollTop = o = a.scrollTop + n.scrollSpeed) - : e.pageY - s.overflowOffset.top < n.scrollSensitivity && - (a.scrollTop = o = a.scrollTop - n.scrollSpeed)), - (n.axis && 'y' === n.axis) || - (s.overflowOffset.left + a.offsetWidth - e.pageX < - n.scrollSensitivity - ? (a.scrollLeft = o = a.scrollLeft + n.scrollSpeed) - : e.pageX - s.overflowOffset.left < n.scrollSensitivity && - (a.scrollLeft = o = a.scrollLeft - n.scrollSpeed))) - : ((n.axis && 'x' === n.axis) || - (e.pageY - t(r).scrollTop() < n.scrollSensitivity - ? (o = t(r).scrollTop(t(r).scrollTop() - n.scrollSpeed)) - : t(window).height() - (e.pageY - t(r).scrollTop()) < - n.scrollSensitivity && - (o = t(r).scrollTop(t(r).scrollTop() + n.scrollSpeed))), - (n.axis && 'y' === n.axis) || - (e.pageX - t(r).scrollLeft() < n.scrollSensitivity - ? (o = t(r).scrollLeft(t(r).scrollLeft() - n.scrollSpeed)) - : t(window).width() - (e.pageX - t(r).scrollLeft()) < - n.scrollSensitivity && - (o = t(r).scrollLeft(t(r).scrollLeft() + n.scrollSpeed)))), - o !== !1 && - t.ui.ddmanager && - !n.dropBehaviour && - t.ui.ddmanager.prepareOffsets(s, e); - } - }), - t.ui.plugin.add('draggable', 'snap', { - start: function(e, i, s) { - var n = s.options; - (s.snapElements = []), - t( - n.snap.constructor !== String - ? n.snap.items || ':data(ui-draggable)' - : n.snap - ).each(function() { - var e = t(this), - i = e.offset(); - this !== s.element[0] && - s.snapElements.push({ - item: this, - width: e.outerWidth(), - height: e.outerHeight(), - top: i.top, - left: i.left - }); - }); - }, - drag: function(e, i, s) { - var n, - o, - a, - r, - h, - l, - c, - u, - d, - p, - f = s.options, - g = f.snapTolerance, - m = i.offset.left, - _ = m + s.helperProportions.width, - v = i.offset.top, - b = v + s.helperProportions.height; - for (d = s.snapElements.length - 1; d >= 0; d--) - (h = s.snapElements[d].left - s.margins.left), - (l = h + s.snapElements[d].width), - (c = s.snapElements[d].top - s.margins.top), - (u = c + s.snapElements[d].height), - h - g > _ || - m > l + g || - c - g > b || - v > u + g || - !t.contains( - s.snapElements[d].item.ownerDocument, - s.snapElements[d].item - ) - ? (s.snapElements[d].snapping && - s.options.snap.release && - s.options.snap.release.call( - s.element, - e, - t.extend(s._uiHash(), { snapItem: s.snapElements[d].item }) - ), - (s.snapElements[d].snapping = !1)) - : ('inner' !== f.snapMode && - ((n = g >= Math.abs(c - b)), - (o = g >= Math.abs(u - v)), - (a = g >= Math.abs(h - _)), - (r = g >= Math.abs(l - m)), - n && - (i.position.top = s._convertPositionTo('relative', { - top: c - s.helperProportions.height, - left: 0 - }).top), - o && - (i.position.top = s._convertPositionTo('relative', { - top: u, - left: 0 - }).top), - a && - (i.position.left = s._convertPositionTo('relative', { - top: 0, - left: h - s.helperProportions.width - }).left), - r && - (i.position.left = s._convertPositionTo('relative', { - top: 0, - left: l - }).left)), - (p = n || o || a || r), - 'outer' !== f.snapMode && - ((n = g >= Math.abs(c - v)), - (o = g >= Math.abs(u - b)), - (a = g >= Math.abs(h - m)), - (r = g >= Math.abs(l - _)), - n && - (i.position.top = s._convertPositionTo('relative', { - top: c, - left: 0 - }).top), - o && - (i.position.top = s._convertPositionTo('relative', { - top: u - s.helperProportions.height, - left: 0 - }).top), - a && - (i.position.left = s._convertPositionTo('relative', { - top: 0, - left: h - }).left), - r && - (i.position.left = s._convertPositionTo('relative', { - top: 0, - left: l - s.helperProportions.width - }).left)), - !s.snapElements[d].snapping && - (n || o || a || r || p) && - s.options.snap.snap && - s.options.snap.snap.call( - s.element, - e, - t.extend(s._uiHash(), { snapItem: s.snapElements[d].item }) - ), - (s.snapElements[d].snapping = n || o || a || r || p)); - } - }), - t.ui.plugin.add('draggable', 'stack', { - start: function(e, i, s) { - var n, - o = s.options, - a = t.makeArray(t(o.stack)).sort(function(e, i) { - return ( - (parseInt(t(e).css('zIndex'), 10) || 0) - - (parseInt(t(i).css('zIndex'), 10) || 0) - ); - }); - a.length && - ((n = parseInt(t(a[0]).css('zIndex'), 10) || 0), - t(a).each(function(e) { - t(this).css('zIndex', n + e); - }), - this.css('zIndex', n + a.length)); - } - }), - t.ui.plugin.add('draggable', 'zIndex', { - start: function(e, i, s) { - var n = t(i.helper), - o = s.options; - n.css('zIndex') && (o._zIndex = n.css('zIndex')), - n.css('zIndex', o.zIndex); - }, - stop: function(e, i, s) { - var n = s.options; - n._zIndex && t(i.helper).css('zIndex', n._zIndex); - } - }), - t.ui.draggable, - t.widget('ui.resizable', t.ui.mouse, { - version: '1.12.1', - widgetEventPrefix: 'resize', - options: { - alsoResize: !1, - animate: !1, - animateDuration: 'slow', - animateEasing: 'swing', - aspectRatio: !1, - autoHide: !1, - classes: { 'ui-resizable-se': 'ui-icon ui-icon-gripsmall-diagonal-se' }, - containment: !1, - ghost: !1, - grid: !1, - handles: 'e,s,se', - helper: !1, - maxHeight: null, - maxWidth: null, - minHeight: 10, - minWidth: 10, - zIndex: 90, - resize: null, - start: null, - stop: null - }, - _num: function(t) { - return parseFloat(t) || 0; - }, - _isNumber: function(t) { - return !isNaN(parseFloat(t)); - }, - _hasScroll: function(e, i) { - if ('hidden' === t(e).css('overflow')) return !1; - var s = i && 'left' === i ? 'scrollLeft' : 'scrollTop', - n = !1; - return e[s] > 0 ? !0 : ((e[s] = 1), (n = e[s] > 0), (e[s] = 0), n); - }, - _create: function() { - var e, - i = this.options, - s = this; - this._addClass('ui-resizable'), - t.extend(this, { - _aspectRatio: !!i.aspectRatio, - aspectRatio: i.aspectRatio, - originalElement: this.element, - _proportionallyResizeElements: [], - _helper: - i.helper || i.ghost || i.animate - ? i.helper || 'ui-resizable-helper' - : null - }), - this.element[0].nodeName.match( - /^(canvas|textarea|input|select|button|img)$/i - ) && - (this.element.wrap( - t("
      ").css( - { - position: this.element.css('position'), - width: this.element.outerWidth(), - height: this.element.outerHeight(), - top: this.element.css('top'), - left: this.element.css('left') - } - ) - ), - (this.element = this.element - .parent() - .data('ui-resizable', this.element.resizable('instance'))), - (this.elementIsWrapper = !0), - (e = { - marginTop: this.originalElement.css('marginTop'), - marginRight: this.originalElement.css('marginRight'), - marginBottom: this.originalElement.css('marginBottom'), - marginLeft: this.originalElement.css('marginLeft') - }), - this.element.css(e), - this.originalElement.css('margin', 0), - (this.originalResizeStyle = this.originalElement.css('resize')), - this.originalElement.css('resize', 'none'), - this._proportionallyResizeElements.push( - this.originalElement.css({ - position: 'static', - zoom: 1, - display: 'block' - }) - ), - this.originalElement.css(e), - this._proportionallyResize()), - this._setupHandles(), - i.autoHide && - t(this.element) - .on('mouseenter', function() { - i.disabled || - (s._removeClass('ui-resizable-autohide'), s._handles.show()); - }) - .on('mouseleave', function() { - i.disabled || - s.resizing || - (s._addClass('ui-resizable-autohide'), s._handles.hide()); - }), - this._mouseInit(); - }, - _destroy: function() { - this._mouseDestroy(); - var e, - i = function(e) { - t(e) - .removeData('resizable') - .removeData('ui-resizable') - .off('.resizable') - .find('.ui-resizable-handle') - .remove(); - }; - return ( - this.elementIsWrapper && - (i(this.element), - (e = this.element), - this.originalElement - .css({ - position: e.css('position'), - width: e.outerWidth(), - height: e.outerHeight(), - top: e.css('top'), - left: e.css('left') - }) - .insertAfter(e), - e.remove()), - this.originalElement.css('resize', this.originalResizeStyle), - i(this.originalElement), - this - ); - }, - _setOption: function(t, e) { - switch ((this._super(t, e), t)) { - case 'handles': - this._removeHandles(), this._setupHandles(); - break; - default: - } - }, - _setupHandles: function() { - var e, - i, - s, - n, - o, - a = this.options, - r = this; - if ( - ((this.handles = - a.handles || - (t('.ui-resizable-handle', this.element).length - ? { - n: '.ui-resizable-n', - e: '.ui-resizable-e', - s: '.ui-resizable-s', - w: '.ui-resizable-w', - se: '.ui-resizable-se', - sw: '.ui-resizable-sw', - ne: '.ui-resizable-ne', - nw: '.ui-resizable-nw' - } - : 'e,s,se')), - (this._handles = t()), - this.handles.constructor === String) - ) - for ( - 'all' === this.handles && (this.handles = 'n,e,s,w,se,sw,ne,nw'), - s = this.handles.split(','), - this.handles = {}, - i = 0; - s.length > i; - i++ - ) - (e = t.trim(s[i])), - (n = 'ui-resizable-' + e), - (o = t('
      ')), - this._addClass(o, 'ui-resizable-handle ' + n), - o.css({ zIndex: a.zIndex }), - (this.handles[e] = '.ui-resizable-' + e), - this.element.append(o); - (this._renderAxis = function(e) { - var i, s, n, o; - e = e || this.element; - for (i in this.handles) - this.handles[i].constructor === String - ? (this.handles[i] = this.element - .children(this.handles[i]) - .first() - .show()) - : (this.handles[i].jquery || this.handles[i].nodeType) && - ((this.handles[i] = t(this.handles[i])), - this._on(this.handles[i], { mousedown: r._mouseDown })), - this.elementIsWrapper && - this.originalElement[0].nodeName.match( - /^(textarea|input|select|button)$/i - ) && - ((s = t(this.handles[i], this.element)), - (o = /sw|ne|nw|se|n|s/.test(i) - ? s.outerHeight() - : s.outerWidth()), - (n = [ - 'padding', - /ne|nw|n/.test(i) - ? 'Top' - : /se|sw|s/.test(i) - ? 'Bottom' - : /^e$/.test(i) - ? 'Right' - : 'Left' - ].join('')), - e.css(n, o), - this._proportionallyResize()), - (this._handles = this._handles.add(this.handles[i])); - }), - this._renderAxis(this.element), - (this._handles = this._handles.add( - this.element.find('.ui-resizable-handle') - )), - this._handles.disableSelection(), - this._handles.on('mouseover', function() { - r.resizing || - (this.className && - (o = this.className.match( - /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i - )), - (r.axis = o && o[1] ? o[1] : 'se')); - }), - a.autoHide && - (this._handles.hide(), this._addClass('ui-resizable-autohide')); - }, - _removeHandles: function() { - this._handles.remove(); - }, - _mouseCapture: function(e) { - var i, - s, - n = !1; - for (i in this.handles) - (s = t(this.handles[i])[0]), - (s === e.target || t.contains(s, e.target)) && (n = !0); - return !this.options.disabled && n; - }, - _mouseStart: function(e) { - var i, - s, - n, - o = this.options, - a = this.element; - return ( - (this.resizing = !0), - this._renderProxy(), - (i = this._num(this.helper.css('left'))), - (s = this._num(this.helper.css('top'))), - o.containment && - ((i += t(o.containment).scrollLeft() || 0), - (s += t(o.containment).scrollTop() || 0)), - (this.offset = this.helper.offset()), - (this.position = { left: i, top: s }), - (this.size = this._helper - ? { width: this.helper.width(), height: this.helper.height() } - : { width: a.width(), height: a.height() }), - (this.originalSize = this._helper - ? { width: a.outerWidth(), height: a.outerHeight() } - : { width: a.width(), height: a.height() }), - (this.sizeDiff = { - width: a.outerWidth() - a.width(), - height: a.outerHeight() - a.height() - }), - (this.originalPosition = { left: i, top: s }), - (this.originalMousePosition = { left: e.pageX, top: e.pageY }), - (this.aspectRatio = - 'number' == typeof o.aspectRatio - ? o.aspectRatio - : this.originalSize.width / this.originalSize.height || 1), - (n = t('.ui-resizable-' + this.axis).css('cursor')), - t('body').css('cursor', 'auto' === n ? this.axis + '-resize' : n), - this._addClass('ui-resizable-resizing'), - this._propagate('start', e), - !0 - ); - }, - _mouseDrag: function(e) { - var i, - s, - n = this.originalMousePosition, - o = this.axis, - a = e.pageX - n.left || 0, - r = e.pageY - n.top || 0, - h = this._change[o]; - return ( - this._updatePrevProperties(), - h - ? ((i = h.apply(this, [e, a, r])), - this._updateVirtualBoundaries(e.shiftKey), - (this._aspectRatio || e.shiftKey) && - (i = this._updateRatio(i, e)), - (i = this._respectSize(i, e)), - this._updateCache(i), - this._propagate('resize', e), - (s = this._applyChanges()), - !this._helper && - this._proportionallyResizeElements.length && - this._proportionallyResize(), - t.isEmptyObject(s) || - (this._updatePrevProperties(), - this._trigger('resize', e, this.ui()), - this._applyChanges()), - !1) - : !1 - ); - }, - _mouseStop: function(e) { - this.resizing = !1; - var i, - s, - n, - o, - a, - r, - h, - l = this.options, - c = this; - return ( - this._helper && - ((i = this._proportionallyResizeElements), - (s = i.length && /textarea/i.test(i[0].nodeName)), - (n = s && this._hasScroll(i[0], 'left') ? 0 : c.sizeDiff.height), - (o = s ? 0 : c.sizeDiff.width), - (a = { - width: c.helper.width() - o, - height: c.helper.height() - n - }), - (r = - parseFloat(c.element.css('left')) + - (c.position.left - c.originalPosition.left) || null), - (h = - parseFloat(c.element.css('top')) + - (c.position.top - c.originalPosition.top) || null), - l.animate || this.element.css(t.extend(a, { top: h, left: r })), - c.helper.height(c.size.height), - c.helper.width(c.size.width), - this._helper && !l.animate && this._proportionallyResize()), - t('body').css('cursor', 'auto'), - this._removeClass('ui-resizable-resizing'), - this._propagate('stop', e), - this._helper && this.helper.remove(), - !1 - ); - }, - _updatePrevProperties: function() { - (this.prevPosition = { - top: this.position.top, - left: this.position.left - }), - (this.prevSize = { - width: this.size.width, - height: this.size.height - }); - }, - _applyChanges: function() { - var t = {}; - return ( - this.position.top !== this.prevPosition.top && - (t.top = this.position.top + 'px'), - this.position.left !== this.prevPosition.left && - (t.left = this.position.left + 'px'), - this.size.width !== this.prevSize.width && - (t.width = this.size.width + 'px'), - this.size.height !== this.prevSize.height && - (t.height = this.size.height + 'px'), - this.helper.css(t), - t - ); - }, - _updateVirtualBoundaries: function(t) { - var e, - i, - s, - n, - o, - a = this.options; - (o = { - minWidth: this._isNumber(a.minWidth) ? a.minWidth : 0, - maxWidth: this._isNumber(a.maxWidth) ? a.maxWidth : 1 / 0, - minHeight: this._isNumber(a.minHeight) ? a.minHeight : 0, - maxHeight: this._isNumber(a.maxHeight) ? a.maxHeight : 1 / 0 - }), - (this._aspectRatio || t) && - ((e = o.minHeight * this.aspectRatio), - (s = o.minWidth / this.aspectRatio), - (i = o.maxHeight * this.aspectRatio), - (n = o.maxWidth / this.aspectRatio), - e > o.minWidth && (o.minWidth = e), - s > o.minHeight && (o.minHeight = s), - o.maxWidth > i && (o.maxWidth = i), - o.maxHeight > n && (o.maxHeight = n)), - (this._vBoundaries = o); - }, - _updateCache: function(t) { - (this.offset = this.helper.offset()), - this._isNumber(t.left) && (this.position.left = t.left), - this._isNumber(t.top) && (this.position.top = t.top), - this._isNumber(t.height) && (this.size.height = t.height), - this._isNumber(t.width) && (this.size.width = t.width); - }, - _updateRatio: function(t) { - var e = this.position, - i = this.size, - s = this.axis; - return ( - this._isNumber(t.height) - ? (t.width = t.height * this.aspectRatio) - : this._isNumber(t.width) && - (t.height = t.width / this.aspectRatio), - 'sw' === s && - ((t.left = e.left + (i.width - t.width)), (t.top = null)), - 'nw' === s && - ((t.top = e.top + (i.height - t.height)), - (t.left = e.left + (i.width - t.width))), - t - ); - }, - _respectSize: function(t) { - var e = this._vBoundaries, - i = this.axis, - s = this._isNumber(t.width) && e.maxWidth && e.maxWidth < t.width, - n = this._isNumber(t.height) && e.maxHeight && e.maxHeight < t.height, - o = this._isNumber(t.width) && e.minWidth && e.minWidth > t.width, - a = this._isNumber(t.height) && e.minHeight && e.minHeight > t.height, - r = this.originalPosition.left + this.originalSize.width, - h = this.originalPosition.top + this.originalSize.height, - l = /sw|nw|w/.test(i), - c = /nw|ne|n/.test(i); - return ( - o && (t.width = e.minWidth), - a && (t.height = e.minHeight), - s && (t.width = e.maxWidth), - n && (t.height = e.maxHeight), - o && l && (t.left = r - e.minWidth), - s && l && (t.left = r - e.maxWidth), - a && c && (t.top = h - e.minHeight), - n && c && (t.top = h - e.maxHeight), - t.width || t.height || t.left || !t.top - ? t.width || t.height || t.top || !t.left || (t.left = null) - : (t.top = null), - t - ); - }, - _getPaddingPlusBorderDimensions: function(t) { - for ( - var e = 0, - i = [], - s = [ - t.css('borderTopWidth'), - t.css('borderRightWidth'), - t.css('borderBottomWidth'), - t.css('borderLeftWidth') - ], - n = [ - t.css('paddingTop'), - t.css('paddingRight'), - t.css('paddingBottom'), - t.css('paddingLeft') - ]; - 4 > e; - e++ - ) - (i[e] = parseFloat(s[e]) || 0), (i[e] += parseFloat(n[e]) || 0); - return { height: i[0] + i[2], width: i[1] + i[3] }; - }, - _proportionallyResize: function() { - if (this._proportionallyResizeElements.length) - for ( - var t, e = 0, i = this.helper || this.element; - this._proportionallyResizeElements.length > e; - e++ - ) - (t = this._proportionallyResizeElements[e]), - this.outerDimensions || - (this.outerDimensions = this._getPaddingPlusBorderDimensions( - t - )), - t.css({ - height: i.height() - this.outerDimensions.height || 0, - width: i.width() - this.outerDimensions.width || 0 - }); - }, - _renderProxy: function() { - var e = this.element, - i = this.options; - (this.elementOffset = e.offset()), - this._helper - ? ((this.helper = - this.helper || t("
      ")), - this._addClass(this.helper, this._helper), - this.helper.css({ - width: this.element.outerWidth(), - height: this.element.outerHeight(), - position: 'absolute', - left: this.elementOffset.left + 'px', - top: this.elementOffset.top + 'px', - zIndex: ++i.zIndex - }), - this.helper.appendTo('body').disableSelection()) - : (this.helper = this.element); - }, - _change: { - e: function(t, e) { - return { width: this.originalSize.width + e }; - }, - w: function(t, e) { - var i = this.originalSize, - s = this.originalPosition; - return { left: s.left + e, width: i.width - e }; - }, - n: function(t, e, i) { - var s = this.originalSize, - n = this.originalPosition; - return { top: n.top + i, height: s.height - i }; - }, - s: function(t, e, i) { - return { height: this.originalSize.height + i }; - }, - se: function(e, i, s) { - return t.extend( - this._change.s.apply(this, arguments), - this._change.e.apply(this, [e, i, s]) - ); - }, - sw: function(e, i, s) { - return t.extend( - this._change.s.apply(this, arguments), - this._change.w.apply(this, [e, i, s]) - ); - }, - ne: function(e, i, s) { - return t.extend( - this._change.n.apply(this, arguments), - this._change.e.apply(this, [e, i, s]) - ); - }, - nw: function(e, i, s) { - return t.extend( - this._change.n.apply(this, arguments), - this._change.w.apply(this, [e, i, s]) - ); - } - }, - _propagate: function(e, i) { - t.ui.plugin.call(this, e, [i, this.ui()]), - 'resize' !== e && this._trigger(e, i, this.ui()); - }, - plugins: {}, - ui: function() { - return { - originalElement: this.originalElement, - element: this.element, - helper: this.helper, - position: this.position, - size: this.size, - originalSize: this.originalSize, - originalPosition: this.originalPosition - }; - } - }), - t.ui.plugin.add('resizable', 'animate', { - stop: function(e) { - var i = t(this).resizable('instance'), - s = i.options, - n = i._proportionallyResizeElements, - o = n.length && /textarea/i.test(n[0].nodeName), - a = o && i._hasScroll(n[0], 'left') ? 0 : i.sizeDiff.height, - r = o ? 0 : i.sizeDiff.width, - h = { width: i.size.width - r, height: i.size.height - a }, - l = - parseFloat(i.element.css('left')) + - (i.position.left - i.originalPosition.left) || null, - c = - parseFloat(i.element.css('top')) + - (i.position.top - i.originalPosition.top) || null; - i.element.animate(t.extend(h, c && l ? { top: c, left: l } : {}), { - duration: s.animateDuration, - easing: s.animateEasing, - step: function() { - var s = { - width: parseFloat(i.element.css('width')), - height: parseFloat(i.element.css('height')), - top: parseFloat(i.element.css('top')), - left: parseFloat(i.element.css('left')) - }; - n && n.length && t(n[0]).css({ width: s.width, height: s.height }), - i._updateCache(s), - i._propagate('resize', e); - } - }); - } - }), - t.ui.plugin.add('resizable', 'containment', { - start: function() { - var e, - i, - s, - n, - o, - a, - r, - h = t(this).resizable('instance'), - l = h.options, - c = h.element, - u = l.containment, - d = - u instanceof t - ? u.get(0) - : /parent/.test(u) - ? c.parent().get(0) - : u; - d && - ((h.containerElement = t(d)), - /document/.test(u) || u === document - ? ((h.containerOffset = { left: 0, top: 0 }), - (h.containerPosition = { left: 0, top: 0 }), - (h.parentData = { - element: t(document), - left: 0, - top: 0, - width: t(document).width(), - height: - t(document).height() || document.body.parentNode.scrollHeight - })) - : ((e = t(d)), - (i = []), - t(['Top', 'Right', 'Left', 'Bottom']).each(function(t, s) { - i[t] = h._num(e.css('padding' + s)); - }), - (h.containerOffset = e.offset()), - (h.containerPosition = e.position()), - (h.containerSize = { - height: e.innerHeight() - i[3], - width: e.innerWidth() - i[1] - }), - (s = h.containerOffset), - (n = h.containerSize.height), - (o = h.containerSize.width), - (a = h._hasScroll(d, 'left') ? d.scrollWidth : o), - (r = h._hasScroll(d) ? d.scrollHeight : n), - (h.parentData = { - element: d, - left: s.left, - top: s.top, - width: a, - height: r - }))); - }, - resize: function(e) { - var i, - s, - n, - o, - a = t(this).resizable('instance'), - r = a.options, - h = a.containerOffset, - l = a.position, - c = a._aspectRatio || e.shiftKey, - u = { top: 0, left: 0 }, - d = a.containerElement, - p = !0; - d[0] !== document && /static/.test(d.css('position')) && (u = h), - l.left < (a._helper ? h.left : 0) && - ((a.size.width = - a.size.width + - (a._helper - ? a.position.left - h.left - : a.position.left - u.left)), - c && ((a.size.height = a.size.width / a.aspectRatio), (p = !1)), - (a.position.left = r.helper ? h.left : 0)), - l.top < (a._helper ? h.top : 0) && - ((a.size.height = - a.size.height + - (a._helper ? a.position.top - h.top : a.position.top)), - c && ((a.size.width = a.size.height * a.aspectRatio), (p = !1)), - (a.position.top = a._helper ? h.top : 0)), - (n = a.containerElement.get(0) === a.element.parent().get(0)), - (o = /relative|absolute/.test(a.containerElement.css('position'))), - n && o - ? ((a.offset.left = a.parentData.left + a.position.left), - (a.offset.top = a.parentData.top + a.position.top)) - : ((a.offset.left = a.element.offset().left), - (a.offset.top = a.element.offset().top)), - (i = Math.abs( - a.sizeDiff.width + - (a._helper ? a.offset.left - u.left : a.offset.left - h.left) - )), - (s = Math.abs( - a.sizeDiff.height + - (a._helper ? a.offset.top - u.top : a.offset.top - h.top) - )), - i + a.size.width >= a.parentData.width && - ((a.size.width = a.parentData.width - i), - c && ((a.size.height = a.size.width / a.aspectRatio), (p = !1))), - s + a.size.height >= a.parentData.height && - ((a.size.height = a.parentData.height - s), - c && ((a.size.width = a.size.height * a.aspectRatio), (p = !1))), - p || - ((a.position.left = a.prevPosition.left), - (a.position.top = a.prevPosition.top), - (a.size.width = a.prevSize.width), - (a.size.height = a.prevSize.height)); - }, - stop: function() { - var e = t(this).resizable('instance'), - i = e.options, - s = e.containerOffset, - n = e.containerPosition, - o = e.containerElement, - a = t(e.helper), - r = a.offset(), - h = a.outerWidth() - e.sizeDiff.width, - l = a.outerHeight() - e.sizeDiff.height; - e._helper && - !i.animate && - /relative/.test(o.css('position')) && - t(this).css({ left: r.left - n.left - s.left, width: h, height: l }), - e._helper && - !i.animate && - /static/.test(o.css('position')) && - t(this).css({ - left: r.left - n.left - s.left, - width: h, - height: l - }); - } - }), - t.ui.plugin.add('resizable', 'alsoResize', { - start: function() { - var e = t(this).resizable('instance'), - i = e.options; - t(i.alsoResize).each(function() { - var e = t(this); - e.data('ui-resizable-alsoresize', { - width: parseFloat(e.width()), - height: parseFloat(e.height()), - left: parseFloat(e.css('left')), - top: parseFloat(e.css('top')) - }); - }); - }, - resize: function(e, i) { - var s = t(this).resizable('instance'), - n = s.options, - o = s.originalSize, - a = s.originalPosition, - r = { - height: s.size.height - o.height || 0, - width: s.size.width - o.width || 0, - top: s.position.top - a.top || 0, - left: s.position.left - a.left || 0 - }; - t(n.alsoResize).each(function() { - var e = t(this), - s = t(this).data('ui-resizable-alsoresize'), - n = {}, - o = e.parents(i.originalElement[0]).length - ? ['width', 'height'] - : ['width', 'height', 'top', 'left']; - t.each(o, function(t, e) { - var i = (s[e] || 0) + (r[e] || 0); - i && i >= 0 && (n[e] = i || null); - }), - e.css(n); - }); - }, - stop: function() { - t(this).removeData('ui-resizable-alsoresize'); - } - }), - t.ui.plugin.add('resizable', 'ghost', { - start: function() { - var e = t(this).resizable('instance'), - i = e.size; - (e.ghost = e.originalElement.clone()), - e.ghost.css({ - opacity: 0.25, - display: 'block', - position: 'relative', - height: i.height, - width: i.width, - margin: 0, - left: 0, - top: 0 - }), - e._addClass(e.ghost, 'ui-resizable-ghost'), - t.uiBackCompat !== !1 && - 'string' == typeof e.options.ghost && - e.ghost.addClass(this.options.ghost), - e.ghost.appendTo(e.helper); - }, - resize: function() { - var e = t(this).resizable('instance'); - e.ghost && - e.ghost.css({ - position: 'relative', - height: e.size.height, - width: e.size.width - }); - }, - stop: function() { - var e = t(this).resizable('instance'); - e.ghost && e.helper && e.helper.get(0).removeChild(e.ghost.get(0)); - } - }), - t.ui.plugin.add('resizable', 'grid', { - resize: function() { - var e, - i = t(this).resizable('instance'), - s = i.options, - n = i.size, - o = i.originalSize, - a = i.originalPosition, - r = i.axis, - h = 'number' == typeof s.grid ? [s.grid, s.grid] : s.grid, - l = h[0] || 1, - c = h[1] || 1, - u = Math.round((n.width - o.width) / l) * l, - d = Math.round((n.height - o.height) / c) * c, - p = o.width + u, - f = o.height + d, - g = s.maxWidth && p > s.maxWidth, - m = s.maxHeight && f > s.maxHeight, - _ = s.minWidth && s.minWidth > p, - v = s.minHeight && s.minHeight > f; - (s.grid = h), - _ && (p += l), - v && (f += c), - g && (p -= l), - m && (f -= c), - /^(se|s|e)$/.test(r) - ? ((i.size.width = p), (i.size.height = f)) - : /^(ne)$/.test(r) - ? ((i.size.width = p), - (i.size.height = f), - (i.position.top = a.top - d)) - : /^(sw)$/.test(r) - ? ((i.size.width = p), - (i.size.height = f), - (i.position.left = a.left - u)) - : ((0 >= f - c || 0 >= p - l) && - (e = i._getPaddingPlusBorderDimensions(this)), - f - c > 0 - ? ((i.size.height = f), (i.position.top = a.top - d)) - : ((f = c - e.height), - (i.size.height = f), - (i.position.top = a.top + o.height - f)), - p - l > 0 - ? ((i.size.width = p), (i.position.left = a.left - u)) - : ((p = l - e.width), - (i.size.width = p), - (i.position.left = a.left + o.width - p))); - } - }), - t.ui.resizable, - t.widget('ui.dialog', { - version: '1.12.1', - options: { - appendTo: 'body', - autoOpen: !0, - buttons: [], - classes: { - 'ui-dialog': 'ui-corner-all', - 'ui-dialog-titlebar': 'ui-corner-all' - }, - closeOnEscape: !0, - closeText: 'Close', - draggable: !0, - hide: null, - height: 'auto', - maxHeight: null, - maxWidth: null, - minHeight: 150, - minWidth: 150, - modal: !1, - position: { - my: 'center', - at: 'center', - of: window, - collision: 'fit', - using: function(e) { - var i = t(this) - .css(e) - .offset().top; - 0 > i && t(this).css('top', e.top - i); - } - }, - resizable: !0, - show: null, - title: null, - width: 300, - beforeClose: null, - close: null, - drag: null, - dragStart: null, - dragStop: null, - focus: null, - open: null, - resize: null, - resizeStart: null, - resizeStop: null - }, - sizeRelatedOptions: { - buttons: !0, - height: !0, - maxHeight: !0, - maxWidth: !0, - minHeight: !0, - minWidth: !0, - width: !0 - }, - resizableRelatedOptions: { - maxHeight: !0, - maxWidth: !0, - minHeight: !0, - minWidth: !0 - }, - _create: function() { - (this.originalCss = { - display: this.element[0].style.display, - width: this.element[0].style.width, - minHeight: this.element[0].style.minHeight, - maxHeight: this.element[0].style.maxHeight, - height: this.element[0].style.height - }), - (this.originalPosition = { - parent: this.element.parent(), - index: this.element - .parent() - .children() - .index(this.element) - }), - (this.originalTitle = this.element.attr('title')), - null == this.options.title && - null != this.originalTitle && - (this.options.title = this.originalTitle), - this.options.disabled && (this.options.disabled = !1), - this._createWrapper(), - this.element - .show() - .removeAttr('title') - .appendTo(this.uiDialog), - this._addClass('ui-dialog-content', 'ui-widget-content'), - this._createTitlebar(), - this._createButtonPane(), - this.options.draggable && t.fn.draggable && this._makeDraggable(), - this.options.resizable && t.fn.resizable && this._makeResizable(), - (this._isOpen = !1), - this._trackFocus(); - }, - _init: function() { - this.options.autoOpen && this.open(); - }, - _appendTo: function() { - var e = this.options.appendTo; - return e && (e.jquery || e.nodeType) - ? t(e) - : this.document.find(e || 'body').eq(0); - }, - _destroy: function() { - var t, - e = this.originalPosition; - this._untrackInstance(), - this._destroyOverlay(), - this.element - .removeUniqueId() - .css(this.originalCss) - .detach(), - this.uiDialog.remove(), - this.originalTitle && this.element.attr('title', this.originalTitle), - (t = e.parent.children().eq(e.index)), - t.length && t[0] !== this.element[0] - ? t.before(this.element) - : e.parent.append(this.element); - }, - widget: function() { - return this.uiDialog; - }, - disable: t.noop, - enable: t.noop, - close: function(e) { - var i = this; - this._isOpen && - this._trigger('beforeClose', e) !== !1 && - ((this._isOpen = !1), - (this._focusedElement = null), - this._destroyOverlay(), - this._untrackInstance(), - this.opener.filter(':focusable').trigger('focus').length || - t.ui.safeBlur(t.ui.safeActiveElement(this.document[0])), - this._hide(this.uiDialog, this.options.hide, function() { - i._trigger('close', e); - })); - }, - isOpen: function() { - return this._isOpen; - }, - moveToTop: function() { - this._moveToTop(); - }, - _moveToTop: function(e, i) { - var s = !1, - n = this.uiDialog - .siblings('.ui-front:visible') - .map(function() { - return +t(this).css('z-index'); - }) - .get(), - o = Math.max.apply(null, n); - return ( - o >= +this.uiDialog.css('z-index') && - (this.uiDialog.css('z-index', o + 1), (s = !0)), - s && !i && this._trigger('focus', e), - s - ); - }, - open: function() { - var e = this; - return this._isOpen - ? (this._moveToTop() && this._focusTabbable(), void 0) - : ((this._isOpen = !0), - (this.opener = t(t.ui.safeActiveElement(this.document[0]))), - this._size(), - this._position(), - this._createOverlay(), - this._moveToTop(null, !0), - this.overlay && - this.overlay.css('z-index', this.uiDialog.css('z-index') - 1), - this._show(this.uiDialog, this.options.show, function() { - e._focusTabbable(), e._trigger('focus'); - }), - this._makeFocusTarget(), - this._trigger('open'), - void 0); - }, - _focusTabbable: function() { - var t = this._focusedElement; - t || (t = this.element.find('[autofocus]')), - t.length || (t = this.element.find(':tabbable')), - t.length || (t = this.uiDialogButtonPane.find(':tabbable')), - t.length || (t = this.uiDialogTitlebarClose.filter(':tabbable')), - t.length || (t = this.uiDialog), - t.eq(0).trigger('focus'); - }, - _keepFocus: function(e) { - function i() { - var e = t.ui.safeActiveElement(this.document[0]), - i = this.uiDialog[0] === e || t.contains(this.uiDialog[0], e); - i || this._focusTabbable(); - } - e.preventDefault(), i.call(this), this._delay(i); - }, - _createWrapper: function() { - (this.uiDialog = t('
      ') - .hide() - .attr({ tabIndex: -1, role: 'dialog' }) - .appendTo(this._appendTo())), - this._addClass( - this.uiDialog, - 'ui-dialog', - 'ui-widget ui-widget-content ui-front' - ), - this._on(this.uiDialog, { - keydown: function(e) { - if ( - this.options.closeOnEscape && - !e.isDefaultPrevented() && - e.keyCode && - e.keyCode === t.ui.keyCode.ESCAPE - ) - return e.preventDefault(), this.close(e), void 0; - if (e.keyCode === t.ui.keyCode.TAB && !e.isDefaultPrevented()) { - var i = this.uiDialog.find(':tabbable'), - s = i.filter(':first'), - n = i.filter(':last'); - (e.target !== n[0] && e.target !== this.uiDialog[0]) || - e.shiftKey - ? (e.target !== s[0] && e.target !== this.uiDialog[0]) || - !e.shiftKey || - (this._delay(function() { - n.trigger('focus'); - }), - e.preventDefault()) - : (this._delay(function() { - s.trigger('focus'); - }), - e.preventDefault()); - } - }, - mousedown: function(t) { - this._moveToTop(t) && this._focusTabbable(); - } - }), - this.element.find('[aria-describedby]').length || - this.uiDialog.attr({ - 'aria-describedby': this.element.uniqueId().attr('id') - }); - }, - _createTitlebar: function() { - var e; - (this.uiDialogTitlebar = t('
      ')), - this._addClass( - this.uiDialogTitlebar, - 'ui-dialog-titlebar', - 'ui-widget-header ui-helper-clearfix' - ), - this._on(this.uiDialogTitlebar, { - mousedown: function(e) { - t(e.target).closest('.ui-dialog-titlebar-close') || - this.uiDialog.trigger('focus'); - } - }), - (this.uiDialogTitlebarClose = t("") - .button({ - label: t('') - .text(this.options.closeText) - .html(), - icon: 'ui-icon-closethick', - showLabel: !1 - }) - .appendTo(this.uiDialogTitlebar)), - this._addClass( - this.uiDialogTitlebarClose, - 'ui-dialog-titlebar-close' - ), - this._on(this.uiDialogTitlebarClose, { - click: function(t) { - t.preventDefault(), this.close(t); - } - }), - (e = t('') - .uniqueId() - .prependTo(this.uiDialogTitlebar)), - this._addClass(e, 'ui-dialog-title'), - this._title(e), - this.uiDialogTitlebar.prependTo(this.uiDialog), - this.uiDialog.attr({ 'aria-labelledby': e.attr('id') }); - }, - _title: function(t) { - this.options.title ? t.text(this.options.title) : t.html(' '); - }, - _createButtonPane: function() { - (this.uiDialogButtonPane = t('
      ')), - this._addClass( - this.uiDialogButtonPane, - 'ui-dialog-buttonpane', - 'ui-widget-content ui-helper-clearfix' - ), - (this.uiButtonSet = t('
      ').appendTo(this.uiDialogButtonPane)), - this._addClass(this.uiButtonSet, 'ui-dialog-buttonset'), - this._createButtons(); - }, - _createButtons: function() { - var e = this, - i = this.options.buttons; - return ( - this.uiDialogButtonPane.remove(), - this.uiButtonSet.empty(), - t.isEmptyObject(i) || (t.isArray(i) && !i.length) - ? (this._removeClass(this.uiDialog, 'ui-dialog-buttons'), void 0) - : (t.each(i, function(i, s) { - var n, o; - (s = t.isFunction(s) ? { click: s, text: i } : s), - (s = t.extend({ type: 'button' }, s)), - (n = s.click), - (o = { - icon: s.icon, - iconPosition: s.iconPosition, - showLabel: s.showLabel, - icons: s.icons, - text: s.text - }), - delete s.click, - delete s.icon, - delete s.iconPosition, - delete s.showLabel, - delete s.icons, - 'boolean' == typeof s.text && delete s.text, - t('', s) - .button(o) - .appendTo(e.uiButtonSet) - .on('click', function() { - n.apply(e.element[0], arguments); - }); - }), - this._addClass(this.uiDialog, 'ui-dialog-buttons'), - this.uiDialogButtonPane.appendTo(this.uiDialog), - void 0) - ); - }, - _makeDraggable: function() { - function e(t) { - return { position: t.position, offset: t.offset }; - } - var i = this, - s = this.options; - this.uiDialog.draggable({ - cancel: '.ui-dialog-content, .ui-dialog-titlebar-close', - handle: '.ui-dialog-titlebar', - containment: 'document', - start: function(s, n) { - i._addClass(t(this), 'ui-dialog-dragging'), - i._blockFrames(), - i._trigger('dragStart', s, e(n)); - }, - drag: function(t, s) { - i._trigger('drag', t, e(s)); - }, - stop: function(n, o) { - var a = o.offset.left - i.document.scrollLeft(), - r = o.offset.top - i.document.scrollTop(); - (s.position = { - my: 'left top', - at: - 'left' + - (a >= 0 ? '+' : '') + - a + - ' ' + - 'top' + - (r >= 0 ? '+' : '') + - r, - of: i.window - }), - i._removeClass(t(this), 'ui-dialog-dragging'), - i._unblockFrames(), - i._trigger('dragStop', n, e(o)); - } - }); - }, - _makeResizable: function() { - function e(t) { - return { - originalPosition: t.originalPosition, - originalSize: t.originalSize, - position: t.position, - size: t.size - }; - } - var i = this, - s = this.options, - n = s.resizable, - o = this.uiDialog.css('position'), - a = 'string' == typeof n ? n : 'n,e,s,w,se,sw,ne,nw'; - this.uiDialog - .resizable({ - cancel: '.ui-dialog-content', - containment: 'document', - alsoResize: this.element, - maxWidth: s.maxWidth, - maxHeight: s.maxHeight, - minWidth: s.minWidth, - minHeight: this._minHeight(), - handles: a, - start: function(s, n) { - i._addClass(t(this), 'ui-dialog-resizing'), - i._blockFrames(), - i._trigger('resizeStart', s, e(n)); - }, - resize: function(t, s) { - i._trigger('resize', t, e(s)); - }, - stop: function(n, o) { - var a = i.uiDialog.offset(), - r = a.left - i.document.scrollLeft(), - h = a.top - i.document.scrollTop(); - (s.height = i.uiDialog.height()), - (s.width = i.uiDialog.width()), - (s.position = { - my: 'left top', - at: - 'left' + - (r >= 0 ? '+' : '') + - r + - ' ' + - 'top' + - (h >= 0 ? '+' : '') + - h, - of: i.window - }), - i._removeClass(t(this), 'ui-dialog-resizing'), - i._unblockFrames(), - i._trigger('resizeStop', n, e(o)); - } - }) - .css('position', o); - }, - _trackFocus: function() { - this._on(this.widget(), { - focusin: function(e) { - this._makeFocusTarget(), (this._focusedElement = t(e.target)); - } - }); - }, - _makeFocusTarget: function() { - this._untrackInstance(), this._trackingInstances().unshift(this); - }, - _untrackInstance: function() { - var e = this._trackingInstances(), - i = t.inArray(this, e); - -1 !== i && e.splice(i, 1); - }, - _trackingInstances: function() { - var t = this.document.data('ui-dialog-instances'); - return t || ((t = []), this.document.data('ui-dialog-instances', t)), t; - }, - _minHeight: function() { - var t = this.options; - return 'auto' === t.height - ? t.minHeight - : Math.min(t.minHeight, t.height); - }, - _position: function() { - var t = this.uiDialog.is(':visible'); - t || this.uiDialog.show(), - this.uiDialog.position(this.options.position), - t || this.uiDialog.hide(); - }, - _setOptions: function(e) { - var i = this, - s = !1, - n = {}; - t.each(e, function(t, e) { - i._setOption(t, e), - t in i.sizeRelatedOptions && (s = !0), - t in i.resizableRelatedOptions && (n[t] = e); - }), - s && (this._size(), this._position()), - this.uiDialog.is(':data(ui-resizable)') && - this.uiDialog.resizable('option', n); - }, - _setOption: function(e, i) { - var s, - n, - o = this.uiDialog; - 'disabled' !== e && - (this._super(e, i), - 'appendTo' === e && this.uiDialog.appendTo(this._appendTo()), - 'buttons' === e && this._createButtons(), - 'closeText' === e && - this.uiDialogTitlebarClose.button({ - label: t('') - .text('' + this.options.closeText) - .html() - }), - 'draggable' === e && - ((s = o.is(':data(ui-draggable)')), - s && !i && o.draggable('destroy'), - !s && i && this._makeDraggable()), - 'position' === e && this._position(), - 'resizable' === e && - ((n = o.is(':data(ui-resizable)')), - n && !i && o.resizable('destroy'), - n && 'string' == typeof i && o.resizable('option', 'handles', i), - n || i === !1 || this._makeResizable()), - 'title' === e && - this._title(this.uiDialogTitlebar.find('.ui-dialog-title'))); - }, - _size: function() { - var t, - e, - i, - s = this.options; - this.element - .show() - .css({ width: 'auto', minHeight: 0, maxHeight: 'none', height: 0 }), - s.minWidth > s.width && (s.width = s.minWidth), - (t = this.uiDialog - .css({ height: 'auto', width: s.width }) - .outerHeight()), - (e = Math.max(0, s.minHeight - t)), - (i = - 'number' == typeof s.maxHeight - ? Math.max(0, s.maxHeight - t) - : 'none'), - 'auto' === s.height - ? this.element.css({ minHeight: e, maxHeight: i, height: 'auto' }) - : this.element.height(Math.max(0, s.height - t)), - this.uiDialog.is(':data(ui-resizable)') && - this.uiDialog.resizable('option', 'minHeight', this._minHeight()); - }, - _blockFrames: function() { - this.iframeBlocks = this.document.find('iframe').map(function() { - var e = t(this); - return t('
      ') - .css({ - position: 'absolute', - width: e.outerWidth(), - height: e.outerHeight() - }) - .appendTo(e.parent()) - .offset(e.offset())[0]; - }); - }, - _unblockFrames: function() { - this.iframeBlocks && - (this.iframeBlocks.remove(), delete this.iframeBlocks); - }, - _allowInteraction: function(e) { - return t(e.target).closest('.ui-dialog').length - ? !0 - : !!t(e.target).closest('.ui-datepicker').length; - }, - _createOverlay: function() { - if (this.options.modal) { - var e = !0; - this._delay(function() { - e = !1; - }), - this.document.data('ui-dialog-overlays') || - this._on(this.document, { - focusin: function(t) { - e || - this._allowInteraction(t) || - (t.preventDefault(), - this._trackingInstances()[0]._focusTabbable()); - } - }), - (this.overlay = t('
      ').appendTo(this._appendTo())), - this._addClass(this.overlay, null, 'ui-widget-overlay ui-front'), - this._on(this.overlay, { mousedown: '_keepFocus' }), - this.document.data( - 'ui-dialog-overlays', - (this.document.data('ui-dialog-overlays') || 0) + 1 - ); - } - }, - _destroyOverlay: function() { - if (this.options.modal && this.overlay) { - var t = this.document.data('ui-dialog-overlays') - 1; - t - ? this.document.data('ui-dialog-overlays', t) - : (this._off(this.document, 'focusin'), - this.document.removeData('ui-dialog-overlays')), - this.overlay.remove(), - (this.overlay = null); - } - } - }), - t.uiBackCompat !== !1 && - t.widget('ui.dialog', t.ui.dialog, { - options: { dialogClass: '' }, - _createWrapper: function() { - this._super(), this.uiDialog.addClass(this.options.dialogClass); - }, - _setOption: function(t, e) { - 'dialogClass' === t && - this.uiDialog.removeClass(this.options.dialogClass).addClass(e), - this._superApply(arguments); - } - }), - t.ui.dialog, - t.widget('ui.droppable', { - version: '1.12.1', - widgetEventPrefix: 'drop', - options: { - accept: '*', - addClasses: !0, - greedy: !1, - scope: 'default', - tolerance: 'intersect', - activate: null, - deactivate: null, - drop: null, - out: null, - over: null - }, - _create: function() { - var e, - i = this.options, - s = i.accept; - (this.isover = !1), - (this.isout = !0), - (this.accept = t.isFunction(s) - ? s - : function(t) { - return t.is(s); - }), - (this.proportions = function() { - return arguments.length - ? ((e = arguments[0]), void 0) - : e - ? e - : (e = { - width: this.element[0].offsetWidth, - height: this.element[0].offsetHeight - }); - }), - this._addToManager(i.scope), - i.addClasses && this._addClass('ui-droppable'); - }, - _addToManager: function(e) { - (t.ui.ddmanager.droppables[e] = t.ui.ddmanager.droppables[e] || []), - t.ui.ddmanager.droppables[e].push(this); - }, - _splice: function(t) { - for (var e = 0; t.length > e; e++) t[e] === this && t.splice(e, 1); - }, - _destroy: function() { - var e = t.ui.ddmanager.droppables[this.options.scope]; - this._splice(e); - }, - _setOption: function(e, i) { - if ('accept' === e) - this.accept = t.isFunction(i) - ? i - : function(t) { - return t.is(i); - }; - else if ('scope' === e) { - var s = t.ui.ddmanager.droppables[this.options.scope]; - this._splice(s), this._addToManager(i); - } - this._super(e, i); - }, - _activate: function(e) { - var i = t.ui.ddmanager.current; - this._addActiveClass(), i && this._trigger('activate', e, this.ui(i)); - }, - _deactivate: function(e) { - var i = t.ui.ddmanager.current; - this._removeActiveClass(), - i && this._trigger('deactivate', e, this.ui(i)); - }, - _over: function(e) { - var i = t.ui.ddmanager.current; - i && - (i.currentItem || i.element)[0] !== this.element[0] && - this.accept.call(this.element[0], i.currentItem || i.element) && - (this._addHoverClass(), this._trigger('over', e, this.ui(i))); - }, - _out: function(e) { - var i = t.ui.ddmanager.current; - i && - (i.currentItem || i.element)[0] !== this.element[0] && - this.accept.call(this.element[0], i.currentItem || i.element) && - (this._removeHoverClass(), this._trigger('out', e, this.ui(i))); - }, - _drop: function(e, i) { - var s = i || t.ui.ddmanager.current, - n = !1; - return s && (s.currentItem || s.element)[0] !== this.element[0] - ? (this.element - .find(':data(ui-droppable)') - .not('.ui-draggable-dragging') - .each(function() { - var i = t(this).droppable('instance'); - return i.options.greedy && - !i.options.disabled && - i.options.scope === s.options.scope && - i.accept.call(i.element[0], s.currentItem || s.element) && - v( - s, - t.extend(i, { offset: i.element.offset() }), - i.options.tolerance, - e - ) - ? ((n = !0), !1) - : void 0; - }), - n - ? !1 - : this.accept.call(this.element[0], s.currentItem || s.element) - ? (this._removeActiveClass(), - this._removeHoverClass(), - this._trigger('drop', e, this.ui(s)), - this.element) - : !1) - : !1; - }, - ui: function(t) { - return { - draggable: t.currentItem || t.element, - helper: t.helper, - position: t.position, - offset: t.positionAbs - }; - }, - _addHoverClass: function() { - this._addClass('ui-droppable-hover'); - }, - _removeHoverClass: function() { - this._removeClass('ui-droppable-hover'); - }, - _addActiveClass: function() { - this._addClass('ui-droppable-active'); - }, - _removeActiveClass: function() { - this._removeClass('ui-droppable-active'); - } - }); - var v = (t.ui.intersect = (function() { - function t(t, e, i) { - return t >= e && e + i > t; - } - return function(e, i, s, n) { - if (!i.offset) return !1; - var o = (e.positionAbs || e.position.absolute).left + e.margins.left, - a = (e.positionAbs || e.position.absolute).top + e.margins.top, - r = o + e.helperProportions.width, - h = a + e.helperProportions.height, - l = i.offset.left, - c = i.offset.top, - u = l + i.proportions().width, - d = c + i.proportions().height; - switch (s) { - case 'fit': - return o >= l && u >= r && a >= c && d >= h; - case 'intersect': - return ( - o + e.helperProportions.width / 2 > l && - u > r - e.helperProportions.width / 2 && - a + e.helperProportions.height / 2 > c && - d > h - e.helperProportions.height / 2 - ); - case 'pointer': - return ( - t(n.pageY, c, i.proportions().height) && - t(n.pageX, l, i.proportions().width) - ); - case 'touch': - return ( - ((a >= c && d >= a) || (h >= c && d >= h) || (c > a && h > d)) && - ((o >= l && u >= o) || (r >= l && u >= r) || (l > o && r > u)) - ); - default: - return !1; - } - }; - })()); - (t.ui.ddmanager = { - current: null, - droppables: { default: [] }, - prepareOffsets: function(e, i) { - var s, - n, - o = t.ui.ddmanager.droppables[e.options.scope] || [], - a = i ? i.type : null, - r = (e.currentItem || e.element).find(':data(ui-droppable)').addBack(); - t: for (s = 0; o.length > s; s++) - if ( - !( - o[s].options.disabled || - (e && - !o[s].accept.call(o[s].element[0], e.currentItem || e.element)) - ) - ) { - for (n = 0; r.length > n; n++) - if (r[n] === o[s].element[0]) { - o[s].proportions().height = 0; - continue t; - } - (o[s].visible = 'none' !== o[s].element.css('display')), - o[s].visible && - ('mousedown' === a && o[s]._activate.call(o[s], i), - (o[s].offset = o[s].element.offset()), - o[s].proportions({ - width: o[s].element[0].offsetWidth, - height: o[s].element[0].offsetHeight - })); - } - }, - drop: function(e, i) { - var s = !1; - return ( - t.each( - (t.ui.ddmanager.droppables[e.options.scope] || []).slice(), - function() { - this.options && - (!this.options.disabled && - this.visible && - v(e, this, this.options.tolerance, i) && - (s = this._drop.call(this, i) || s), - !this.options.disabled && - this.visible && - this.accept.call(this.element[0], e.currentItem || e.element) && - ((this.isout = !0), - (this.isover = !1), - this._deactivate.call(this, i))); - } - ), - s - ); - }, - dragStart: function(e, i) { - e.element.parentsUntil('body').on('scroll.droppable', function() { - e.options.refreshPositions || t.ui.ddmanager.prepareOffsets(e, i); - }); - }, - drag: function(e, i) { - e.options.refreshPositions && t.ui.ddmanager.prepareOffsets(e, i), - t.each(t.ui.ddmanager.droppables[e.options.scope] || [], function() { - if (!this.options.disabled && !this.greedyChild && this.visible) { - var s, - n, - o, - a = v(e, this, this.options.tolerance, i), - r = - !a && this.isover - ? 'isout' - : a && !this.isover - ? 'isover' - : null; - r && - (this.options.greedy && - ((n = this.options.scope), - (o = this.element - .parents(':data(ui-droppable)') - .filter(function() { - return t(this).droppable('instance').options.scope === n; - })), - o.length && - ((s = t(o[0]).droppable('instance')), - (s.greedyChild = 'isover' === r))), - s && - 'isover' === r && - ((s.isover = !1), (s.isout = !0), s._out.call(s, i)), - (this[r] = !0), - (this['isout' === r ? 'isover' : 'isout'] = !1), - this['isover' === r ? '_over' : '_out'].call(this, i), - s && - 'isout' === r && - ((s.isout = !1), (s.isover = !0), s._over.call(s, i))); - } - }); - }, - dragStop: function(e, i) { - e.element.parentsUntil('body').off('scroll.droppable'), - e.options.refreshPositions || t.ui.ddmanager.prepareOffsets(e, i); - } - }), - t.uiBackCompat !== !1 && - t.widget('ui.droppable', t.ui.droppable, { - options: { hoverClass: !1, activeClass: !1 }, - _addActiveClass: function() { - this._super(), - this.options.activeClass && - this.element.addClass(this.options.activeClass); - }, - _removeActiveClass: function() { - this._super(), - this.options.activeClass && - this.element.removeClass(this.options.activeClass); - }, - _addHoverClass: function() { - this._super(), - this.options.hoverClass && - this.element.addClass(this.options.hoverClass); - }, - _removeHoverClass: function() { - this._super(), - this.options.hoverClass && - this.element.removeClass(this.options.hoverClass); - } - }), - t.ui.droppable, - t.widget('ui.progressbar', { - version: '1.12.1', - options: { - classes: { - 'ui-progressbar': 'ui-corner-all', - 'ui-progressbar-value': 'ui-corner-left', - 'ui-progressbar-complete': 'ui-corner-right' - }, - max: 100, - value: 0, - change: null, - complete: null - }, - min: 0, - _create: function() { - (this.oldValue = this.options.value = this._constrainedValue()), - this.element.attr({ role: 'progressbar', 'aria-valuemin': this.min }), - this._addClass('ui-progressbar', 'ui-widget ui-widget-content'), - (this.valueDiv = t('
      ').appendTo(this.element)), - this._addClass( - this.valueDiv, - 'ui-progressbar-value', - 'ui-widget-header' - ), - this._refreshValue(); - }, - _destroy: function() { - this.element.removeAttr( - 'role aria-valuemin aria-valuemax aria-valuenow' - ), - this.valueDiv.remove(); - }, - value: function(t) { - return void 0 === t - ? this.options.value - : ((this.options.value = this._constrainedValue(t)), - this._refreshValue(), - void 0); - }, - _constrainedValue: function(t) { - return ( - void 0 === t && (t = this.options.value), - (this.indeterminate = t === !1), - 'number' != typeof t && (t = 0), - this.indeterminate - ? !1 - : Math.min(this.options.max, Math.max(this.min, t)) - ); - }, - _setOptions: function(t) { - var e = t.value; - delete t.value, - this._super(t), - (this.options.value = this._constrainedValue(e)), - this._refreshValue(); - }, - _setOption: function(t, e) { - 'max' === t && (e = Math.max(this.min, e)), this._super(t, e); - }, - _setOptionDisabled: function(t) { - this._super(t), - this.element.attr('aria-disabled', t), - this._toggleClass(null, 'ui-state-disabled', !!t); - }, - _percentage: function() { - return this.indeterminate - ? 100 - : (100 * (this.options.value - this.min)) / - (this.options.max - this.min); - }, - _refreshValue: function() { - var e = this.options.value, - i = this._percentage(); - this.valueDiv - .toggle(this.indeterminate || e > this.min) - .width(i.toFixed(0) + '%'), - this._toggleClass( - this.valueDiv, - 'ui-progressbar-complete', - null, - e === this.options.max - )._toggleClass( - 'ui-progressbar-indeterminate', - null, - this.indeterminate - ), - this.indeterminate - ? (this.element.removeAttr('aria-valuenow'), - this.overlayDiv || - ((this.overlayDiv = t('
      ').appendTo(this.valueDiv)), - this._addClass(this.overlayDiv, 'ui-progressbar-overlay'))) - : (this.element.attr({ - 'aria-valuemax': this.options.max, - 'aria-valuenow': e - }), - this.overlayDiv && - (this.overlayDiv.remove(), (this.overlayDiv = null))), - this.oldValue !== e && ((this.oldValue = e), this._trigger('change')), - e === this.options.max && this._trigger('complete'); - } - }), - t.widget('ui.selectable', t.ui.mouse, { - version: '1.12.1', - options: { - appendTo: 'body', - autoRefresh: !0, - distance: 0, - filter: '*', - tolerance: 'touch', - selected: null, - selecting: null, - start: null, - stop: null, - unselected: null, - unselecting: null - }, - _create: function() { - var e = this; - this._addClass('ui-selectable'), - (this.dragged = !1), - (this.refresh = function() { - (e.elementPos = t(e.element[0]).offset()), - (e.selectees = t(e.options.filter, e.element[0])), - e._addClass(e.selectees, 'ui-selectee'), - e.selectees.each(function() { - var i = t(this), - s = i.offset(), - n = { - left: s.left - e.elementPos.left, - top: s.top - e.elementPos.top - }; - t.data(this, 'selectable-item', { - element: this, - $element: i, - left: n.left, - top: n.top, - right: n.left + i.outerWidth(), - bottom: n.top + i.outerHeight(), - startselected: !1, - selected: i.hasClass('ui-selected'), - selecting: i.hasClass('ui-selecting'), - unselecting: i.hasClass('ui-unselecting') - }); - }); - }), - this.refresh(), - this._mouseInit(), - (this.helper = t('
      ')), - this._addClass(this.helper, 'ui-selectable-helper'); - }, - _destroy: function() { - this.selectees.removeData('selectable-item'), this._mouseDestroy(); - }, - _mouseStart: function(e) { - var i = this, - s = this.options; - (this.opos = [e.pageX, e.pageY]), - (this.elementPos = t(this.element[0]).offset()), - this.options.disabled || - ((this.selectees = t(s.filter, this.element[0])), - this._trigger('start', e), - t(s.appendTo).append(this.helper), - this.helper.css({ - left: e.pageX, - top: e.pageY, - width: 0, - height: 0 - }), - s.autoRefresh && this.refresh(), - this.selectees.filter('.ui-selected').each(function() { - var s = t.data(this, 'selectable-item'); - (s.startselected = !0), - e.metaKey || - e.ctrlKey || - (i._removeClass(s.$element, 'ui-selected'), - (s.selected = !1), - i._addClass(s.$element, 'ui-unselecting'), - (s.unselecting = !0), - i._trigger('unselecting', e, { unselecting: s.element })); - }), - t(e.target) - .parents() - .addBack() - .each(function() { - var s, - n = t.data(this, 'selectable-item'); - return n - ? ((s = - (!e.metaKey && !e.ctrlKey) || - !n.$element.hasClass('ui-selected')), - i - ._removeClass( - n.$element, - s ? 'ui-unselecting' : 'ui-selected' - ) - ._addClass( - n.$element, - s ? 'ui-selecting' : 'ui-unselecting' - ), - (n.unselecting = !s), - (n.selecting = s), - (n.selected = s), - s - ? i._trigger('selecting', e, { selecting: n.element }) - : i._trigger('unselecting', e, { - unselecting: n.element - }), - !1) - : void 0; - })); - }, - _mouseDrag: function(e) { - if (((this.dragged = !0), !this.options.disabled)) { - var i, - s = this, - n = this.options, - o = this.opos[0], - a = this.opos[1], - r = e.pageX, - h = e.pageY; - return ( - o > r && ((i = r), (r = o), (o = i)), - a > h && ((i = h), (h = a), (a = i)), - this.helper.css({ left: o, top: a, width: r - o, height: h - a }), - this.selectees.each(function() { - var i = t.data(this, 'selectable-item'), - l = !1, - c = {}; - i && - i.element !== s.element[0] && - ((c.left = i.left + s.elementPos.left), - (c.right = i.right + s.elementPos.left), - (c.top = i.top + s.elementPos.top), - (c.bottom = i.bottom + s.elementPos.top), - 'touch' === n.tolerance - ? (l = !( - c.left > r || - o > c.right || - c.top > h || - a > c.bottom - )) - : 'fit' === n.tolerance && - (l = - c.left > o && r > c.right && c.top > a && h > c.bottom), - l - ? (i.selected && - (s._removeClass(i.$element, 'ui-selected'), - (i.selected = !1)), - i.unselecting && - (s._removeClass(i.$element, 'ui-unselecting'), - (i.unselecting = !1)), - i.selecting || - (s._addClass(i.$element, 'ui-selecting'), - (i.selecting = !0), - s._trigger('selecting', e, { selecting: i.element }))) - : (i.selecting && - ((e.metaKey || e.ctrlKey) && i.startselected - ? (s._removeClass(i.$element, 'ui-selecting'), - (i.selecting = !1), - s._addClass(i.$element, 'ui-selected'), - (i.selected = !0)) - : (s._removeClass(i.$element, 'ui-selecting'), - (i.selecting = !1), - i.startselected && - (s._addClass(i.$element, 'ui-unselecting'), - (i.unselecting = !0)), - s._trigger('unselecting', e, { - unselecting: i.element - }))), - i.selected && - (e.metaKey || - e.ctrlKey || - i.startselected || - (s._removeClass(i.$element, 'ui-selected'), - (i.selected = !1), - s._addClass(i.$element, 'ui-unselecting'), - (i.unselecting = !0), - s._trigger('unselecting', e, { - unselecting: i.element - }))))); - }), - !1 - ); - } - }, - _mouseStop: function(e) { - var i = this; - return ( - (this.dragged = !1), - t('.ui-unselecting', this.element[0]).each(function() { - var s = t.data(this, 'selectable-item'); - i._removeClass(s.$element, 'ui-unselecting'), - (s.unselecting = !1), - (s.startselected = !1), - i._trigger('unselected', e, { unselected: s.element }); - }), - t('.ui-selecting', this.element[0]).each(function() { - var s = t.data(this, 'selectable-item'); - i - ._removeClass(s.$element, 'ui-selecting') - ._addClass(s.$element, 'ui-selected'), - (s.selecting = !1), - (s.selected = !0), - (s.startselected = !0), - i._trigger('selected', e, { selected: s.element }); - }), - this._trigger('stop', e), - this.helper.remove(), - !1 - ); - } - }), - t.widget('ui.selectmenu', [ - t.ui.formResetMixin, - { - version: '1.12.1', - defaultElement: '', - widgetEventPrefix: 'spin', - options: { - classes: { - 'ui-spinner': 'ui-corner-all', - 'ui-spinner-down': 'ui-corner-br', - 'ui-spinner-up': 'ui-corner-tr' - }, - culture: null, - icons: { down: 'ui-icon-triangle-1-s', up: 'ui-icon-triangle-1-n' }, - incremental: !0, - max: null, - min: null, - numberFormat: null, - page: 10, - step: 1, - change: null, - spin: null, - start: null, - stop: null - }, - _create: function() { - this._setOption('max', this.options.max), - this._setOption('min', this.options.min), - this._setOption('step', this.options.step), - '' !== this.value() && this._value(this.element.val(), !0), - this._draw(), - this._on(this._events), - this._refresh(), - this._on(this.window, { - beforeunload: function() { - this.element.removeAttr('autocomplete'); - } - }); - }, - _getCreateOptions: function() { - var e = this._super(), - i = this.element; - return ( - t.each(['min', 'max', 'step'], function(t, s) { - var n = i.attr(s); - null != n && n.length && (e[s] = n); - }), - e - ); - }, - _events: { - keydown: function(t) { - this._start(t) && this._keydown(t) && t.preventDefault(); - }, - keyup: '_stop', - focus: function() { - this.previous = this.element.val(); - }, - blur: function(t) { - return this.cancelBlur - ? (delete this.cancelBlur, void 0) - : (this._stop(), - this._refresh(), - this.previous !== this.element.val() && - this._trigger('change', t), - void 0); - }, - mousewheel: function(t, e) { - if (e) { - if (!this.spinning && !this._start(t)) return !1; - this._spin((e > 0 ? 1 : -1) * this.options.step, t), - clearTimeout(this.mousewheelTimer), - (this.mousewheelTimer = this._delay(function() { - this.spinning && this._stop(t); - }, 100)), - t.preventDefault(); - } - }, - 'mousedown .ui-spinner-button': function(e) { - function i() { - var e = - this.element[0] === t.ui.safeActiveElement(this.document[0]); - e || - (this.element.trigger('focus'), - (this.previous = s), - this._delay(function() { - this.previous = s; - })); - } - var s; - (s = - this.element[0] === t.ui.safeActiveElement(this.document[0]) - ? this.previous - : this.element.val()), - e.preventDefault(), - i.call(this), - (this.cancelBlur = !0), - this._delay(function() { - delete this.cancelBlur, i.call(this); - }), - this._start(e) !== !1 && - this._repeat( - null, - t(e.currentTarget).hasClass('ui-spinner-up') ? 1 : -1, - e - ); - }, - 'mouseup .ui-spinner-button': '_stop', - 'mouseenter .ui-spinner-button': function(e) { - return t(e.currentTarget).hasClass('ui-state-active') - ? this._start(e) === !1 - ? !1 - : (this._repeat( - null, - t(e.currentTarget).hasClass('ui-spinner-up') ? 1 : -1, - e - ), - void 0) - : void 0; - }, - 'mouseleave .ui-spinner-button': '_stop' - }, - _enhance: function() { - this.uiSpinner = this.element - .attr('autocomplete', 'off') - .wrap('') - .parent() - .append(''); - }, - _draw: function() { - this._enhance(), - this._addClass( - this.uiSpinner, - 'ui-spinner', - 'ui-widget ui-widget-content' - ), - this._addClass('ui-spinner-input'), - this.element.attr('role', 'spinbutton'), - (this.buttons = this.uiSpinner - .children('a') - .attr('tabIndex', -1) - .attr('aria-hidden', !0) - .button({ classes: { 'ui-button': '' } })), - this._removeClass(this.buttons, 'ui-corner-all'), - this._addClass( - this.buttons.first(), - 'ui-spinner-button ui-spinner-up' - ), - this._addClass( - this.buttons.last(), - 'ui-spinner-button ui-spinner-down' - ), - this.buttons - .first() - .button({ icon: this.options.icons.up, showLabel: !1 }), - this.buttons - .last() - .button({ icon: this.options.icons.down, showLabel: !1 }), - this.buttons.height() > Math.ceil(0.5 * this.uiSpinner.height()) && - this.uiSpinner.height() > 0 && - this.uiSpinner.height(this.uiSpinner.height()); - }, - _keydown: function(e) { - var i = this.options, - s = t.ui.keyCode; - switch (e.keyCode) { - case s.UP: - return this._repeat(null, 1, e), !0; - case s.DOWN: - return this._repeat(null, -1, e), !0; - case s.PAGE_UP: - return this._repeat(null, i.page, e), !0; - case s.PAGE_DOWN: - return this._repeat(null, -i.page, e), !0; - } - return !1; - }, - _start: function(t) { - return this.spinning || this._trigger('start', t) !== !1 - ? (this.counter || (this.counter = 1), (this.spinning = !0), !0) - : !1; - }, - _repeat: function(t, e, i) { - (t = t || 500), - clearTimeout(this.timer), - (this.timer = this._delay(function() { - this._repeat(40, e, i); - }, t)), - this._spin(e * this.options.step, i); - }, - _spin: function(t, e) { - var i = this.value() || 0; - this.counter || (this.counter = 1), - (i = this._adjustValue(i + t * this._increment(this.counter))), - (this.spinning && this._trigger('spin', e, { value: i }) === !1) || - (this._value(i), this.counter++); - }, - _increment: function(e) { - var i = this.options.incremental; - return i - ? t.isFunction(i) - ? i(e) - : Math.floor((e * e * e) / 5e4 - (e * e) / 500 + (17 * e) / 200 + 1) - : 1; - }, - _precision: function() { - var t = this._precisionOf(this.options.step); - return ( - null !== this.options.min && - (t = Math.max(t, this._precisionOf(this.options.min))), - t - ); - }, - _precisionOf: function(t) { - var e = '' + t, - i = e.indexOf('.'); - return -1 === i ? 0 : e.length - i - 1; - }, - _adjustValue: function(t) { - var e, - i, - s = this.options; - return ( - (e = null !== s.min ? s.min : 0), - (i = t - e), - (i = Math.round(i / s.step) * s.step), - (t = e + i), - (t = parseFloat(t.toFixed(this._precision()))), - null !== s.max && t > s.max - ? s.max - : null !== s.min && s.min > t - ? s.min - : t - ); - }, - _stop: function(t) { - this.spinning && - (clearTimeout(this.timer), - clearTimeout(this.mousewheelTimer), - (this.counter = 0), - (this.spinning = !1), - this._trigger('stop', t)); - }, - _setOption: function(t, e) { - var i, s, n; - return 'culture' === t || 'numberFormat' === t - ? ((i = this._parse(this.element.val())), - (this.options[t] = e), - this.element.val(this._format(i)), - void 0) - : (('max' === t || 'min' === t || 'step' === t) && - 'string' == typeof e && - (e = this._parse(e)), - 'icons' === t && - ((s = this.buttons.first().find('.ui-icon')), - this._removeClass(s, null, this.options.icons.up), - this._addClass(s, null, e.up), - (n = this.buttons.last().find('.ui-icon')), - this._removeClass(n, null, this.options.icons.down), - this._addClass(n, null, e.down)), - this._super(t, e), - void 0); - }, - _setOptionDisabled: function(t) { - this._super(t), - this._toggleClass(this.uiSpinner, null, 'ui-state-disabled', !!t), - this.element.prop('disabled', !!t), - this.buttons.button(t ? 'disable' : 'enable'); - }, - _setOptions: r(function(t) { - this._super(t); - }), - _parse: function(t) { - return ( - 'string' == typeof t && - '' !== t && - (t = - window.Globalize && this.options.numberFormat - ? Globalize.parseFloat(t, 10, this.options.culture) - : +t), - '' === t || isNaN(t) ? null : t - ); - }, - _format: function(t) { - return '' === t - ? '' - : window.Globalize && this.options.numberFormat - ? Globalize.format(t, this.options.numberFormat, this.options.culture) - : t; - }, - _refresh: function() { - this.element.attr({ - 'aria-valuemin': this.options.min, - 'aria-valuemax': this.options.max, - 'aria-valuenow': this._parse(this.element.val()) - }); - }, - isValid: function() { - var t = this.value(); - return null === t ? !1 : t === this._adjustValue(t); - }, - _value: function(t, e) { - var i; - '' !== t && - ((i = this._parse(t)), - null !== i && - (e || (i = this._adjustValue(i)), (t = this._format(i)))), - this.element.val(t), - this._refresh(); - }, - _destroy: function() { - this.element - .prop('disabled', !1) - .removeAttr( - 'autocomplete role aria-valuemin aria-valuemax aria-valuenow' - ), - this.uiSpinner.replaceWith(this.element); - }, - stepUp: r(function(t) { - this._stepUp(t); - }), - _stepUp: function(t) { - this._start() && - (this._spin((t || 1) * this.options.step), this._stop()); - }, - stepDown: r(function(t) { - this._stepDown(t); - }), - _stepDown: function(t) { - this._start() && - (this._spin((t || 1) * -this.options.step), this._stop()); - }, - pageUp: r(function(t) { - this._stepUp((t || 1) * this.options.page); - }), - pageDown: r(function(t) { - this._stepDown((t || 1) * this.options.page); - }), - value: function(t) { - return arguments.length - ? (r(this._value).call(this, t), void 0) - : this._parse(this.element.val()); - }, - widget: function() { - return this.uiSpinner; - } - }), - t.uiBackCompat !== !1 && - t.widget('ui.spinner', t.ui.spinner, { - _enhance: function() { - this.uiSpinner = this.element - .attr('autocomplete', 'off') - .wrap(this._uiSpinnerHtml()) - .parent() - .append(this._buttonHtml()); - }, - _uiSpinnerHtml: function() { - return ''; - }, - _buttonHtml: function() { - return ''; - } - }), - t.ui.spinner, - t.widget('ui.tabs', { - version: '1.12.1', - delay: 300, - options: { - active: null, - classes: { - 'ui-tabs': 'ui-corner-all', - 'ui-tabs-nav': 'ui-corner-all', - 'ui-tabs-panel': 'ui-corner-bottom', - 'ui-tabs-tab': 'ui-corner-top' - }, - collapsible: !1, - event: 'click', - heightStyle: 'content', - hide: null, - show: null, - activate: null, - beforeActivate: null, - beforeLoad: null, - load: null - }, - _isLocal: (function() { - var t = /#.*$/; - return function(e) { - var i, s; - (i = e.href.replace(t, '')), (s = location.href.replace(t, '')); - try { - i = decodeURIComponent(i); - } catch (n) {} - try { - s = decodeURIComponent(s); - } catch (n) {} - return e.hash.length > 1 && i === s; - }; - })(), - _create: function() { - var e = this, - i = this.options; - (this.running = !1), - this._addClass('ui-tabs', 'ui-widget ui-widget-content'), - this._toggleClass('ui-tabs-collapsible', null, i.collapsible), - this._processTabs(), - (i.active = this._initialActive()), - t.isArray(i.disabled) && - (i.disabled = t - .unique( - i.disabled.concat( - t.map(this.tabs.filter('.ui-state-disabled'), function(t) { - return e.tabs.index(t); - }) - ) - ) - .sort()), - (this.active = - this.options.active !== !1 && this.anchors.length - ? this._findActive(i.active) - : t()), - this._refresh(), - this.active.length && this.load(i.active); - }, - _initialActive: function() { - var e = this.options.active, - i = this.options.collapsible, - s = location.hash.substring(1); - return ( - null === e && - (s && - this.tabs.each(function(i, n) { - return t(n).attr('aria-controls') === s - ? ((e = i), !1) - : void 0; - }), - null === e && - (e = this.tabs.index(this.tabs.filter('.ui-tabs-active'))), - (null === e || -1 === e) && (e = this.tabs.length ? 0 : !1)), - e !== !1 && - ((e = this.tabs.index(this.tabs.eq(e))), - -1 === e && (e = i ? !1 : 0)), - !i && e === !1 && this.anchors.length && (e = 0), - e - ); - }, - _getCreateEventData: function() { - return { - tab: this.active, - panel: this.active.length ? this._getPanelForTab(this.active) : t() - }; - }, - _tabKeydown: function(e) { - var i = t(t.ui.safeActiveElement(this.document[0])).closest('li'), - s = this.tabs.index(i), - n = !0; - if (!this._handlePageNav(e)) { - switch (e.keyCode) { - case t.ui.keyCode.RIGHT: - case t.ui.keyCode.DOWN: - s++; - break; - case t.ui.keyCode.UP: - case t.ui.keyCode.LEFT: - (n = !1), s--; - break; - case t.ui.keyCode.END: - s = this.anchors.length - 1; - break; - case t.ui.keyCode.HOME: - s = 0; - break; - case t.ui.keyCode.SPACE: - return ( - e.preventDefault(), - clearTimeout(this.activating), - this._activate(s), - void 0 - ); - case t.ui.keyCode.ENTER: - return ( - e.preventDefault(), - clearTimeout(this.activating), - this._activate(s === this.options.active ? !1 : s), - void 0 - ); - default: - return; - } - e.preventDefault(), - clearTimeout(this.activating), - (s = this._focusNextTab(s, n)), - e.ctrlKey || - e.metaKey || - (i.attr('aria-selected', 'false'), - this.tabs.eq(s).attr('aria-selected', 'true'), - (this.activating = this._delay(function() { - this.option('active', s); - }, this.delay))); - } - }, - _panelKeydown: function(e) { - this._handlePageNav(e) || - (e.ctrlKey && - e.keyCode === t.ui.keyCode.UP && - (e.preventDefault(), this.active.trigger('focus'))); - }, - _handlePageNav: function(e) { - return e.altKey && e.keyCode === t.ui.keyCode.PAGE_UP - ? (this._activate(this._focusNextTab(this.options.active - 1, !1)), - !0) - : e.altKey && e.keyCode === t.ui.keyCode.PAGE_DOWN - ? (this._activate(this._focusNextTab(this.options.active + 1, !0)), - !0) - : void 0; - }, - _findNextTab: function(e, i) { - function s() { - return e > n && (e = 0), 0 > e && (e = n), e; - } - for ( - var n = this.tabs.length - 1; - -1 !== t.inArray(s(), this.options.disabled); - - ) - e = i ? e + 1 : e - 1; - return e; - }, - _focusNextTab: function(t, e) { - return ( - (t = this._findNextTab(t, e)), this.tabs.eq(t).trigger('focus'), t - ); - }, - _setOption: function(t, e) { - return 'active' === t - ? (this._activate(e), void 0) - : (this._super(t, e), - 'collapsible' === t && - (this._toggleClass('ui-tabs-collapsible', null, e), - e || this.options.active !== !1 || this._activate(0)), - 'event' === t && this._setupEvents(e), - 'heightStyle' === t && this._setupHeightStyle(e), - void 0); - }, - _sanitizeSelector: function(t) { - return t - ? t.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, '\\$&') - : ''; - }, - refresh: function() { - var e = this.options, - i = this.tablist.children(':has(a[href])'); - (e.disabled = t.map(i.filter('.ui-state-disabled'), function(t) { - return i.index(t); - })), - this._processTabs(), - e.active !== !1 && this.anchors.length - ? this.active.length && !t.contains(this.tablist[0], this.active[0]) - ? this.tabs.length === e.disabled.length - ? ((e.active = !1), (this.active = t())) - : this._activate( - this._findNextTab(Math.max(0, e.active - 1), !1) - ) - : (e.active = this.tabs.index(this.active)) - : ((e.active = !1), (this.active = t())), - this._refresh(); - }, - _refresh: function() { - this._setOptionDisabled(this.options.disabled), - this._setupEvents(this.options.event), - this._setupHeightStyle(this.options.heightStyle), - this.tabs.not(this.active).attr({ - 'aria-selected': 'false', - 'aria-expanded': 'false', - tabIndex: -1 - }), - this.panels - .not(this._getPanelForTab(this.active)) - .hide() - .attr({ 'aria-hidden': 'true' }), - this.active.length - ? (this.active.attr({ - 'aria-selected': 'true', - 'aria-expanded': 'true', - tabIndex: 0 - }), - this._addClass(this.active, 'ui-tabs-active', 'ui-state-active'), - this._getPanelForTab(this.active) - .show() - .attr({ 'aria-hidden': 'false' })) - : this.tabs.eq(0).attr('tabIndex', 0); - }, - _processTabs: function() { - var e = this, - i = this.tabs, - s = this.anchors, - n = this.panels; - (this.tablist = this._getList().attr('role', 'tablist')), - this._addClass( - this.tablist, - 'ui-tabs-nav', - 'ui-helper-reset ui-helper-clearfix ui-widget-header' - ), - this.tablist - .on('mousedown' + this.eventNamespace, '> li', function(e) { - t(this).is('.ui-state-disabled') && e.preventDefault(); - }) - .on('focus' + this.eventNamespace, '.ui-tabs-anchor', function() { - t(this) - .closest('li') - .is('.ui-state-disabled') && this.blur(); - }), - (this.tabs = this.tablist - .find('> li:has(a[href])') - .attr({ role: 'tab', tabIndex: -1 })), - this._addClass(this.tabs, 'ui-tabs-tab', 'ui-state-default'), - (this.anchors = this.tabs - .map(function() { - return t('a', this)[0]; - }) - .attr({ role: 'presentation', tabIndex: -1 })), - this._addClass(this.anchors, 'ui-tabs-anchor'), - (this.panels = t()), - this.anchors.each(function(i, s) { - var n, - o, - a, - r = t(s) - .uniqueId() - .attr('id'), - h = t(s).closest('li'), - l = h.attr('aria-controls'); - e._isLocal(s) - ? ((n = s.hash), - (a = n.substring(1)), - (o = e.element.find(e._sanitizeSelector(n)))) - : ((a = h.attr('aria-controls') || t({}).uniqueId()[0].id), - (n = '#' + a), - (o = e.element.find(n)), - o.length || - ((o = e._createPanel(a)), - o.insertAfter(e.panels[i - 1] || e.tablist)), - o.attr('aria-live', 'polite')), - o.length && (e.panels = e.panels.add(o)), - l && h.data('ui-tabs-aria-controls', l), - h.attr({ 'aria-controls': a, 'aria-labelledby': r }), - o.attr('aria-labelledby', r); - }), - this.panels.attr('role', 'tabpanel'), - this._addClass(this.panels, 'ui-tabs-panel', 'ui-widget-content'), - i && - (this._off(i.not(this.tabs)), - this._off(s.not(this.anchors)), - this._off(n.not(this.panels))); - }, - _getList: function() { - return this.tablist || this.element.find('ol, ul').eq(0); - }, - _createPanel: function(e) { - return t('
      ') - .attr('id', e) - .data('ui-tabs-destroy', !0); - }, - _setOptionDisabled: function(e) { - var i, s, n; - for ( - t.isArray(e) && - (e.length - ? e.length === this.anchors.length && (e = !0) - : (e = !1)), - n = 0; - (s = this.tabs[n]); - n++ - ) - (i = t(s)), - e === !0 || -1 !== t.inArray(n, e) - ? (i.attr('aria-disabled', 'true'), - this._addClass(i, null, 'ui-state-disabled')) - : (i.removeAttr('aria-disabled'), - this._removeClass(i, null, 'ui-state-disabled')); - (this.options.disabled = e), - this._toggleClass( - this.widget(), - this.widgetFullName + '-disabled', - null, - e === !0 - ); - }, - _setupEvents: function(e) { - var i = {}; - e && - t.each(e.split(' '), function(t, e) { - i[e] = '_eventHandler'; - }), - this._off(this.anchors.add(this.tabs).add(this.panels)), - this._on(!0, this.anchors, { - click: function(t) { - t.preventDefault(); - } - }), - this._on(this.anchors, i), - this._on(this.tabs, { keydown: '_tabKeydown' }), - this._on(this.panels, { keydown: '_panelKeydown' }), - this._focusable(this.tabs), - this._hoverable(this.tabs); - }, - _setupHeightStyle: function(e) { - var i, - s = this.element.parent(); - 'fill' === e - ? ((i = s.height()), - (i -= this.element.outerHeight() - this.element.height()), - this.element.siblings(':visible').each(function() { - var e = t(this), - s = e.css('position'); - 'absolute' !== s && 'fixed' !== s && (i -= e.outerHeight(!0)); - }), - this.element - .children() - .not(this.panels) - .each(function() { - i -= t(this).outerHeight(!0); - }), - this.panels - .each(function() { - t(this).height( - Math.max(0, i - t(this).innerHeight() + t(this).height()) - ); - }) - .css('overflow', 'auto')) - : 'auto' === e && - ((i = 0), - this.panels - .each(function() { - i = Math.max( - i, - t(this) - .height('') - .height() - ); - }) - .height(i)); - }, - _eventHandler: function(e) { - var i = this.options, - s = this.active, - n = t(e.currentTarget), - o = n.closest('li'), - a = o[0] === s[0], - r = a && i.collapsible, - h = r ? t() : this._getPanelForTab(o), - l = s.length ? this._getPanelForTab(s) : t(), - c = { oldTab: s, oldPanel: l, newTab: r ? t() : o, newPanel: h }; - e.preventDefault(), - o.hasClass('ui-state-disabled') || - o.hasClass('ui-tabs-loading') || - this.running || - (a && !i.collapsible) || - this._trigger('beforeActivate', e, c) === !1 || - ((i.active = r ? !1 : this.tabs.index(o)), - (this.active = a ? t() : o), - this.xhr && this.xhr.abort(), - l.length || - h.length || - t.error('jQuery UI Tabs: Mismatching fragment identifier.'), - h.length && this.load(this.tabs.index(o), e), - this._toggle(e, c)); - }, - _toggle: function(e, i) { - function s() { - (o.running = !1), o._trigger('activate', e, i); - } - function n() { - o._addClass( - i.newTab.closest('li'), - 'ui-tabs-active', - 'ui-state-active' - ), - a.length && o.options.show - ? o._show(a, o.options.show, s) - : (a.show(), s()); - } - var o = this, - a = i.newPanel, - r = i.oldPanel; - (this.running = !0), - r.length && this.options.hide - ? this._hide(r, this.options.hide, function() { - o._removeClass( - i.oldTab.closest('li'), - 'ui-tabs-active', - 'ui-state-active' - ), - n(); - }) - : (this._removeClass( - i.oldTab.closest('li'), - 'ui-tabs-active', - 'ui-state-active' - ), - r.hide(), - n()), - r.attr('aria-hidden', 'true'), - i.oldTab.attr({ 'aria-selected': 'false', 'aria-expanded': 'false' }), - a.length && r.length - ? i.oldTab.attr('tabIndex', -1) - : a.length && - this.tabs - .filter(function() { - return 0 === t(this).attr('tabIndex'); - }) - .attr('tabIndex', -1), - a.attr('aria-hidden', 'false'), - i.newTab.attr({ - 'aria-selected': 'true', - 'aria-expanded': 'true', - tabIndex: 0 - }); - }, - _activate: function(e) { - var i, - s = this._findActive(e); - s[0] !== this.active[0] && - (s.length || (s = this.active), - (i = s.find('.ui-tabs-anchor')[0]), - this._eventHandler({ - target: i, - currentTarget: i, - preventDefault: t.noop - })); - }, - _findActive: function(e) { - return e === !1 ? t() : this.tabs.eq(e); - }, - _getIndex: function(e) { - return ( - 'string' == typeof e && - (e = this.anchors.index( - this.anchors.filter("[href$='" + t.ui.escapeSelector(e) + "']") - )), - e - ); - }, - _destroy: function() { - this.xhr && this.xhr.abort(), - this.tablist.removeAttr('role').off(this.eventNamespace), - this.anchors.removeAttr('role tabIndex').removeUniqueId(), - this.tabs.add(this.panels).each(function() { - t.data(this, 'ui-tabs-destroy') - ? t(this).remove() - : t(this).removeAttr( - 'role tabIndex aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded' - ); - }), - this.tabs.each(function() { - var e = t(this), - i = e.data('ui-tabs-aria-controls'); - i - ? e.attr('aria-controls', i).removeData('ui-tabs-aria-controls') - : e.removeAttr('aria-controls'); - }), - this.panels.show(), - 'content' !== this.options.heightStyle && - this.panels.css('height', ''); - }, - enable: function(e) { - var i = this.options.disabled; - i !== !1 && - (void 0 === e - ? (i = !1) - : ((e = this._getIndex(e)), - (i = t.isArray(i) - ? t.map(i, function(t) { - return t !== e ? t : null; - }) - : t.map(this.tabs, function(t, i) { - return i !== e ? i : null; - }))), - this._setOptionDisabled(i)); - }, - disable: function(e) { - var i = this.options.disabled; - if (i !== !0) { - if (void 0 === e) i = !0; - else { - if (((e = this._getIndex(e)), -1 !== t.inArray(e, i))) return; - i = t.isArray(i) ? t.merge([e], i).sort() : [e]; - } - this._setOptionDisabled(i); - } - }, - load: function(e, i) { - e = this._getIndex(e); - var s = this, - n = this.tabs.eq(e), - o = n.find('.ui-tabs-anchor'), - a = this._getPanelForTab(n), - r = { tab: n, panel: a }, - h = function(t, e) { - 'abort' === e && s.panels.stop(!1, !0), - s._removeClass(n, 'ui-tabs-loading'), - a.removeAttr('aria-busy'), - t === s.xhr && delete s.xhr; - }; - this._isLocal(o[0]) || - ((this.xhr = t.ajax(this._ajaxSettings(o, i, r))), - this.xhr && - 'canceled' !== this.xhr.statusText && - (this._addClass(n, 'ui-tabs-loading'), - a.attr('aria-busy', 'true'), - this.xhr - .done(function(t, e, n) { - setTimeout(function() { - a.html(t), s._trigger('load', i, r), h(n, e); - }, 1); - }) - .fail(function(t, e) { - setTimeout(function() { - h(t, e); - }, 1); - }))); - }, - _ajaxSettings: function(e, i, s) { - var n = this; - return { - url: e.attr('href').replace(/#.*$/, ''), - beforeSend: function(e, o) { - return n._trigger( - 'beforeLoad', - i, - t.extend({ jqXHR: e, ajaxSettings: o }, s) - ); - } - }; - }, - _getPanelForTab: function(e) { - var i = t(e).attr('aria-controls'); - return this.element.find(this._sanitizeSelector('#' + i)); - } - }), - t.uiBackCompat !== !1 && - t.widget('ui.tabs', t.ui.tabs, { - _processTabs: function() { - this._superApply(arguments), this._addClass(this.tabs, 'ui-tab'); - } - }), - t.ui.tabs, - t.widget('ui.tooltip', { - version: '1.12.1', - options: { - classes: { 'ui-tooltip': 'ui-corner-all ui-widget-shadow' }, - content: function() { - var e = t(this).attr('title') || ''; - return t('') - .text(e) - .html(); - }, - hide: !0, - items: '[title]:not([disabled])', - position: { - my: 'left top+15', - at: 'left bottom', - collision: 'flipfit flip' - }, - show: !0, - track: !1, - close: null, - open: null - }, - _addDescribedBy: function(e, i) { - var s = (e.attr('aria-describedby') || '').split(/\s+/); - s.push(i), - e - .data('ui-tooltip-id', i) - .attr('aria-describedby', t.trim(s.join(' '))); - }, - _removeDescribedBy: function(e) { - var i = e.data('ui-tooltip-id'), - s = (e.attr('aria-describedby') || '').split(/\s+/), - n = t.inArray(i, s); - -1 !== n && s.splice(n, 1), - e.removeData('ui-tooltip-id'), - (s = t.trim(s.join(' '))), - s ? e.attr('aria-describedby', s) : e.removeAttr('aria-describedby'); - }, - _create: function() { - this._on({ mouseover: 'open', focusin: 'open' }), - (this.tooltips = {}), - (this.parents = {}), - (this.liveRegion = t('
      ') - .attr({ - role: 'log', - 'aria-live': 'assertive', - 'aria-relevant': 'additions' - }) - .appendTo(this.document[0].body)), - this._addClass(this.liveRegion, null, 'ui-helper-hidden-accessible'), - (this.disabledTitles = t([])); - }, - _setOption: function(e, i) { - var s = this; - this._super(e, i), - 'content' === e && - t.each(this.tooltips, function(t, e) { - s._updateContent(e.element); - }); - }, - _setOptionDisabled: function(t) { - this[t ? '_disable' : '_enable'](); - }, - _disable: function() { - var e = this; - t.each(this.tooltips, function(i, s) { - var n = t.Event('blur'); - (n.target = n.currentTarget = s.element[0]), e.close(n, !0); - }), - (this.disabledTitles = this.disabledTitles.add( - this.element - .find(this.options.items) - .addBack() - .filter(function() { - var e = t(this); - return e.is('[title]') - ? e - .data('ui-tooltip-title', e.attr('title')) - .removeAttr('title') - : void 0; - }) - )); - }, - _enable: function() { - this.disabledTitles.each(function() { - var e = t(this); - e.data('ui-tooltip-title') && - e.attr('title', e.data('ui-tooltip-title')); - }), - (this.disabledTitles = t([])); - }, - open: function(e) { - var i = this, - s = t(e ? e.target : this.element).closest(this.options.items); - s.length && - !s.data('ui-tooltip-id') && - (s.attr('title') && s.data('ui-tooltip-title', s.attr('title')), - s.data('ui-tooltip-open', !0), - e && - 'mouseover' === e.type && - s.parents().each(function() { - var e, - s = t(this); - s.data('ui-tooltip-open') && - ((e = t.Event('blur')), - (e.target = e.currentTarget = this), - i.close(e, !0)), - s.attr('title') && - (s.uniqueId(), - (i.parents[this.id] = { - element: this, - title: s.attr('title') - }), - s.attr('title', '')); - }), - this._registerCloseHandlers(e, s), - this._updateContent(s, e)); - }, - _updateContent: function(t, e) { - var i, - s = this.options.content, - n = this, - o = e ? e.type : null; - return 'string' == typeof s || s.nodeType || s.jquery - ? this._open(e, t, s) - : ((i = s.call(t[0], function(i) { - n._delay(function() { - t.data('ui-tooltip-open') && - (e && (e.type = o), this._open(e, t, i)); - }); - })), - i && this._open(e, t, i), - void 0); - }, - _open: function(e, i, s) { - function n(t) { - (l.of = t), a.is(':hidden') || a.position(l); - } - var o, - a, - r, - h, - l = t.extend({}, this.options.position); - if (s) { - if ((o = this._find(i))) - return o.tooltip.find('.ui-tooltip-content').html(s), void 0; - i.is('[title]') && - (e && 'mouseover' === e.type - ? i.attr('title', '') - : i.removeAttr('title')), - (o = this._tooltip(i)), - (a = o.tooltip), - this._addDescribedBy(i, a.attr('id')), - a.find('.ui-tooltip-content').html(s), - this.liveRegion.children().hide(), - (h = t('
      ').html(a.find('.ui-tooltip-content').html())), - h - .removeAttr('name') - .find('[name]') - .removeAttr('name'), - h - .removeAttr('id') - .find('[id]') - .removeAttr('id'), - h.appendTo(this.liveRegion), - this.options.track && e && /^mouse/.test(e.type) - ? (this._on(this.document, { mousemove: n }), n(e)) - : a.position(t.extend({ of: i }, this.options.position)), - a.hide(), - this._show(a, this.options.show), - this.options.track && - this.options.show && - this.options.show.delay && - (r = this.delayedShow = setInterval(function() { - a.is(':visible') && (n(l.of), clearInterval(r)); - }, t.fx.interval)), - this._trigger('open', e, { tooltip: a }); - } - }, - _registerCloseHandlers: function(e, i) { - var s = { - keyup: function(e) { - if (e.keyCode === t.ui.keyCode.ESCAPE) { - var s = t.Event(e); - (s.currentTarget = i[0]), this.close(s, !0); - } - } - }; - i[0] !== this.element[0] && - (s.remove = function() { - this._removeTooltip(this._find(i).tooltip); - }), - (e && 'mouseover' !== e.type) || (s.mouseleave = 'close'), - (e && 'focusin' !== e.type) || (s.focusout = 'close'), - this._on(!0, i, s); - }, - close: function(e) { - var i, - s = this, - n = t(e ? e.currentTarget : this.element), - o = this._find(n); - return o - ? ((i = o.tooltip), - o.closing || - (clearInterval(this.delayedShow), - n.data('ui-tooltip-title') && - !n.attr('title') && - n.attr('title', n.data('ui-tooltip-title')), - this._removeDescribedBy(n), - (o.hiding = !0), - i.stop(!0), - this._hide(i, this.options.hide, function() { - s._removeTooltip(t(this)); - }), - n.removeData('ui-tooltip-open'), - this._off(n, 'mouseleave focusout keyup'), - n[0] !== this.element[0] && this._off(n, 'remove'), - this._off(this.document, 'mousemove'), - e && - 'mouseleave' === e.type && - t.each(this.parents, function(e, i) { - t(i.element).attr('title', i.title), delete s.parents[e]; - }), - (o.closing = !0), - this._trigger('close', e, { tooltip: i }), - o.hiding || (o.closing = !1)), - void 0) - : (n.removeData('ui-tooltip-open'), void 0); - }, - _tooltip: function(e) { - var i = t('
      ').attr('role', 'tooltip'), - s = t('
      ').appendTo(i), - n = i.uniqueId().attr('id'); - return ( - this._addClass(s, 'ui-tooltip-content'), - this._addClass(i, 'ui-tooltip', 'ui-widget ui-widget-content'), - i.appendTo(this._appendTo(e)), - (this.tooltips[n] = { element: e, tooltip: i }) - ); - }, - _find: function(t) { - var e = t.data('ui-tooltip-id'); - return e ? this.tooltips[e] : null; - }, - _removeTooltip: function(t) { - t.remove(), delete this.tooltips[t.attr('id')]; - }, - _appendTo: function(t) { - var e = t.closest('.ui-front, dialog'); - return e.length || (e = this.document[0].body), e; - }, - _destroy: function() { - var e = this; - t.each(this.tooltips, function(i, s) { - var n = t.Event('blur'), - o = s.element; - (n.target = n.currentTarget = o[0]), - e.close(n, !0), - t('#' + i).remove(), - o.data('ui-tooltip-title') && - (o.attr('title') || o.attr('title', o.data('ui-tooltip-title')), - o.removeData('ui-tooltip-title')); - }), - this.liveRegion.remove(); - } - }), - t.uiBackCompat !== !1 && - t.widget('ui.tooltip', t.ui.tooltip, { - options: { tooltipClass: null }, - _tooltip: function() { - var t = this._superApply(arguments); - return ( - this.options.tooltipClass && - t.tooltip.addClass(this.options.tooltipClass), - t - ); - } - }), - t.ui.tooltip; -}); From de033d20e006408e7b01866b3e71d411424ee184 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Tue, 18 Jul 2023 11:27:25 +0200 Subject: [PATCH 22/46] Fixes redirection problem to inventory data, stats and configuration in an agent (#5685) Fix redirect to inventory data, stats and configuration --- .../common/welcome/agents-welcome.js | 51 +++---------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/plugins/main/public/components/common/welcome/agents-welcome.js b/plugins/main/public/components/common/welcome/agents-welcome.js index 20c1ded43c..4d40211286 100644 --- a/plugins/main/public/components/common/welcome/agents-welcome.js +++ b/plugins/main/public/components/common/welcome/agents-welcome.js @@ -13,8 +13,7 @@ */ import React, { Component, Fragment } from 'react'; import { - EuiCard, - EuiIcon, + EuiLink, EuiPanel, EuiFlexItem, EuiFlexGroup, @@ -22,7 +21,6 @@ import { EuiText, EuiFlexGrid, EuiButtonEmpty, - EuiTitle, EuiPage, EuiButton, EuiPopover, @@ -39,10 +37,8 @@ import { RequirementVis, } from './components'; import { AgentInfo } from './agents-info'; -import { WAZUH_MODULES } from '../../../../common/wazuh-modules'; import store from '../../../redux/store'; import { updateGlobalBreadcrumb } from '../../../redux/actions/globalBreadcrumbActions'; -import { ActionAgents } from '../../../react-services/action-agents'; import WzReduxProvider from '../../../redux/wz-redux-provider'; import MenuAgent from './components/menu-agent'; import './welcome.scss'; @@ -53,7 +49,6 @@ import { AppState } from '../../../react-services/app-state'; import { FilterHandler } from '../../../utils/filter-handler'; import { TabVisualizations } from '../../../factories/tab-visualizations'; import { updateCurrentAgentData } from '../../../redux/actions/appStateActions'; -import WzTextWithTooltipIfTruncated from '../wz-text-with-tooltip-if-truncated'; import { getAngularModule } from '../../../kibana-services'; import { hasAgentSupportModule } from '../../../react-services/wz-agents'; import { withErrorBoundary, withReduxProvider } from '../hocs'; @@ -296,6 +291,7 @@ export const AgentsWelcome = compose( } renderTitle() { + const notNeedStatus = true; return ( @@ -378,43 +374,6 @@ export const AgentsWelcome = compose( this.setState({ datePicker: { from, to } }); }; - getOptions() { - return [ - { value: 'pci', text: 'PCI DSS' }, - { value: 'gdpr', text: 'GDPR' }, - { value: 'nist', text: 'NIST 800-53' }, - { value: 'hipaa', text: 'HIPAA' }, - { value: 'gpg13', text: 'GPG13' }, - { value: 'tsc', text: 'TSC' }, - ]; - } - - setSelectValue(e) { - this.setState({ selectedRequirement: e.target.value }); - } - - getRequirementVis() { - if (this.state.selectedRequirement === 'pci') { - return 'Wazuh-App-Agents-Welcome-Top-PCI'; - } - if (this.state.selectedRequirement === 'gdpr') { - return 'Wazuh-App-Agents-Welcome-Top-GDPR'; - } - if (this.state.selectedRequirement === 'hipaa') { - return 'Wazuh-App-Agents-Welcome-Top-HIPAA'; - } - if (this.state.selectedRequirement === 'nist') { - return 'Wazuh-App-Agents-Welcome-Top-NIST-800-53'; - } - if (this.state.selectedRequirement === 'gpg13') { - return 'Wazuh-App-Agents-Welcome-Top-GPG-13'; - } - if (this.state.selectedRequirement === 'tsc') { - return 'Wazuh-App-Agents-Welcome-Top-TSC'; - } - return 'Wazuh-App-Agents-Welcome-Top-PCI'; - } - renderMitrePanel() { return ( @@ -530,14 +489,16 @@ export const AgentsWelcome = compose( The agent has been registered but has not yet connected to the manager.

      -
      Checking connection with the Wazuh server - + } actions={ From 657fecbb138258cefbc67e4539844e2624ba3e8f Mon Sep 17 00:00:00 2001 From: Antonio <34042064+Desvelao@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:53:51 +0200 Subject: [PATCH 23/46] Fix conflicts in branch synchronization (#5708) * fix: fix conflicts * changelog: remove entry --- CHANGELOG.md | 1 - .../components/common/welcome/agents-info.js | 3 +- .../agent/components/agents-table.js | 18 +- .../public/services/ipv6-services.test.js | 21 - plugins/main/public/services/ipv6-services.ts | 21 - plugins/main/scripts/tag.py | 2 +- plugins/main/yarn.lock | 1263 +++++++++++++++-- 7 files changed, 1171 insertions(+), 158 deletions(-) delete mode 100644 plugins/main/public/services/ipv6-services.test.js delete mode 100644 plugins/main/public/services/ipv6-services.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ec9d90f88e..620d6677d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,6 @@ All notable changes to the Wazuh app project will be documented in this file. ### Fixed - Fixed trailing hyphen character for OS value in the list of agents [#4828](https://github.com/wazuh/wazuh-kibana-app/pull/4828) -- Fixed an issue that caused incorrect visualization of IPv6 addresses ([#4909](https://github.com/wazuh/wazuh-kibana-app/pull/4909)). - Fixed several typos in the code, by @jctello [#4911](https://github.com/wazuh/wazuh-kibana-app/pull/4911) - Fixed the display of more than one protocol in the Global configuration section [#4917](https://github.com/wazuh/wazuh-kibana-app/pull/4917) - Handling endpoint response was done when there is no data to show [#4918](https://github.com/wazuh/wazuh-kibana-app/pull/4918) diff --git a/plugins/main/public/components/common/welcome/agents-info.js b/plugins/main/public/components/common/welcome/agents-info.js index 00ab5b8f81..20fa415a46 100644 --- a/plugins/main/public/components/common/welcome/agents-info.js +++ b/plugins/main/public/components/common/welcome/agents-info.js @@ -19,7 +19,6 @@ import WzTextWithTooltipIfTruncated from '../wz-text-with-tooltip-if-truncated'; import { WzStat } from '../../wz-stat'; import { GroupTruncate } from '../util/agent-group-truncate'; import { AgentStatus } from '../../agents/agent_status'; -import { compressIPv6 } from '../../../services/ipv6-services'; export class AgentInfo extends Component { constructor(props) { @@ -181,7 +180,7 @@ export class AgentInfo extends Component { { title: agent.ip, description: 'IP address', - style: { minwidth: 150 }, + style: {}, }, { title: agent.version, diff --git a/plugins/main/public/controllers/agent/components/agents-table.js b/plugins/main/public/controllers/agent/components/agents-table.js index 212bd69505..09141ac9d6 100644 --- a/plugins/main/public/controllers/agent/components/agents-table.js +++ b/plugins/main/public/controllers/agent/components/agents-table.js @@ -49,7 +49,6 @@ import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/ import { getErrorOrchestrator } from '../../../react-services/common-services'; import { AgentStatus } from '../../../components/agents/agent_status'; import { AgentSynced } from '../../../components/agents/agent-synced'; -import { compressIPv6 } from '../../../services/ipv6-services'; export const AgentsTable = withErrorBoundary( class AgentsTable extends Component { @@ -310,9 +309,6 @@ export const AgentsTable = withErrorBoundary( } formatAgent(agent) { - const checkField = field => { - return field !== undefined ? field : '-'; - }; const agentVersion = agent.version !== undefined ? agent.version.split(' ')[1] : '-'; const node_name = @@ -323,7 +319,7 @@ export const AgentsTable = withErrorBoundary( return { id: agent.id, name: agent.name, - ip: compressIPv6(agent.ip), + ip: agent.ip, status: agent.status, group_config_status: agent.group_config_status, group: agent?.group || '-', @@ -384,11 +380,8 @@ export const AgentsTable = withErrorBoundary( } addIconPlatformRender(agent) { - let icon = false; - const checkField = field => { - return field !== undefined ? field : '-'; - }; - const os = (agent || {}).os; + let icon = ''; + const os = agent?.os || {}; if ((os?.uname || '').includes('Linux')) { icon = 'linux'; @@ -397,8 +390,7 @@ export const AgentsTable = withErrorBoundary( } else if (os?.platform === 'darwin') { icon = 'apple'; } - const os_name = - checkField(agent?.os?.name) + ' ' + checkField(agent?.os?.version); + const os_name = `${agent?.os?.name || ''} ${agent?.os?.version || ''}`; return ( @@ -408,7 +400,7 @@ export const AgentsTable = withErrorBoundary( aria-hidden='true' > {' '} - {os_name === '- -' ? '-' : os_name} + {os_name.trim() || '-'} ); } diff --git a/plugins/main/public/services/ipv6-services.test.js b/plugins/main/public/services/ipv6-services.test.js deleted file mode 100644 index 9887751b65..0000000000 --- a/plugins/main/public/services/ipv6-services.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import { compressIPv6 } from "./ipv6-services"; - -describe('[settings] Input validation', () => { - it.each` - value | expectedValidation - ${'192.168.100.1'} | ${'192.168.100.1'} - ${'FE80:1234:2223:A000:2202:B3FF:FE1E:8329'} | ${'FE80:1234:2223:A000:2202:B3FF:FE1E:8329'} - ${'FE80:0034:0223:A000:0002:B3FF:0000:8329'} | ${'FE80:34:223:A000:2:B3FF:0:8329'} - ${'FE80:1234:2223:0000:0000:B3FF:FE1E:8329'} | ${'FE80:1234:2223::B3FF:FE1E:8329'} - ${'FE80:0000:0000:A000:0000:0000:0000:8329'} | ${'FE80:0:0:A000::8329'} - ${'FE80:0000:0000:0000:2202:00FF:0E1E:8329'} | ${'FE80::2202:FF:E1E:8329'} - ${'0000:0000:0000:0000:2202:00FF:0E1E:8329'} | ${'::2202:FF:E1E:8329'} - ${'2202:00FF:0E1E:8329:2202:0000:0000:0000'} | ${'2202:FF:E1E:8329:2202::'} - ${'0000:0000:0000:0000:0000:0000:0000:0000'} | ${'::'} - ${undefined} | ${undefined} - ${234} | ${234} - `('$value | $expectedValidation', ({ value, expectedValidation }) => { - expect( - compressIPv6(value)).toBe(expectedValidation); - }); -}); diff --git a/plugins/main/public/services/ipv6-services.ts b/plugins/main/public/services/ipv6-services.ts deleted file mode 100644 index ac7fe331c2..0000000000 --- a/plugins/main/public/services/ipv6-services.ts +++ /dev/null @@ -1,21 +0,0 @@ -export function compressIPv6 (ip: string) { - if (typeof (ip) !== 'string') { - return ip; - } - if (ip?.split(':').length !== 8) { - return ip; - } - - let output = ip.split(':').map(terms => terms.replace(/\b0+/g, '') || '0').join(':'); - const zeros = Array.from(output.matchAll(/\b:?(?:0+:?){2,}/g)); - if (zeros.length > 0) { - let max = ''; - zeros.forEach(item => { - if (item[0].replace(/:/g, '').length > max.replace(/:/g, '').length) { - max = item[0]; - } - }); - output = output.replace(max, '::'); - } - return output; -} diff --git a/plugins/main/scripts/tag.py b/plugins/main/scripts/tag.py index 21ab704414..68f4ce6747 100644 --- a/plugins/main/scripts/tag.py +++ b/plugins/main/scripts/tag.py @@ -17,7 +17,7 @@ # Wazuh version: major.minor.patch version = '4.6.0' # App's revision number (previous rev + 1) -revision = '02' +revision = '01' # One of 'pre-alpha', 'alpha', 'beta', 'release-candidate', 'stable' stage = 'stable' # Tag suffix. Usually set to stage + stage iteration. diff --git a/plugins/main/yarn.lock b/plugins/main/yarn.lock index d4d5bca1f3..ca82f2acbe 100644 --- a/plugins/main/yarn.lock +++ b/plugins/main/yarn.lock @@ -28,6 +28,14 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/runtime-corejs3@^7.20.13", "@babel/runtime-corejs3@^7.20.7": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.22.6.tgz#e8e625eb3db29491e0326b3aeb9929c43b270ae4" + integrity sha512-M+37LLIRBTEVjktoJjbw4KVhupF0U/3PYUCbBwgAd9k17hoKhRu1n935QiG7Tuxv0LJOMrb2vuKEeYUlv0iyiw== + dependencies: + core-js-pure "^3.30.2" + regenerator-runtime "^0.13.11" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -154,6 +162,313 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgr/utils@^2.3.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" + integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== + dependencies: + cross-spawn "^7.0.3" + fast-glob "^3.3.0" + is-glob "^4.0.3" + open "^9.1.0" + picocolors "^1.0.0" + tslib "^2.6.0" + +"@swagger-api/apidom-ast@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ast/-/apidom-ast-0.73.0.tgz#9d0a919f6ba06b3968686ced97fd375f819fa324" + integrity sha512-M7FCOmV9ycJZgJckFYAHUox6mjjgXWyuNoiblKkEWIprx+qVa7a/9PQQ5B1RImWTi/8Rjh2ja83vk8kzQq5qGw== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + unraw "^2.0.1" + +"@swagger-api/apidom-core@>=0.71.0 <1.0.0", "@swagger-api/apidom-core@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-core/-/apidom-core-0.73.0.tgz#ae6cf11773907916e5b04baeafad04636c21efe3" + integrity sha512-CaFUoniCEe1vLLxPIqHot8G7h6Z8YTHtdji2qDiESvJ+rk2qJGwDYoX9ngAokVsj0z9p/8wloC8CMCXhzUMosg== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-ast" "^0.73.0" + "@types/ramda" "~0.29.3" + minim "~0.23.8" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + short-unique-id "^4.4.4" + stampit "^4.3.2" + +"@swagger-api/apidom-json-pointer@>=0.71.0 <1.0.0", "@swagger-api/apidom-json-pointer@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-0.73.0.tgz#66d418742a917cb0b900fd9205b934fe98071370" + integrity sha512-Izgg8HfPBtbwODDZBmR+sejwC0IQjxKhRBgsUOzxbRhcv2tfwFWcgTAwFhC2EGXk8UUX1VggWLqGZ6Psp+wOvA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-ns-api-design-systems@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-0.73.0.tgz#a16da41859be11fc6cae982f0dd09ac51e5f1454" + integrity sha512-Bam1o2Qx8gTRL8m8eVUO92JjowJ8uO7TVAXes3O6XHQR60bf9lqVrZ1W0f9sq4hKEXEm19cixNxXg1XweB0COA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-1" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-ns-asyncapi-2@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-0.73.0.tgz#6f67c53dd90382a4149c5653429e48acedeffce1" + integrity sha512-tqSLrEekI9a/ezpVT6HUQCpbZ2iKPYqGL906OrSOIdWE1wIsXFoN9mBU6UMDYk8oy61DbUyw82EOqUuCP3oGEA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-json-schema-draft-7" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-ns-json-schema-draft-4@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-0.73.0.tgz#7113e3e64d7186b05e4d2c6e7f5225be0f55b89a" + integrity sha512-B6nKIU3fIM3a4BeWvaSPCGc5NCxLs/TPA2PX9QsHtjDml1xeBq1HgLoSyfxf0nMB/YgrK4UsZZKu5jkmoAeatw== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-ast" "^0.73.0" + "@swagger-api/apidom-core" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-ns-json-schema-draft-6@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-0.73.0.tgz#900bcf84da64761f3945fbf3f41de2663261e28b" + integrity sha512-p+INp5k2k40ED2xAKf68tPefnQQLRbzUMdP5q7b614UM/Q4LY/m+T5af4qDWF+E++/ses7GB6ndyCLk5R1UKuA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-json-schema-draft-4" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-ns-json-schema-draft-7@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-0.73.0.tgz#6e885b0188d7d5146d12fb118a5c657b2b3d4afb" + integrity sha512-N7cL5qSFc7gNXYebIjOeoRtaaknRcShVOet3HJ1ZLTcGfoL2Y1c5Z6P4biFBhVkzlpS8d6MmcbwEuexFEQqPNw== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-json-schema-draft-6" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-ns-openapi-3-0@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-0.73.0.tgz#ca9348a0caab1b99cf5c5826c06ee179ca048a75" + integrity sha512-oyG+tW/+H0H3+L+JBZkb7C6OYmNTqpLIykJ8an9bzxOr8XkC6+qj44A9ZcM4V1LXCYB0nZxsUFeoqKk62MkwwA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-json-schema-draft-4" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-ns-openapi-3-1@>=0.71.0 <1.0.0", "@swagger-api/apidom-ns-openapi-3-1@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-0.73.0.tgz#2c66af6429d2900661e6b4eb39b7f5e78edcdb05" + integrity sha512-X5zbGTERlDaOEZKkg+aVtzjNthZe+cx2iOvJRZpyLWZvIyAbKmc5LJw+rq6Lv/9s1GvcJKbpYxGUE6eCU90Qag== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-ast" "^0.73.0" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-0" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + +"@swagger-api/apidom-parser-adapter-api-design-systems-json@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-0.73.0.tgz#876b9d59f68e1550f153ab6db3d65c2cd25157cd" + integrity sha512-7X65FZ6z++meIqaiMNRSPMLXsTuNRkm0TFCnmr5P46V8oRrh1/7c1lOk0wdBoVfL86WayVODZaLCsogl+YnI1A== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-api-design-systems" "^0.73.0" + "@swagger-api/apidom-parser-adapter-json" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-0.73.0.tgz#d588434f2a984ff057ea094a8aaeae374cb20c0f" + integrity sha512-SVZrVP7enfmi7vPIqzkamWZLdQBffVzT9JJ31kQHgrXX2AXlXUKEnX3uATYKnphZy1gUpg6WEga8GSQlaliTWA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-api-design-systems" "^0.73.0" + "@swagger-api/apidom-parser-adapter-yaml-1-2" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-asyncapi-json-2@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-0.73.0.tgz#bc8ad0f1d4e852180c8dd59e23120ac38f32f120" + integrity sha512-gtKzNg2i4ZBD8dZlosgZh36GY5LTYKYSUmarmicaTufazICziM636SytrO0awOgmN1aNvHOvH4O7H1dNgTy8PQ== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-asyncapi-2" "^0.73.0" + "@swagger-api/apidom-parser-adapter-json" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-0.73.0.tgz#d685d585da4c1dd438a36892d5608661044c008d" + integrity sha512-cPd1M4xg9SPLZjNa2puOZc1e5CLM8Evf7/Buxub1QM+/mCrmK7OgDojwrQCzsvit5ttIdifiR0ik12VobshUcg== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-asyncapi-2" "^0.73.0" + "@swagger-api/apidom-parser-adapter-yaml-1-2" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-json@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-0.73.0.tgz#e282418ff08c11d057d2b36a69d7ea964826c576" + integrity sha512-dV8pxyxnZvI3Rh6DL30BrHcZhGUBFI7HXsfR3tf/uqkwigUpu5qVGEEAHZwaZHJ3rOxCeVCBFco7uyaO2gXxjA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-ast" "^0.73.0" + "@swagger-api/apidom-core" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + tree-sitter "=0.20.4" + tree-sitter-json "=0.20.0" + web-tree-sitter "=0.20.3" + +"@swagger-api/apidom-parser-adapter-openapi-json-3-0@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-0.73.0.tgz#f4e8a0a1a02aaed613c7feb444948ee128cc8b43" + integrity sha512-SgNa+FEsqOImxMMGTRj8fLgLc6h2Jgho04ut6o5VOJVl0OIQRdG39/AbiODEXWCjS3SiyY62p0BoO+oM5TRteA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-0" "^0.73.0" + "@swagger-api/apidom-parser-adapter-json" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-openapi-json-3-1@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-0.73.0.tgz#98e9f37a8bc5a2bea319a12a99f3003fd875175f" + integrity sha512-8T0CAwEczk1YepHyNA9Y6Y20/MD9G8le7OcMvmgA/KITAmPY1RJyGeiiH+LEUmEQeOzC/rsIFG7YAwZsrPYKpg== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-1" "^0.73.0" + "@swagger-api/apidom-parser-adapter-json" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-0.73.0.tgz#0b7245e3078eaeb8e6f3ba05ac002fc20a2b093b" + integrity sha512-DNK1Aa2vU5EMtladdmilM/ZfWNGQH1BERHP2R0ISlukHSlBuTEjybYvfHZXdx+f1AyGrTT9kXGthOa8T/puKiA== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-0" "^0.73.0" + "@swagger-api/apidom-parser-adapter-yaml-1-2" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-0.73.0.tgz#77f6e05796cb28b8375cfd228e87c298bc128ab6" + integrity sha512-7J/j3UZc47jaNl/39wUsPK2Ggtrdn/Kx0zzdk+dggEW8VBc16lmF3QM2vzGB/4OAM1CyIiurfEVXSZ099Z1F5Q== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-1" "^0.73.0" + "@swagger-api/apidom-parser-adapter-yaml-1-2" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + +"@swagger-api/apidom-parser-adapter-yaml-1-2@^0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-0.73.0.tgz#d48edf5827d875591cd8ce787a2ce439d349b837" + integrity sha512-6arD3UZEhwdJnaXiL71vLzVrJHhn9z6bJluNa3mlDBASiML4+fIgMj128vHo3j7rB4bU1ye623+esMX39QLNfQ== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-ast" "^0.73.0" + "@swagger-api/apidom-core" "^0.73.0" + "@types/ramda" "~0.29.3" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + tree-sitter "=0.20.4" + tree-sitter-yaml "=0.5.0" + web-tree-sitter "=0.20.3" + +"@swagger-api/apidom-reference@>=0.71.1 <1.0.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@swagger-api/apidom-reference/-/apidom-reference-0.73.0.tgz#604b6ff74bce8142448a0681c27ce48fb06e6290" + integrity sha512-M4h83ZIfdMBeGWt39aWWA/8h/+qLY40FyU0Iow0Bc4wY/Rlennj17d/hIY8zpNto/QtkBXYJkqLjAKlDdMq07A== + dependencies: + "@babel/runtime-corejs3" "^7.20.7" + "@swagger-api/apidom-core" "^0.73.0" + "@types/ramda" "~0.29.3" + axios "^1.4.0" + minimatch "^7.4.3" + process "^0.11.10" + ramda "~0.29.0" + ramda-adjunct "^4.0.0" + stampit "^4.3.2" + optionalDependencies: + "@swagger-api/apidom-json-pointer" "^0.73.0" + "@swagger-api/apidom-ns-asyncapi-2" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-0" "^0.73.0" + "@swagger-api/apidom-ns-openapi-3-1" "^0.73.0" + "@swagger-api/apidom-parser-adapter-api-design-systems-json" "^0.73.0" + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml" "^0.73.0" + "@swagger-api/apidom-parser-adapter-asyncapi-json-2" "^0.73.0" + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2" "^0.73.0" + "@swagger-api/apidom-parser-adapter-json" "^0.73.0" + "@swagger-api/apidom-parser-adapter-openapi-json-3-0" "^0.73.0" + "@swagger-api/apidom-parser-adapter-openapi-json-3-1" "^0.73.0" + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0" "^0.73.0" + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1" "^0.73.0" + "@swagger-api/apidom-parser-adapter-yaml-1-2" "^0.73.0" + "@types/cookie@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" @@ -189,6 +504,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/ramda@~0.29.3": + version "0.29.3" + resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.29.3.tgz#6e4d4066df900a3456cf402bcef9b78b6990a754" + integrity sha512-Yh/RHkjN0ru6LVhSQtTkCRo6HXkfL9trot/2elzM/yXLJmbLm2v6kJc8yftTnwv1zvUob6TEtqI2cYjdqG3U0Q== + dependencies: + types-ramda "^0.29.4" + "@types/react@*": version "18.2.14" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" @@ -346,15 +668,15 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== -angular-animate@1.7.8: - version "1.7.8" - resolved "https://registry.yarnpkg.com/angular-animate/-/angular-animate-1.7.8.tgz#c95f237efe7ecfe0e6003adb5e2c7ef0e5a2b9d4" - integrity sha512-bINtzizq7TbJzfVrDpwLfTxVl0Qd7fRNWFb5jKYI1vaFZobQNX/QONXlLow6ySsDbZ6eLECycB7mvWtfh1YYaw== +angular-animate@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/angular-animate/-/angular-animate-1.8.3.tgz#f88db37325de256f9144d1242ce3158134a9d72a" + integrity sha512-/LtTKvy5sD6MZbV0v+nHgOIpnFF0mrUp+j5WIxVprVhcrJriYpuCZf4S7Owj1o76De/J0eRzANUozNJ6hVepnQ== -angular-material@1.1.18: - version "1.1.18" - resolved "https://registry.yarnpkg.com/angular-material/-/angular-material-1.1.18.tgz#91976b9df06a66e6627c6bf4ce074c10daa3d998" - integrity sha512-a+9Jzg4WF10G3vMbLCp5LSbmroeEbEYvzQoYVpWcXIgOUmnuOjxsNDPL73uNWOE97wOtEcFWxKF2wcP8zgixbA== +angular-material@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/angular-material/-/angular-material-1.2.5.tgz#6fb3fbf3622d443e4449aaf237d692ad04623a23" + integrity sha512-bTTDV0vszpfms1tAMzhLntxBiNMCk/I3Mx/vhbtfhijJILODjpDBfWah0nvWrniFIcxMLcsb1tcPri13hZEaew== ansi-regex@^5.0.1: version "5.0.1" @@ -471,17 +793,24 @@ async@^3.2.3: resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== +axios@^1.3.4, axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== dependencies: - follow-redirects "^1.14.0" + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" balanced-match@^1.0.0: version "1.0.2" @@ -493,11 +822,32 @@ base64-js@1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== -base64-js@^1.1.2, base64-js@^1.3.0: +base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -506,6 +856,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -556,11 +913,26 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -596,6 +968,11 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + classnames@^2.2.5: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" @@ -659,6 +1036,13 @@ colorspace@1.1.x: color "^3.1.3" text-hex "1.0.x" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^2.12.1, commander@^2.15.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -689,6 +1073,16 @@ cookie@^0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +core-js-pure@^3.30.2: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.31.1.tgz#73d154958881873bc19381df80bddb20c8d0cdb5" + integrity sha512-w+C62kvWti0EPs4KPMCMVv9DriHSXfQOCQ94bGGBiEW5rrbtt/Rz8n5Krhfw9cpFyzXBjf3DB3QnPdEzGDY4Fw== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -702,7 +1096,14 @@ create-react-class@^15.5.1: loose-envify "^1.3.1" object-assign "^4.1.1" -cross-spawn@^7.0.2: +cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -753,6 +1154,13 @@ debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + deep-equal@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -765,11 +1173,44 @@ deep-equal@^1.0.0: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deepmerge@~4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -778,6 +1219,16 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +detect-libc@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + dfa@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" @@ -821,6 +1272,21 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^5.12.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.21.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" @@ -983,16 +1449,19 @@ eslint-import-resolver-node@^0.3.7: is-core-module "^2.11.0" resolve "^1.22.1" -eslint-import-resolver-typescript@2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" - integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== +eslint-import-resolver-typescript@3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" + integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== dependencies: debug "^4.3.4" - glob "^7.2.0" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + get-tsconfig "^4.5.0" + globby "^13.1.3" + is-core-module "^2.11.0" is-glob "^4.0.3" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" + synckit "^0.8.5" eslint-module-utils@^2.7.4: version "2.8.0" @@ -1013,10 +1482,10 @@ eslint-plugin-cypress@^2.12.1: dependencies: globals "^11.12.0" -eslint-plugin-filenames-simple@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-filenames-simple/-/eslint-plugin-filenames-simple-0.7.0.tgz#cff3c48de89ff543ef8f724dde135daac7ae5714" - integrity sha512-CbiYl+XJtVI+JwIf573c9tENDYwngX7iYgPVJn6LExlXkdPhLKgBa4Rcht/lNBdgT1C5k3c+6eK7NePxs6Kakg== +eslint-plugin-filenames-simple@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-filenames-simple/-/eslint-plugin-filenames-simple-0.8.0.tgz#71fb9fc975f5e12835bf9b58fa11a06ad0933432" + integrity sha512-8+uBzNBE5gSUMQv7bmMBiOD26eKzD4/5flPtD5Vl3dzZLXotSwXK3W7ZZqKQfU0Qyoborh+LqbN76EfmbBcU8A== dependencies: pluralize "^8.0.0" @@ -1074,6 +1543,14 @@ eslint-plugin-react@^7.31.8: semver "^6.3.0" string.prototype.matchall "^4.0.8" +eslint-scope@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1090,6 +1567,11 @@ eslint-scope@^7.2.0: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-visitor-keys@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" @@ -1166,7 +1648,7 @@ esquery@^1.4.2: dependencies: estraverse "^5.1.0" -esrecurse@^4.3.0: +esrecurse@^4.1.0, esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== @@ -1211,6 +1693,41 @@ event-emitter@^0.3.5, event-emitter@~0.3.5: d "1" es5-ext "~0.10.14" +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + ext@^1.1.2: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -1228,7 +1745,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9: +fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== @@ -1239,6 +1756,11 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-json-patch@^3.0.0-1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947" + integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1301,7 +1823,7 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.14.0: +follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -1313,6 +1835,33 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data-encoder@^1.4.3: + version "1.9.0" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.9.0.tgz#fd18d316b1ec830d2a8be8ad86c1cf0317320b34" + integrity sha512-rahaRMkN8P8d/tgK/BLPX+WBVM27NbvdXBxqQujBtkDAIFspaRqN7Od7lfdGQA6KAD+f82fYCLBq1ipvcu8qLw== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formdata-node@^4.0.0: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1353,6 +1902,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -1361,6 +1915,18 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-tsconfig@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.6.2.tgz#831879a5e6c2aa24fe79b60340e2233a1e0f472e" + integrity sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg== + dependencies: + resolve-pkg-maps "^1.0.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1375,7 +1941,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.1, glob@^7.1.3, glob@^7.2.0: +glob@^7.1.1, glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1418,6 +1984,17 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^13.1.3: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -1425,6 +2002,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -1483,12 +2065,15 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: dependencies: react-is "^16.7.0" -iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== iconv-lite@^0.6.3: version "0.6.3" @@ -1497,7 +2082,12 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ignore@^5.2.0: +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -1523,15 +2113,20 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -install@^0.10.1: - version "0.10.4" - resolved "https://registry.yarnpkg.com/install/-/install-0.10.4.tgz#9cb09115768b93a582d1450a6ba3f275975b49aa" - integrity sha512-+IRyOastuPmLVx9zlVXJoKErSqz1Ma5at9A7S8yfsj3W+Kg95faPoh3bPDtMrZ/grz4PRmXzrswmlzfLlYyLOw== +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +install@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/install/-/install-0.13.0.tgz#6af6e9da9dd0987de2ab420f78e60d9c17260776" + integrity sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA== internal-slot@^1.0.3, internal-slot@^1.0.5: version "1.0.5" @@ -1603,6 +2198,16 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1615,6 +2220,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -1637,6 +2249,11 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-regex@^1.0.4, is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -1657,6 +2274,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -1689,6 +2311,13 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -1719,12 +2348,12 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -js2xmlparser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733" - integrity sha512-CSOkdn0/GhRFwxnipmhXfqJ+FG6+wkWBi46kKSsPx6+j65176ZiQcrCYpg6K8x3iLbO4k3zScBnZ7I/L80dAtw== +js2xmlparser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-5.0.0.tgz#2c969c136762b567402a605f3931eb2e98bce1b9" + integrity sha512-ckXs0Fzd6icWurbeAXuqo+3Mhq2m8pOPygsQjTPh8K5UWgKaUgDSHrdDxAfexmT11xvBKOQ6sgYwPkYc5RW/bg== dependencies: - xmlcreate "^1.0.1" + xmlcreate "^2.0.4" json-schema-traverse@^0.4.1: version "0.4.1" @@ -1767,10 +2396,10 @@ jsonparse@^1.3.1: object.assign "^4.1.4" object.values "^1.1.6" -jwt-decode@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79" - integrity sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ== +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== kuler@^2.0.0: version "2.0.0" @@ -1830,7 +2459,12 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg== -logform@^2.3.2: +lodash@^4.15.0, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +logform@^2.3.2, logform@^2.4.0: version "2.5.1" resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== @@ -1868,10 +2502,10 @@ magic-string@0.25.1: dependencies: sourcemap-codec "^1.4.1" -markdown-it-link-attributes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/markdown-it-link-attributes/-/markdown-it-link-attributes-3.0.0.tgz#12d6f403102ac22695ee2617bec109ee79426e45" - integrity sha512-B34ySxVeo6MuEGSPCWyIYryuXINOvngNZL87Mp7YYfKIf6DcD837+lXA8mo6EBbauKsnGz22ZH0zsbOiQRWTNg== +markdown-it-link-attributes@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz#25751f2cf74fd91f0a35ba7b3247fa45f2056d88" + integrity sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ== md5@^2.3.0: version "2.3.0" @@ -1889,6 +2523,11 @@ merge-source-map@1.0.4: dependencies: source-map "^0.5.6" +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1902,6 +2541,40 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minim@~0.23.8: + version "0.23.8" + resolved "https://registry.yarnpkg.com/minim/-/minim-0.23.8.tgz#a529837afe1654f119dfb68ce7487dd8d4866b9c" + integrity sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww== + dependencies: + lodash "^4.15.0" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1909,12 +2582,24 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.6: +minimatch@^7.4.3: + version "7.4.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" + integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mkdirp@^0.5.1: +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -1940,6 +2625,16 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nan@^2.14.0, nan@^2.14.1, nan@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -1950,13 +2645,13 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -needle@^2.0.1: - version "2.9.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" - integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== +needle@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-3.2.0.tgz#07d240ebcabfd65c76c03afae7f6defe6469df44" + integrity sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ== dependencies: debug "^3.2.6" - iconv-lite "^0.4.4" + iconv-lite "^0.6.3" sax "^1.2.4" next-tick@^1.1.0: @@ -1964,11 +2659,44 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +node-abi@^3.3.0: + version "3.45.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.45.0.tgz#f568f163a3bfca5aacfce1fbeee1fa2cc98441f5" + integrity sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ== + dependencies: + semver "^7.3.5" + node-cron@^1.1.2: version "1.2.1" resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-1.2.1.tgz#8c90bc5dc723a56289b0786655ab4a1c4cb60368" integrity sha512-lgci/ub6KWL6SUnKOIiMkhfjmUk3jvEuO/Ypa2/CGSXiC8z4x9EkwMx7Dcu7Dt4LktcfOl8/WcLT2x7gInBa7g== +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^2.6.12: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== + dependencies: + whatwg-url "^5.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2037,7 +2765,7 @@ object.values@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -2051,6 +2779,30 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^2.2.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -2111,11 +2863,16 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -2136,6 +2893,11 @@ pdfmake@0.2.7: iconv-lite "^0.6.3" xmldoc "^1.1.2" +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -2151,6 +2913,24 @@ png-js@^1.0.0: resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -2178,6 +2958,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + prop-types@^15.5.4, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -2187,11 +2972,36 @@ prop-types@^15.5.4, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +qs@^6.10.2, qs@^6.11.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + querystring-browser@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/querystring-browser/-/querystring-browser-1.0.4.tgz#f2e35881840a819bc7b1bf597faf0979e6622dc6" @@ -2211,6 +3021,26 @@ quote-stream@^1.0.1: minimist "^1.1.3" through2 "^2.0.0" +ramda-adjunct@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ramda-adjunct/-/ramda-adjunct-4.0.0.tgz#99873cc707e86207ec7e757385144b3f235b7c59" + integrity sha512-W/NiJAlZdwZ/iUkWEQQgRdH5Szqqet1WoVH9cdqDVjFbVaZHuJfJRvsxqHhvq6tZse+yVbFatLDLdVa30wBlGQ== + +ramda@~0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.29.0.tgz#fbbb67a740a754c8a4cbb41e2a6e0eb8507f55fb" + integrity sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA== + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-codemirror@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-1.0.0.tgz#91467b53b1f5d80d916a2fd0b4c7adb85a9001ba" @@ -2257,7 +3087,7 @@ readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.3, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -2273,6 +3103,11 @@ redux-mock-store@^1.5.4: dependencies: lodash.isplainobject "^4.0.6" +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: version "1.5.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" @@ -2287,12 +3122,17 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve@1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== -resolve@^1.1.5, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.2: +resolve@^1.1.5, resolve@^1.22.1, resolve@^1.3.2: version "1.22.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== @@ -2322,6 +3162,13 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -2329,16 +3176,16 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -2353,7 +3200,7 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -2391,7 +3238,7 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.7: +semver@^7.3.5, semver@^7.3.7: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -2415,6 +3262,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +short-unique-id@^4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/short-unique-id/-/short-unique-id-4.4.4.tgz#a45df68303bbd2dbb5785ed7708e891809c9cb7a" + integrity sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -2424,6 +3276,25 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -2436,6 +3307,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -2468,6 +3344,11 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== +stampit@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/stampit/-/stampit-4.3.2.tgz#cfd3f607dd628a161ce6305621597994b4d56573" + integrity sha512-pE2org1+ZWQBnIxRPrBM2gVupkuDD0TTNIo1H6GdT/vO82NXli2z8lRE8cu/nBIHrcOCXFBAHpb9ZldrB2/qOA== + static-eval@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.1.0.tgz#a16dbe54522d7fa5ef1389129d813fd47b148014" @@ -2562,11 +3443,26 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2586,6 +3482,63 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swagger-client@^3.19.8: + version "3.19.11" + resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.19.11.tgz#b0725d260a9cb13745326dcec2b7dac0be3bcb23" + integrity sha512-ef4t4nRGC8NuC8rz6OazEGU/QgkrFVMUba1vDmCL1Zuov50rTix9f33COr6RSmzQEc9aqY/kd+6f43a/7TbHhQ== + dependencies: + "@babel/runtime-corejs3" "^7.20.13" + "@swagger-api/apidom-core" ">=0.71.0 <1.0.0" + "@swagger-api/apidom-json-pointer" ">=0.71.0 <1.0.0" + "@swagger-api/apidom-ns-openapi-3-1" ">=0.71.0 <1.0.0" + "@swagger-api/apidom-reference" ">=0.71.1 <1.0.0" + cookie "~0.5.0" + cross-fetch "^3.1.5" + deepmerge "~4.3.0" + fast-json-patch "^3.0.0-1" + form-data-encoder "^1.4.3" + formdata-node "^4.0.0" + is-plain-object "^5.0.0" + js-yaml "^4.1.0" + lodash "^4.17.21" + qs "^6.10.2" + traverse "~0.6.6" + url "~0.11.0" + +synckit@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -2633,6 +3586,11 @@ tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2640,11 +3598,48 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +traverse@~0.6.6: + version "0.6.7" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.7.tgz#46961cd2d57dd8706c36664acde06a248f1173fe" + integrity sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg== + +tree-sitter-json@=0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/tree-sitter-json/-/tree-sitter-json-0.20.0.tgz#e17bb4917e8d5fe9f2f0d5eaec603e2d3552b07c" + integrity sha512-PteOLH+Tx6Bz4ZA/d40/DbkiSXXRM/gKahhHI8hQ1lWNfFvdknnz9k3Mz84ol5srRyLboJ8wp8GSkhZ6ht9EGQ== + dependencies: + nan "^2.14.1" + +tree-sitter-yaml@=0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/tree-sitter-yaml/-/tree-sitter-yaml-0.5.0.tgz#c617ba72837399d8105ec10cdb4c360e1ed76076" + integrity sha512-POJ4ZNXXSWIG/W4Rjuyg36MkUD4d769YRUGKRqN+sVaj/VCo6Dh6Pkssn1Rtewd5kybx+jT1BWMyWN0CijXnMA== + dependencies: + nan "^2.14.0" + +tree-sitter@=0.20.4: + version "0.20.4" + resolved "https://registry.yarnpkg.com/tree-sitter/-/tree-sitter-0.20.4.tgz#7d9d4f769fc05342ef43e5559f7ff34b0fc48327" + integrity sha512-rjfR5dc4knG3jnJNN/giJ9WOoN1zL/kZyrS0ILh+eqq8RNcIbiXA63JsMEgluug0aNvfQvK4BfCErN1vIzvKog== + dependencies: + nan "^2.17.0" + prebuild-install "^7.1.1" + triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== +ts-toolbelt@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz#50a25426cfed500d4a09bd1b3afb6f28879edfd5" + integrity sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w== + tsconfig-paths@^3.14.1: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" @@ -2655,15 +3650,20 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.0, tslib@^1.8.1: +tslib@^1.13.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslint@^5.11.0: - version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" - integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== +tslib@^2.5.0, tslib@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== + +tslint@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" + integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== dependencies: "@babel/code-frame" "^7.0.0" builtin-modules "^1.1.1" @@ -2673,10 +3673,10 @@ tslint@^5.11.0: glob "^7.1.1" js-yaml "^3.13.1" minimatch "^3.0.4" - mkdirp "^0.5.1" + mkdirp "^0.5.3" resolve "^1.3.2" semver "^5.3.0" - tslib "^1.8.0" + tslib "^1.13.0" tsutils "^2.29.0" tsutils@^2.29.0: @@ -2693,6 +3693,13 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -2736,18 +3743,34 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript-eslint-parser@^18.0.0: +types-ramda@^0.29.4: + version "0.29.4" + resolved "https://registry.yarnpkg.com/types-ramda/-/types-ramda-0.29.4.tgz#8d9b51df2e550a05cedab541cc75dcd72972c625" + integrity sha512-XO/820iRsCDwqLjE8XE+b57cVGPyk1h+U9lBGpDWvbEky+NQChvHVwaKM05WnW1c5z3EVQh8NhXFmh2E/1YazQ== + dependencies: + ts-toolbelt "^9.6.0" + +typescript-eslint-parser@^22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-22.0.0.tgz#f5e766c9b50711b03535e29a10b45f957e3c516a" + integrity sha512-pD8D7oTeRwWvFVxK3PaY6FYAiZsuRXFkIc2+1xkwCT3NduySgCgjeAkR5/dnIWecOiFVcEHf4ypXurF02Q6Z3Q== + dependencies: + eslint-scope "^4.0.0" + eslint-visitor-keys "^1.0.0" + typescript-estree "18.0.0" + +typescript-estree@18.0.0: version "18.0.0" - resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-18.0.0.tgz#3e5055a44980d69e4154350fc5d8b1ab4e2332a8" - integrity sha512-Pn/A/Cw9ysiXSX5U1xjBmPQlxtWGV2o7jDNiH/u7KgBO2yC/y37wNFl2ogSrGZBQFuglLzGq0Xl0Bt31Jv44oA== + resolved "https://registry.yarnpkg.com/typescript-estree/-/typescript-estree-18.0.0.tgz#a309f6c6502c64d74b3f88c205d871a9af0b1d40" + integrity sha512-HxTWrzFyYOPWA91Ij7xL9mNUVpGTKLH2KiaBn28CMbYgX2zgWdJqU9hO7Are+pAPAqY91NxAYoaAyDDZ3rLj2A== dependencies: lodash.unescape "4.0.1" semver "5.5.0" -typescript@^4.4.2: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.0.4: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== unbox-primitive@^1.0.2: version "1.0.2" @@ -2783,6 +3806,16 @@ universal-cookie@^4.0.0: "@types/cookie" "^0.3.3" cookie "^0.4.0" +unraw@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unraw/-/unraw-2.0.1.tgz#7b51dcdfb1e43d59d5e52cdb44d349d029edbaba" + integrity sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ== + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2790,11 +3823,42 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url@~0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.1.tgz#26f90f615427eca1b9f4d6a28288c147e2302a32" + integrity sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA== + dependencies: + punycode "^1.4.1" + qs "^6.11.0" + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + +web-tree-sitter@=0.20.3: + version "0.20.3" + resolved "https://registry.yarnpkg.com/web-tree-sitter/-/web-tree-sitter-0.20.3.tgz#3dd17b283ad63b1d8c07c5ea814f0fefb2b1f776" + integrity sha512-zKGJW9r23y3BcJusbgvnOH2OYAW40MXAOi9bi3Gcc7T4Gms9WWgXF8m6adsJWpGJEhgOzCrfiz1IzKowJWrtYw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -2825,7 +3889,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -winston-transport@^4.4.2: +winston-transport@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== @@ -2834,21 +3898,22 @@ winston-transport@^4.4.2: readable-stream "^3.6.0" triple-beam "^1.3.0" -winston@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.5.1.tgz#b25cc899d015836dbf8c583dec8c4c4483a0da2e" - integrity sha512-tbRtVy+vsSSCLcZq/8nXZaOie/S2tPXPFt4be/Q3vI/WtYwm7rrwidxVw2GRa38FIXcJ1kUM6MOZ9Jmnk3F3UA== +winston@3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.9.0.tgz#2bbdeb8167a75fac6d9a0c6d002890cd908016c2" + integrity sha512-jW51iW/X95BCW6MMtZWr2jKQBP4hV5bIDq9QrIjfDk6Q9QuxvTKEAlpUNAzP+HYHFFCeENhph16s0zEunu4uuQ== dependencies: + "@colors/colors" "1.5.0" "@dabh/diagnostics" "^2.0.2" async "^3.2.3" is-stream "^2.0.0" - logform "^2.3.2" + logform "^2.4.0" one-time "^1.0.0" readable-stream "^3.4.0" safe-stable-stringify "^2.3.1" stack-trace "0.0.x" triple-beam "^1.3.0" - winston-transport "^4.4.2" + winston-transport "^4.5.0" word-wrap@~1.2.3: version "1.2.3" @@ -2860,10 +3925,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -xmlcreate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f" - integrity sha512-Mbe56Dvj00onbnSo9J0qj/XlY5bfN9KidsOnpd5tRCsR3ekB3hyyNU9fGrTdqNT5ZNvv4BsA2TcQlignsZyVcw== +xmlcreate@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be" + integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg== xmldoc@^1.1.2: version "1.3.0" From a957b240fbe82d379ae9c87e59e04df273e30d4a Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:58:28 +0200 Subject: [PATCH 24/46] Fix 4.6.0 changelog merge errors (#5692) * Update changelog * Update CHANGELOG.md --- CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 620d6677d6..8fc509559a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,6 @@ All notable changes to the Wazuh app project will be documented in this file. - Added `ignore` and `restrict` options to Syslog configuration. [#5203](https://github.com/wazuh/wazuh-kibana-app/pull/5203) - Added the `extensions.github` and `extensions.office` settings to the default configuration file [#5376](https://github.com/wazuh/wazuh-kibana-app/pull/5376) - Added new global error treatment (client-side) [#4163](https://github.com/wazuh/wazuh-kibana-app/pull/4163) -- Added a description to step 3 of the deploy a new agent section. [#5419](https://github.com/wazuh/wazuh-kibana-app/pull/5419) -- Added a title to the agent name input of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) -- Added callout below the agent name entry of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) - Added new CLI to generate API data from specification file [#5519](https://github.com/wazuh/wazuh-kibana-app/pull/5519) - Added specific RBAC permissions to Security section [#5551](https://github.com/wazuh/wazuh-kibana-app/pull/5551) @@ -20,10 +17,6 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed of regular expression in RBAC. [#5201](https://github.com/wazuh/wazuh-kibana-app/pull/5201) - Migrate the timeFilter, metaFields, maxBuckets health checks inside the pattern check. [#5384](https://github.com/wazuh/wazuh-kibana-app/pull/5384) -- Changed the title to step 3 of the deploy a new agent section. [#5419](https://github.com/wazuh/wazuh-kibana-app/pull/5419) -- Changed the title of step 3 of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) -- Changed the description of step 3 of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) -- Changed the placeholder of the agent name input of the deploy a new agent section. [#5429](https://github.com/wazuh/wazuh-kibana-app/pull/5429) - Changed the query to search for an agent in `management/configuration`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) - Changed the search bar in management/log to the one used in the rest of the app. [#5476](https://github.com/wazuh/wazuh-kibana-app/pull/5476) From 8e2405ba94d2ab3e9f7d454de0a18c32c73b8d6c Mon Sep 17 00:00:00 2001 From: Antonio <34042064+Desvelao@users.noreply.github.com> Date: Fri, 21 Jul 2023 15:02:04 +0200 Subject: [PATCH 25/46] Merge 4.5.2 into 4.6.0 (#5721) --- CHANGELOG.md | 6 + Makefile | 4 - RELEASING.md | 243 +++++++++ docker/imposter/agents/agent.json | 2 +- docker/imposter/agents/agents.json | 40 +- docker/imposter/api-info/api_info.json | 2 +- docker/osd-dev/dev.sh | 2 - docker/wazuh-4.4-wz/pre.sh | 2 + docker/wazuh-4.4-wz/rel.sh | 2 + docker/wazuh-4.x-es/pre.sh | 2 + docker/wazuh-4.x-es/rel.sh | 2 + plugins/main/README.md | 83 ++- plugins/main/opensearch_dashboards.json | 4 +- plugins/main/package.json | 4 +- plugins/main/public/package.test.ts | 19 - plugins/main/scripts/release/bump.js | 304 +++++++++++ plugins/main/scripts/release/lib/logger.js | 20 + .../release/lib/read-manifest-package.js | 20 + .../release/lib/update-manifest-package.js | 36 ++ .../release/lib/update-manifest-plugin.js | 35 ++ plugins/main/scripts/release/tag.js | 482 ++++++++++++++++++ plugins/main/scripts/tag.py | 151 ------ 22 files changed, 1221 insertions(+), 244 deletions(-) create mode 100644 RELEASING.md create mode 100644 plugins/main/scripts/release/bump.js create mode 100644 plugins/main/scripts/release/lib/logger.js create mode 100644 plugins/main/scripts/release/lib/read-manifest-package.js create mode 100644 plugins/main/scripts/release/lib/update-manifest-package.js create mode 100644 plugins/main/scripts/release/lib/update-manifest-plugin.js create mode 100644 plugins/main/scripts/release/tag.js delete mode 100644 plugins/main/scripts/tag.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc509559a..75f3723f55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,12 @@ All notable changes to the Wazuh app project will be documented in this file. - Removed obsolete code that caused duplicate requests to the api in `Management`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) - Removed unused embedded jquery-ui [#5592](https://github.com/wazuh/wazuh-kibana-app/pull/5592) +## Wazuh v4.5.2 - OpenSearch Dashboards 2.6.0 - Revision 01 + +### Added + +- Support for Wazuh 4.5.2 + ## Wazuh v4.5.1 - OpenSearch Dashboards 2.6.0 - Revision 01 ### Added diff --git a/Makefile b/Makefile index 24d9ab4370..ad6ba2f360 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,3 @@ prebuild: @echo "- Updating project's versions ..." @node scripts/generate-build-version -tags: - @echo "- Generating Git tags ..." - @python3 scripts/tag.py - diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000000..b6e18cab94 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,243 @@ +## Releasing + +## Runbook + +### Overview + +### Release Phase 1 - Preparation + +#### Files + +The following files must be updated: + +- `package.json`: Defines the package manifest. It contains the following properties: + - `version`: Plugin version. Schema: `{major}.{minor}.{patch}`. Example: 4.4.5 + - `revision`: Plugin revision. Schema: number with 2 digits. This value is reset for each version to `01` and increament for following revisions. + - `pluginPlatform.version`: version of the plugin platform. +- `opensearch_dashboards.json` or `kibana.json`: Defines the plugin manifest. It contains the following properties: + - `version`: Combination of version and revision of the plugin: `{version}-{revision}`. +- `README.md`: References to the version of server, indexer and dashboard applications. +- `CHANGELOG.md`: Changelog of the new release. +- `common/api-info/endpoints.json`: Data related to endpoints and extracted from server's API +- `common/api-info/security-actions.json`: Data related to security actions of extracted from server's API +- Unit tests + +To bump the version, see [# Bump](#Bump) + +#### Create tags + +After the base branches have set the expected [# Files](#files), we must create the tags. + +The tag name follows the pattern: +- final release tag: `v{version}-{platform version}`. Example: `v4.4.5-2.6.0`. +- non-final release tag: `v{version}-{platform version}{suffix}`. Example: `v4.4.5-2.6.0-pre-alpha1`, `v4.4.5-2.6.0-alpha1`, `v4.4.5-2.6.0-rc1`. + +> See the [script instructions](#create-tags---script) that reduces this job. + +#### Create tags - Manually + +Steps: + +1. Switch and update the base branch + +``` +git checkout +git pull +``` + +2. Review if the version, revision and platform values are defined to the target release in the [#Files](#files), if not accomodate them (creating a new commit). + +3. Create the tag + +``` +git tag {tag} -a -m "Wazuh {version} for {platform} {platform version}" +``` + +> replace the placeholders: +> +> - `{tag}`: tag name. Use this schema: `v{version}-{platform version}`. We add suffixes for release candidates or alpha versions: +> - pre-alpha: `-pre-alpha{number}`. Example: `-pre-alpha1`. +> - release candidates: `-rc{number}`. Example: `-rc1`. +> - `{version}`: plugin version +> - `{platform}`: platform name. One of `OpenSearch` or `Kibana` +> - `{platform}`: platform version. + +4. Push the tag + +``` +git push origin {tag} +``` + +> replace the placeholder: + +- `{tag}`: tag name + +#### Create tags - Script + +The process to create all the required tags can be run through a script ( `scripts/release/tag` ). + +For each supported version defined in `scripts/release/tag` + +- edit `revision` package manifest file: `package.json` +- edit the `version` property in plugin manifest file: `opensearch_dashboards.json` or `kibana.json` +- commit +- create tag +- push tag + +The script can be run through the package script `yarn release:tag` too. This is the prefered method because defines some required parameters. + +Steps: + +1. Ensure the target versions are defined as the supported versions in `scripts/release/tag` and the others files are updated. + Currently there are 3 platforms: OpenDistro (Kibana 7.10.2), Kibana 7.16-7.17 and OpenSearch (Wazuh stack). + +2. Bump version/revision/platform version and create the local and remote tag using the package script + +```console +yarn release:tag --revision +``` + +> If the version or the revision is not specified, then it will use the current values from the package manifest file (package.json). +> You can bump the `version` or `platform-version` too or combine them. +> :warning: if the `version` is set, the base branches must exist in the remote repository. + +```console +yarn release:tag --version +yarn release:tag --revision +yarn release:tag --platform-version +yarn release:tag --version --revision --platform-version +``` + +Examples: + +- Change the plugin version + +``` +yarn release:tag --version 4.5.0 +``` +- Change the plugin revision + +``` +yarn release:tag --revision 02 +``` +- Change the platform version + +``` +yarn release:tag --platform-version 2.8.0 +``` +- Change the plugin version, revision and platform version + +``` +yarn release:tag --version 4.5.0 --revision 02 --platform-version 2.8.0 +``` +For tags that needs a suffix, use the `--tag-suffix ` flag. + +``` +yarn release:tag --tag-suffix +``` + +Example: + +``` +yarn release:tag --tag-suffix -rc2 --revision 02 +``` + +If you want to get a report of the tags generated and stored locally, use the `--export-tags `. + +``` +yarn release:tag --revision --export-tags +``` + +Example: + +``` +yarn release:tag --version 4.5.0 --export-tags tags.log +``` + +3. Review the new tags were pushed to the remote repository. + +### Build packages + +## Release Phase 2 - Release testing + +### Release Phase 3 - Release Announcement + +### Release Phase 4 - Post-Release + +### Bump + +It means to increment the version number to a new, unique value. + +Bumping the version requires to do some changes in the source code of the application. See [# Files](#files). + +We have a script (`scripts/release/bump`) to update some of these files: + +- package.json +- opensearch_dashboards.json or kibana.json + +This can be run through the `yarn release:bump` package script too. This is the prefered method because defines some required parameters. **The rest of the files should be changed manually.** + +> The package script sets some required parameters related to manifest files. + +Steps: + +1. Switch to new branch from the base branch to bump + +```console +git checkout +git pull +git checkout -b +``` + +2. Bump the version/revision/platform version using the package script + +```console +yarn release:bump --version +``` + +> You can bump the `revision` or `platform-version` too or combine them. + +```console +yarn release:bump --version +yarn release:bump --revision +yarn release:bump --platform-version +yarn release:bump --version --revision --platform-version +``` + +Examples: + +- Change the plugin version + +``` +yarn release:bump --version 4.5.0 +``` + +- Change the plugin revision + +``` +yarn release:bump --revision 02 +``` + +- Change the platform version + +``` +yarn release:bump --platform-version 2.8.0 +``` + +- Change the plugin version, revision and platform version + +``` +yarn release:bump --version 4.5.0 --revision 02 --platform-version 2.8.0 +``` + +3. Apply manually the changes to the rest of files if needed it. See [# Files](#Files). + +4. Optional. Commit and push the new branch to the remote repository. + +``` +git add . +git commit -m "bump: Bump version/revision/platform version to " +git push origin +``` + +A new branch will be created in the remote and will be ready to receive pull requests or use as source to create the tags. diff --git a/docker/imposter/agents/agent.json b/docker/imposter/agents/agent.json index c8715054c7..cd97020021 100644 --- a/docker/imposter/agents/agent.json +++ b/docker/imposter/agents/agent.json @@ -24,7 +24,7 @@ "node_name": "master", "group": ["default", "debian"], "lastKeepAlive": "2022-09-12T08:48:40Z", - "version": "Wazuh v4.5.0" + "version": "Wazuh v4.3.7" } ], "total_affected_items": 1, diff --git a/docker/imposter/agents/agents.json b/docker/imposter/agents/agents.json index 3d961a0e4b..658d3dde5d 100644 --- a/docker/imposter/agents/agents.json +++ b/docker/imposter/agents/agents.json @@ -10,8 +10,6 @@ "uname": "Linux |wazuh-manager-master-0 |4.14.114-105.126.amzn2.x86_64 |#1 SMP Tue May 7 02:26:40 UTC 2019 |x86_64", "version": "2" }, - "ip": "127.0.0.1", - "id": "001", "group": [ "default", "test", @@ -21,16 +19,42 @@ "test5", "test6", "test7", - "test8" + "test8", + "test9", + "test10" ], - "registerIP": "127.0.0.1", + "ip": "FE80:0034:0223:A000:0002:B3FF:0000:8329", + "id": "000", + "registerIP": "FE80:0034:0223:A000:0002:B3FF:0000:8329", "dateAdd": "2022-08-25T16:17:46Z", - "name": "Debian agent", + "name": "wazuh-manager-master-0", "status": "active", "manager": "wazuh-manager-master-0", "node_name": "master", "lastKeepAlive": "9999-12-31T23:59:59Z", - "version": "Wazuh v4.5.0", + "version": "Wazuh v4.4.0", + "group_config_status": "synced" + }, + { + "os": { + "arch": "x86_64", + "major": "2", + "name": "Amazon Linux", + "platform": "amzn", + "uname": "Linux |wazuh-manager-master-0 |4.14.114-105.126.amzn2.x86_64 |#1 SMP Tue May 7 02:26:40 UTC 2019 |x86_64", + "version": "2" + }, + "group": ["default", "test", "test2", "test3", "test4", "test5"], + "ip": "FE80:1234:2223:A000:2202:B3FF:FE1E:8329", + "id": "001", + "registerIP": "FE80:1234:2223:A000:2202:B3FF:FE1E:8329", + "dateAdd": "2022-08-25T16:17:46Z", + "name": "wazuh-manager-master-0", + "status": "active", + "manager": "wazuh-manager-master-0", + "node_name": "master", + "lastKeepAlive": "9999-12-31T23:59:59Z", + "version": "Wazuh v4.4.0", "group_config_status": "not synced" }, { @@ -42,9 +66,9 @@ "uname": "Linux |wazuh-manager-master-0 |4.14.114-105.126.amzn2.x86_64 |#1 SMP Tue May 7 02:26:40 UTC 2019 |x86_64", "version": "2" }, + "group": ["default", "test", "test2"], "ip": "127.0.0.1", "id": "002", - "group": ["default", "test", "test2", "test3", "test4"], "registerIP": "127.0.0.1", "dateAdd": "2022-08-25T16:17:46Z", "name": "wazuh-manager-master-0", @@ -52,7 +76,7 @@ "manager": "wazuh-manager-master-0", "node_name": "master", "lastKeepAlive": "9999-12-31T23:59:59Z", - "version": "Wazuh v4.5.0", + "version": "Wazuh v4.4.0", "group_config_status": "synced" }, { diff --git a/docker/imposter/api-info/api_info.json b/docker/imposter/api-info/api_info.json index 4b720bbfcf..6bc67bd7fe 100644 --- a/docker/imposter/api-info/api_info.json +++ b/docker/imposter/api-info/api_info.json @@ -2,7 +2,7 @@ "data": { "title": "Wazuh API REST", "api_version": "4.6.0", - "revision": 4600, + "revision": 1, "license_name": "GPL 2.0", "license_url": "https://github.com/wazuh/wazuh/blob/4.5/LICENSE", "hostname": "imposter", diff --git a/docker/osd-dev/dev.sh b/docker/osd-dev/dev.sh index d0ee632dbb..b58e218728 100755 --- a/docker/osd-dev/dev.sh +++ b/docker/osd-dev/dev.sh @@ -8,7 +8,6 @@ os_versions=( '2.3.0' '2.4.0' '2.4.1' - '2.5.0' '2.6.0' ) @@ -20,7 +19,6 @@ osd_versions=( '2.3.0' '2.4.0' '2.4.1' - '2.5.0' '2.6.0' ) diff --git a/docker/wazuh-4.4-wz/pre.sh b/docker/wazuh-4.4-wz/pre.sh index 9487376cfa..7fee5f65e0 100755 --- a/docker/wazuh-4.4-wz/pre.sh +++ b/docker/wazuh-4.4-wz/pre.sh @@ -9,6 +9,8 @@ versions=( "4.4.5" "4.5.0" "4.5.1" + "4.5.2" + "4.6.0" ) wazuh_api_version=( diff --git a/docker/wazuh-4.4-wz/rel.sh b/docker/wazuh-4.4-wz/rel.sh index cd74d62c4b..7eb13031f3 100755 --- a/docker/wazuh-4.4-wz/rel.sh +++ b/docker/wazuh-4.4-wz/rel.sh @@ -9,6 +9,8 @@ versions=( "4.4.5" "4.5.0" "4.5.1" + "4.5.2" + "4.6.0" ) usage() { diff --git a/docker/wazuh-4.x-es/pre.sh b/docker/wazuh-4.x-es/pre.sh index 4f1ef6606f..139dfb338c 100755 --- a/docker/wazuh-4.x-es/pre.sh +++ b/docker/wazuh-4.x-es/pre.sh @@ -40,6 +40,8 @@ wazuh_api_version=( "4.4.5" "4.5.0" "4.5.1" + "4.5.2" + "4.6.0" ) usage() { diff --git a/docker/wazuh-4.x-es/rel.sh b/docker/wazuh-4.x-es/rel.sh index 1aef8980b5..c741baef7b 100755 --- a/docker/wazuh-4.x-es/rel.sh +++ b/docker/wazuh-4.x-es/rel.sh @@ -40,6 +40,8 @@ wazuh_versions=( "4.4.5" "4.5.0" "4.5.1" + "4.5.2" + "4.6.0" ) usage() { diff --git a/plugins/main/README.md b/plugins/main/README.md index c50e7f6361..0d48293988 100644 --- a/plugins/main/README.md +++ b/plugins/main/README.md @@ -1,68 +1,45 @@ # Wazuh dashboard app -This folder contains the Wazuh dashboard plugin, from which you can navigate through the -Wazuh data using visualizations in a simple and understandable way. It also allows you to +This folder contains the Wazuh dashboard plugin, from which you can navigate through the +Wazuh data using visualizations in a simple and understandable way. It also allows you to manage the configuration and capabilities of the Wazuh server. ## Description -This plugin for Wazuh dashboard allows you to visualize and analyze Wazuh alerts stored in +This plugin for Wazuh dashboard allows you to visualize and analyze Wazuh alerts stored in the Wazuh Indexer. The plugin provides the following capabilities: -- Search alerts classified by modules and filter them using the different views. You will -be able to explore the alerts both at Wazuh cluster level, and in a particular agent. The -modules, divided into the following use cases, are: - - Security Information Management - - Security events: Browse through your security alerts, identifying issues and - threats in your environment. - - Integrity monitoring: Alerts related to file changes, including permissions, - content, ownership and attributes. - - Amazon AWS: Security events related to your Amazon AWS services, collected - directly via AWS API. - - Office 365: Security events related to your Office 365 services. - - GitHub: Security events related to your GitHub organizations, collected via - GitHub audit logs API. - - Google Cloud Platform: Security events related to your Google Cloud Platform - services, collected directly via GCP API. - - Auditing and Policy Monitoring - - Policy monitoring: Verify that your systems are configured according to your - security policies baseline. - - Security configuration assessment: Scan your assets as part of a configuration - assessment audit. - - System auditing: Audit users behavior, monitoring command execution and - alerting on access to critical files. - - OpenSCAP: Configuration assessment and automation of compliance monitoring - using SCAP checks. - - CIS-CAT: Configuration assessment using Center of Internet Security scanner - and SCAP checks. - - Threat Detection and Response - - Vulnerabilities: Discover what applications in your environment are affected by - well-known vulnerabilities. - - MITRE ATT&CK: Security events from the knowledge base of adversary tactics and - techniques based on real-world observations. - - VirusTotal: Alerts resulting from VirusTotal analysis of suspicious files via an - integration with their API. - - Osquery: Osquery can be used to expose an operating system as a high-performance - relational database. - - Docker listener: Monitor and collect the activity from Docker containers such as - creation, running, starting, stopping or pausing events. - - Regulatory Compliance - - PCI DSS: Global security standard for entities that process, store or transmit - payment cardholder data. - - NIST 800-53: National Institute of Standards and Technology Special Publication - 800-53 (NIST 800-53) sets guidelines for federal information systems. - - GDPR: General Data Protection Regulation (GDPR) sets guidelines for processing - of personal data. - - HIPAA: Health Insurance Portability and Accountability Act of 1996 (HIPAA) - provides data privacy and security provisions for safeguarding medical information. - - TSC: Trust Services Criteria for Security, Availability, Processing Integrity, - Confidentiality, and Privacy. +- Search alerts classified by modules and filter them using the different views. You will be able to explore the alerts both at Wazuh cluster level, and in a particular agent. The modules, divided into the following use cases, are: + - Security Information Management + - Security events: Browse through your security alerts, identifying issues and threats in your environment. + - Integrity monitoring: Alerts related to file changes, including permissions, content, ownership and attributes. + - Amazon AWS: Security events related to your Amazon AWS services, collected directly via AWS API. + - Office 365: Security events related to your Office 365 services. + - GitHub: Security events related to your GitHub organizations, collected via GitHub audit logs API. + - Google Cloud Platform: Security events related to your Google Cloud Platform services, collected directly via GCP API. + - Auditing and Policy Monitoring + - Policy monitoring: Verify that your systems are configured according to your security policies baseline. + - Security configuration assessment: Scan your assets as part of a configuration assessment audit. + - System auditing: Audit users behavior, monitoring command execution and alerting on access to critical files. + - OpenSCAP: Configuration assessment and automation of compliance monitoring using SCAP checks. + - CIS-CAT: Configuration assessment using Center of Internet Security scanner and SCAP checks. + - Threat Detection and Response + - Vulnerabilities: Discover what applications in your environment are affected by well-known vulnerabilities. + - MITRE ATT&CK: Security events from the knowledge base of adversary tactics and techniques based on real-world observations. + - VirusTotal: Alerts resulting from VirusTotal analysis of suspicious files via an integration with their API. + - Osquery: Osquery can be used to expose an operating system as a high-performance relational database. + - Docker listener: Monitor and collect the activity from Docker containers such as creation, running, starting, stopping or pausing events. + - Regulatory Compliance + - PCI DSS: Global security standard for entities that process, store or transmit payment cardholder data. + - NIST 800-53: National Institute of Standards and Technology Special Publication 800-53 (NIST 800-53) sets guidelines for federal information systems. + - GDPR: General Data Protection Regulation (GDPR) sets guidelines for processing of personal data. + - HIPAA: Health Insurance Portability and Accountability Act of 1996 (HIPAA) provides data privacy and security provisions for safeguarding medical information. + - TSC: Trust Services Criteria for Security, Availability, Processing Integrity, Confidentiality, and Privacy. - View and edit the Wazuh server configuration. - Manage your ruleset (rules, decoders and CDB lists). - Manage your groups of agents. - Check the status and logs of your Wazuh cluster. -- Manage your agents, as well as see their configuration and data inventory. You can also -deploy new agents. +- Manage your agents, as well as see their configuration and data inventory. You can also deploy new agents. - Explore and interact with the Wazuh API through our Dev Tools. ## Software and libraries used diff --git a/plugins/main/opensearch_dashboards.json b/plugins/main/opensearch_dashboards.json index 40bab887f8..7c10bc68fb 100644 --- a/plugins/main/opensearch_dashboards.json +++ b/plugins/main/opensearch_dashboards.json @@ -2,9 +2,7 @@ "id": "wazuh", "version": "4.6.0-01", "opensearchDashboardsVersion": "opensearchDashboards", - "configPath": [ - "wazuh" - ], + "configPath": ["wazuh"], "requiredPlugins": [ "navigation", "data", diff --git a/plugins/main/package.json b/plugins/main/package.json index 43894ca580..37d9923be4 100644 --- a/plugins/main/package.json +++ b/plugins/main/package.json @@ -2,8 +2,6 @@ "name": "wazuh", "version": "4.6.0", "revision": "01", - "stage": "stable", - "commit": "c805cbcd0", "pluginPlatform": { "version": "2.6.0" }, @@ -43,6 +41,8 @@ "test:jest": "node scripts/jest", "test:jest:runner": "node scripts/runner test", "generate:api-data": "node scripts/generate-api-data.js --spec https://raw.githubusercontent.com/wazuh/wazuh/$(node -e \"console.log(require('./package.json').version.split('.').splice(0,2).join('.'))\")/api/api/spec/spec.yaml --output file --output-directory common/api-info --display-configuration", + "release:bump": "node scripts/release/bump --manifest-package package.json --manifest-plugin opensearch_dashboards.json", + "release:tag": "node scripts/release/tag --manifest-package package.json", "prebuild": "node scripts/generate-build-version" }, "dependencies": { diff --git a/plugins/main/public/package.test.ts b/plugins/main/public/package.test.ts index 0ee26aa170..b7986f4910 100644 --- a/plugins/main/public/package.test.ts +++ b/plugins/main/public/package.test.ts @@ -29,22 +29,3 @@ describe('package.json revison', () => { expect(revisionIsNaN).toBeFalsy(); }); }); - -describe('package.json stage', () => { - it('should have a stage', () => { - expect(packageValue.stage).toBeDefined(); - }); - it('the state should be one of the defined.', () => { - const stateDefined = [ - 'pre-alpha', - 'alpha', - 'beta', - 'release-candidate', - 'stable', - ]; - - const stage = packageValue.stage; - - expect(stateDefined).toContain(stage); - }); -}); diff --git a/plugins/main/scripts/release/bump.js b/plugins/main/scripts/release/bump.js new file mode 100644 index 0000000000..68fa65b65c --- /dev/null +++ b/plugins/main/scripts/release/bump.js @@ -0,0 +1,304 @@ +// NodeJS script which receives a version, revision and/or platform version and updates +// the package.json file +// Usage: node bump.js +// Help: node bump.js --help +// Examples: node bump.js --examples + +// Process +// 1. Take values from stdin +// 2. Edit the package and plugin manifest files +// 3. Display warning about manual changes + +const cliName = 'bump'; +const cliDescription = `Bump the plugin version, revision and/or platform version +Some warning messages are sent to stderr.`; + +// Default configuration +const defaultConfiguration = { + displayConfiguration: false, + displayExamples: false, + displayHelp: false, + version: '', + revision: '', + platformVersion: '', + manifestPackage: '', + manifestPlugin: '', +}; + +const logger = require('./lib/logger'); + +const { readPackageManifest } = require('./lib/read-manifest-package'); +const { updatePackageManifest } = require('./lib/update-manifest-package'); +const { updatePluginManifest } = require('./lib/update-manifest-plugin'); + +/** + * + * @param {String[]} input Input parameters + * @returns {Object} the configuration values + */ +function parse(input) { + const configuration = {}; + // Parse the input parameters + while (input.length) { + // Extract the first parameter + const [parameter] = input.splice(0, 1); + + // Compare the parameter + switch (parameter) { + case '--debug': + // Set the logger to debug mode + logger.setLevel(0); + break; + case '--display-configuration': + // Display the configuration + configuration.displayConfiguration = true; + break; + case '--examples': + // Display the examples + configuration.displayExamples = true; + break; + case '--help': + // Display the help + configuration.displayHelp = true; + break; + case '--version': { + // Set the version + const version = typeof input[0] === 'string' && input[0]; + + if (version) { + if (/\d+\.\d+\.\d+/.test(version)) { + configuration.version = version; + input.splice(0, 1); + } else { + logger.error( + 'version parameter is not valid. Expected format: X.Y.Z where X,Y, and Z are numbers.', + ); + process.exit(1); + } + } else { + logger.error('version parameter is not defined.'); + process.exit(1); + } + break; + } + case '--revision': { + // Set the version + const revision = typeof input[0] === 'string' && input[0]; + + if (revision) { + if (/\d{2}/.test(revision)) { + configuration.revision = revision; + input.splice(0, 1); + } else { + logger.error( + 'revision parameter is not valid. Expected format: Number', + ); + process.exit(1); + } + } else { + logger.error('revision parameter is not defined.'); + process.exit(1); + } + break; + } + case '--platform-version': { + // Set the version + const platformVersion = typeof input[0] === 'string' && input[0]; + + if (platformVersion) { + if (/\d+\.\d+\.\d+/.test(platformVersion)) { + configuration.platformVersion = platformVersion; + input.splice(0, 1); + } else { + logger.error( + 'platform-version parameter is not valid. Expected format: X.Y.Z where X,Y, and Z are numbers.', + ); + process.exit(1); + } + } else { + logger.error('platform-version parameter is not defined.'); + process.exit(1); + } + break; + } + case '--manifest-package': { + // Set the version + const manifestPackage = typeof input[0] === 'string' && input[0]; + + if (manifestPackage) { + configuration.manifestPackage = manifestPackage; + input.splice(0, 1); + } else { + logger.error('manifest-package parameter is not defined.'); + process.exit(1); + } + break; + } + case '--manifest-plugin': { + // Set the version + const manifestPlugin = typeof input[0] === 'string' && input[0]; + + if (manifestPlugin) { + configuration.manifestPlugin = manifestPlugin; + input.splice(0, 1); + } else { + logger.error('manifest-plugin parameter is not defined.'); + process.exit(1); + } + break; + } + default: { + } + } + } + return configuration; +} + +const usageOptionsMessage = `Options: + --debug Set the logger to debug mode. + --display-configuration Display the configuration. Log to sterr. + --examples Display examples of usage. + --help Display the help. + --manifest-package Set the package manifest file location. + --manifest-plugin Set the plugin platform manifest file location. + --platform-version Set the platform version. + --revision Set the revision. + --version Set the version.`; + +/** + * Display the CLI help + */ +function displayHelp() { + console.log(`${cliName} - Help +${cliDescription} + +Usage: node ${cliFilePath} [options] + +${usageOptionsMessage} +`); +} + +/** + * Display the examples + */ +function displayExamples() { + console.log(` +- Change the plugin version +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --version 4.5.0 + +- Change the plugin revision +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --revision 02 --manifest-package ../package.json + +- Change the platform version +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --platform-version 2.8.0 + +- Change the plugin version, revision and platform version +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --version 4.5.0 --revision 02 --platform-version 2.8.0 +`); +} + +// Display the message to do manual changes +function displayMessageManualChanges() { + logger.warn( + 'Some files could require to do changes manually. See RELEASING.md.', + ); +} + +function run(configuration) { + const { + version, + revision, + platformVersion, + manifestPackage, + manifestPlugin, + } = configuration; + version && logger.info(`Version: ${version}`); + revision && logger.info(`Revision: ${revision}`); + platformVersion && logger.info(`Platform version: ${platformVersion}`); + manifestPackage && logger.info(`Package manifest: ${manifestPackage}`); + manifestPlugin && logger.info(`Plugin manifest: ${manifestPlugin}`); + + logger.info( + 'This will update the manifest files: package and platform plugin.', + ); + + updatePackageManifest(manifestPackage, { + version, + revision, + platformVersion, + }); + + updatePluginManifest(manifestPlugin, { + version, + revision, + }); + + displayMessageManualChanges(); +} + +function main(input) { + try { + // Display the help information and exit if there is no parameters + if (input.length === 0) { + displayHelp(); + process.exit(1); + } + + const configuration = { + ...defaultConfiguration, + ...parse(input), + }; + + // Display the configuration + if (configuration.displayConfiguration) { + /* Send to stderr. This does the configuration can be displayed and redirect the stdout output + to a file */ + console.error(configuration); + } + + // Display the help + if (configuration.displayHelp) { + displayHelp(); + process.exit(0); + } + + // Display the examples of usage + if (configuration.displayExamples) { + displayExamples(); + process.exit(0); + } + + // Check version is set + if (!configuration.version || !configuration.revision) { + const { version: manifestVersion, revision: manifestRevision } = + readPackageManifest(configuration.manifestPackage); + if (!configuration.version) { + logger.warn( + `version is not defined. Using from the current package manifest ${configuration.manifestPackage}: ${manifestVersion}`, + ); + configuration.version = manifestVersion; + } + if (!configuration.revision) { + logger.warn( + `revision is not defined. Using from the current package manifest ${configuration.manifestPackage}: ${manifestRevision}`, + ); + configuration.revision = manifestRevision; + } + } + + run(configuration); + } catch (error) { + logger.error(`An unexpected error: ${error}. ${error.stack}`); + process.exit(1); + } +} + +module.exports = run; + +let cliFilePath = __dirname; + +if (require.main === module) { + cliFilePath = process.argv[1]; + const consoleInputParameters = [...process.argv].slice(2); + main(consoleInputParameters); +} diff --git a/plugins/main/scripts/release/lib/logger.js b/plugins/main/scripts/release/lib/logger.js new file mode 100644 index 0000000000..17659895e3 --- /dev/null +++ b/plugins/main/scripts/release/lib/logger.js @@ -0,0 +1,20 @@ +let minimumLevel = 1; + +function setLevel(level) { + minimumLevel = level; +} + +function createLogLevel(tag, level, fn) { + return function (message) { + level >= minimumLevel && fn(`[${tag}]: ${message}`); + }; +} + +module.exports = { + info: createLogLevel('INFO', 2, console.log), + warn: createLogLevel('WARN', 1, console.log), + error: createLogLevel('ERROR', 2, console.log), + debug: createLogLevel('DEBUG', 0, console.log), + getLevel: () => minimumLevel, + setLevel: setLevel, +}; diff --git a/plugins/main/scripts/release/lib/read-manifest-package.js b/plugins/main/scripts/release/lib/read-manifest-package.js new file mode 100644 index 0000000000..e4bdbbff2f --- /dev/null +++ b/plugins/main/scripts/release/lib/read-manifest-package.js @@ -0,0 +1,20 @@ +const logger = require('./logger'); + +function readPackageManifest(manifestPath) { + if (!manifestPath) { + logger.error( + `package manifest file is not defined. Use --manifest-package .`, + ); + process.exit(1); + } + const fs = require('fs'); + logger.debug(`Reading file ${manifestPath}`); + const packageJson = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + logger.debug(`Read file ${manifestPath}: ${JSON.stringify(packageJson)}`); + + return packageJson; +} + +module.exports = { + readPackageManifest: readPackageManifest, +}; diff --git a/plugins/main/scripts/release/lib/update-manifest-package.js b/plugins/main/scripts/release/lib/update-manifest-package.js new file mode 100644 index 0000000000..a3e7490a0b --- /dev/null +++ b/plugins/main/scripts/release/lib/update-manifest-package.js @@ -0,0 +1,36 @@ +const logger = require('./logger'); + +function updatePackageManifest( + manifestPath, + { version, revision, platformVersion }, +) { + if (!manifestPath) { + logger.error( + `package manifest file is not defined. Use --manifest-package .`, + ); + process.exit(1); + } + const fs = require('fs'); + logger.debug(`Reading file ${manifestPath}`); + const packageJson = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + logger.debug(`Read file ${manifestPath}: ${JSON.stringify(packageJson)}`); + version && + (packageJson.version = version) && + logger.debug(`Change version to ${packageJson.version}`); + revision && + (packageJson.revision = revision) && + logger.debug(`Change revision to ${packageJson.revision}`); + platformVersion && + (packageJson.pluginPlatform.version = platformVersion) && + logger.debug( + `Change platform version to ${packageJson.pluginPlatform.version}`, + ); + + logger.debug(`Updating ${manifestPath}: ${JSON.stringify(packageJson)}`); + fs.writeFileSync(manifestPath, JSON.stringify(packageJson, null, 2)); + logger.info(`Updated ${manifestPath}`); +} + +module.exports = { + updatePackageManifest: updatePackageManifest, +}; diff --git a/plugins/main/scripts/release/lib/update-manifest-plugin.js b/plugins/main/scripts/release/lib/update-manifest-plugin.js new file mode 100644 index 0000000000..dcacfebe9b --- /dev/null +++ b/plugins/main/scripts/release/lib/update-manifest-plugin.js @@ -0,0 +1,35 @@ +const logger = require('./logger'); + +function updatePluginManifest(manifestPath, { version, revision }) { + if (!manifestPath) { + logger.error( + `plugin manifest file is not defined. Use --manifest-plugin .`, + ); + process.exit(1); + } + + if (!version) { + logger.error(`version is not defined. Use --version .`); + process.exit(1); + } + + if (!revision) { + logger.error(`revision is not defined. Use --revision .`); + process.exit(1); + } + + const fs = require('fs'); + logger.debug(`Reading file ${manifestPath}`); + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + logger.debug(`Read file ${manifestPath}: ${JSON.stringify(manifest)}`); + manifest.version = `${version}-${revision}`; + logger.debug(`Change version to: ${manifestPath}: ${manifest.version}`); + + logger.debug(`Updating ${manifestPath}: ${JSON.stringify(manifest)}`); + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); + logger.info(`Updated ${manifestPath}`); +} + +module.exports = { + updatePluginManifest: updatePluginManifest, +}; diff --git a/plugins/main/scripts/release/tag.js b/plugins/main/scripts/release/tag.js new file mode 100644 index 0000000000..fc24479f7d --- /dev/null +++ b/plugins/main/scripts/release/tag.js @@ -0,0 +1,482 @@ +// NodeJS script which creates the tags in local and remote for the supported versions. +// It receives a version, revision and/or platform version and for each +// supported version: updates the package and plugin manifest files and create the tag. +// Usage: node tag.js +// Help: node tag.js --help +// Examples: node tag.js --examples + +// Process +// 1. Parse stdin +// 2. For each supported platform version +// 2.1. Checkout to the platform base branch +// 2.2. Prune local branches and tags +// 2.3. Edit the package manifest file +// 2.4. Edit the plugin manifest file +// 2.5. Commit +// 2.6. Create tag +// 2.7. Push tag +// 2.8. Reset local branch to remote branch +// 3. Optional. Export tags to file + +const cliName = 'tag'; +const cliDescription = `Create the tags in remote repository for the supported versions. +Some warning messages are sent to stderr.`; + +// Default configuration +const defaultConfiguration = { + displayConfiguration: false, + displayExamples: false, + displayHelp: false, + version: '', + revision: '', + platformVersion: '', + ignoreConfirmation: false, + manifestPackage: '', + manifestPlugin: '', + tagSuffix: '', + exportTagsToFile: '', +}; + +const logger = require('./lib/logger'); + +const { readPackageManifest } = require('./lib/read-manifest-package'); +const bump = require('./bump'); +const readline = require('readline'); + +// Supported versions +function getSupportedVersions(pluginVersion) { + return { + Kibana: { + branch: `${pluginVersion}-7.16`, + versions: [ + ...[...Array(4).keys()].map(idx => `7.16.${idx}`), + ...[...Array(11).keys()].map(idx => `7.17.${idx}`), + ], + manifestPluginPath: 'kibana.json', + }, + OpenDistro: { + branch: `${pluginVersion}-7.10`, + versions: ['7.10.2'], + manifestPluginPath: 'kibana.json', + }, + OpenSearch: { + branch: pluginVersion, + versions: ['2.6.0'], + manifestPluginPath: 'opensearch_dashboards.json', + }, + }; +} + +/** + * + * @param {String[]} input Input parameters + * @returns {Object} the configuration values + */ +function parse(input) { + const configuration = {}; + // Parse the input parameters + while (input.length) { + // Extract the first parameter + const [parameter] = input.splice(0, 1); + + // Compare the parameter + switch (parameter) { + case '--debug': + // Set the logger to debug mode + logger.setLevel(0); + break; + case '--display-configuration': + // Display the configuration + configuration.displayConfiguration = true; + break; + case '--export-tags': { + // Export tags to file + const exportTagsToFile = typeof input[0] === 'string' && input[0]; + if (exportTagsToFile) { + configuration.exportTagsToFile = exportTagsToFile; + input.splice(0, 1); + } else { + logger.error('export-tags parameter is not defined.'); + process.exit(1); + } + break; + } + case '--examples': + // Display the examples + configuration.displayExamples = true; + break; + case '--help': + // Display the help + configuration.displayHelp = true; + break; + case '--ignore-confirmation': + // Display the help + configuration.ignoreConfirmation = true; + break; + case '--version': { + // Set the version + const version = typeof input[0] === 'string' && input[0]; + + if (version) { + if (/\d+\.\d+\.\d+/.test(version)) { + configuration.version = version; + input.splice(0, 1); + } else { + logger.error( + 'version parameter is not valid. Expected format: X.Y.Z where X,Y, and Z are numbers.', + ); + process.exit(1); + } + } else { + logger.error('version parameter is not defined.'); + process.exit(1); + } + break; + } + case '--revision': { + // Set the version + const revision = typeof input[0] === 'string' && input[0]; + + if (revision) { + if (/\d{2}/.test(revision)) { + configuration.revision = revision; + input.splice(0, 1); + } else { + logger.error( + 'revision parameter is not valid. Expected format: Number', + ); + process.exit(1); + } + } else { + logger.error('revision parameter is not defined.'); + process.exit(1); + } + break; + } + case '--platform-version': { + // Set the version + const platformVersion = typeof input[0] === 'string' && input[0]; + + if (platformVersion) { + if (/\d+\.\d+\.\d+/.test(platformVersion)) { + configuration.platformVersion = platformVersion; + input.splice(0, 1); + } else { + logger.error( + 'platform-version parameter is not valid. Expected format: X.Y.Z where X,Y, and Z are numbers.', + ); + process.exit(1); + } + } else { + logger.error('platform-version parameter is not defined.'); + process.exit(1); + } + break; + } + case '--tag-suffix': { + // Set the version + const tagSuffix = typeof input[0] === 'string' && input[0]; + + if (tagSuffix) { + configuration.tagSuffix = tagSuffix; + input.splice(0, 1); + } else { + logger.error('tag-suffix parameter is not defined.'); + process.exit(1); + } + break; + } + case '--manifest-package': { + // Set the version + const manifestPackage = typeof input[0] === 'string' && input[0]; + + if (manifestPackage) { + configuration.manifestPackage = manifestPackage; + input.splice(0, 1); + } else { + logger.error('manifest-package parameter is not defined.'); + process.exit(1); + } + break; + } + case '--manifest-plugin': { + // Set the version + const manifestPlugin = typeof input[0] === 'string' && input[0]; + + if (manifestPlugin) { + configuration.manifestPlugin = manifestPlugin; + input.splice(0, 1); + } else { + logger.error('manifest-plugin parameter is not defined.'); + process.exit(1); + } + break; + } + default: { + } + } + } + return configuration; +} + +const usageOptionsMessage = `Options: + --debug Set the logger to debug mode. + --display-configuration Display the configuration. Log to sterr. + --examples Display examples of usage. + --export-tags Export tags to file. + --help Display the help. + --ignore-confirmation Ignore the confirmation. + --manifest-package Set the package manifest file location. + --manifest-plugin Set the plugin platform manifest file location. + --platform-version Set the platform version. + --revision Set the revision. + --tag-suffix Set the tag suffix. + --version Set the version.`; + +/** + * Display the CLI help + */ +function displayHelp() { + console.log(`${cliName} - Help +${cliDescription} + +Usage: node ${cliFilePath} [options] + +${usageOptionsMessage} +`); +} + +/** + * Display the examples + */ +function displayExamples() { + console.log(` +- Change the plugin version +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --version 4.5.0 + +- Change the plugin revision +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --revision 02 + +- Change the platform version +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --platform-version 2.8.0 + +- Change the plugin version, revision and platform version +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --version 4.5.0 + +- Change plugin revision and export tags to file +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --export-tags tags.log --revision 02 + +- Change plugin revision and ignoring the confirmation +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --ignore-confirmation --revision 02 + +- Change plugin revision and redirect the stdout and stderr to a file +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --ignore-confirmation --revision 02 &> /path/to/create_tags.log + +- Change plugin revision, redirect the stdout and stderr to a file and export tags to file +node ${cliFilePath} --manifest-package package.json --manifest-plugin opensearch_dashboards.json --ignore-confirmation --revision 02 --export-tags tags.log &> /path/to/create_tags.log +`); +} + +async function question(question) { + return new Promise(res => { + const rd = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + rd.question(question, input => { + rd.close(); + res(input); + }); + }); +} + +async function requireConfirmation({ ignoreConfirmation }) { + logger.warn( + 'Ensure the base branches are created in the remote and they have updated the files: ' + + 'README.md, CHANGELOG.md, unit tests files, API data files. ' + + 'It does not modify these files.', + ); + + logger.warn( + 'This script will commit and push the tags to the remote repository, ' + + 'deleting any unpushed changes.', + ); + + if (!ignoreConfirmation) { + const response = await question('Do you want to continue? [y/N] '); + if (response.toLowerCase() !== 'y') { + logger.info('Aborting...'); + process.exit(0); + } + } +} + +async function run(configuration) { + let { + version, + revision, + platformVersion, + tagSuffix, + exportTagsToFile, + ignoreConfirmation, + } = configuration; + if (!version || !revision) { + const { version: manifestVersion, revision: manifestRevision } = + readPackageManifest(configuration.manifestPackage); + if (!version) { + logger.warn( + `version is not defined. Using from the current package manifest ${configuration.manifestPackage}: ${manifestVersion}`, + ); + configuration.version = version = manifestVersion; + } + if (!revision) { + logger.warn( + `revision is not defined. Using from the current package manifest ${configuration.manifestPackage}: ${manifestRevision}`, + ); + configuration.revision = revision = manifestRevision; + } + } + version && logger.info(`Version: ${version}`); + revision && logger.info(`Revision: ${revision}`); + platformVersion && logger.info(`Platform version: ${platformVersion}`); + tagSuffix && logger.info(`Tag suffix: ${tagSuffix}`); + exportTagsToFile && logger.info(`Export tags to file: ${exportTagsToFile}`); + + const supportedVersions = getSupportedVersions(version); + logger.info('Supported platforms'); + for (const platformID in supportedVersions) { + const { + branch, + versions: pluginPlatformVersions, + manifestPluginPath, + } = supportedVersions[platformID]; + logger.info( + `Platform: ${platformID} - Base branch: ${branch} - Versions: [${pluginPlatformVersions.join( + ', ', + )}] - Manifest plugin path: ${manifestPluginPath}`, + ); + } + + await requireConfirmation({ ignoreConfirmation }); + + const { execSync } = require('child_process'); + + function execSystem(command) { + logger.info(`Run command: ${command}`); + return execSync(command); + } + + for (const platformID in supportedVersions) { + const { + branch, + versions: pluginPlatformVersions, + manifestPluginPath, + } = supportedVersions[platformID]; + + for (const pluginPlatformVersion of pluginPlatformVersions) { + logger.debug(`Switching to branch: ${branch}`); + execSystem(`git checkout ${branch}`); + logger.info(`Switched to branch: ${branch}`); + logger.debug('Pruning local branches and tags'); + execSystem('git fetch --prune --prune-tags'); + logger.info('Pruned local branches and tags'); + + const tag = `v${version}-${pluginPlatformVersion}${tagSuffix}`; + logger.info(`Generating tag: ${tag}...`); + logger.info('Calling to bump script'); + const configurationBump = { + ...configuration, + manifestPlugin: manifestPluginPath, + platformVersion: pluginPlatformVersion, + }; + logger.debug( + `Configuration to use with the bump script: ${configurationBump}`, + ); + + bump(configurationBump); + + logger.debug('Checking if there are changes to commit'); + const thereChangesToCommit = + execSystem('git diff --exit-code --no-patch;echo -n $?').toString() === + '1'; + logger.debug(`Are there changes to commit?: ${thereChangesToCommit}`); + + if (thereChangesToCommit) { + logger.info('There are changes to commit.'); + logger.debug('Commiting'); + execSystem(`git commit -am "Bump ${tag}"`); + logger.info('Commited'); + } else { + logger.info('There are not changes to commit.'); + } + + logger.debug(`Creating tag: ${tag}`); + execSystem( + `git tag -a ${tag} -m "Wazuh ${version} for ${platformID} ${pluginPlatformVersion}"`, + ); + logger.info(`Created tag: ${tag}`); + logger.debug(`Pushing tag ${tag} to remote`); + execSystem(`git push origin ${tag}`); + logger.info(`Pushed tag ${tag} to remote`); + logger.debug('Undoing changes'); + execSystem(`git reset --hard origin/${branch}`); + logger.info('Undone changes'); + } + } + + if (exportTagsToFile) { + logger.debug(`Exporting tags to file ${exportTagsToFile}`); + execSystem( + `git tag | grep -P -i "^v${version}-.*${tagSuffix}" > ${exportTagsToFile}`, + ); + logger.info(`Exported tags to file ${exportTagsToFile}`); + } +} + +async function main(input) { + try { + // Display the help information and exit if there is no parameters + if (input.length === 0) { + displayHelp(); + process.exit(1); + } + + const configuration = { + ...defaultConfiguration, + ...parse(input), + }; + + // Display the configuration + if (configuration.displayConfiguration) { + console.error(configuration); // Send to stderr. This does the configuration can be displayed and redirect the stdout output to a file + } + + // Display the help + if (configuration.displayHelp) { + displayHelp(); + process.exit(0); + } + + // Display the examples of usage + if (configuration.displayExamples) { + displayExamples(); + process.exit(0); + } + + await run(configuration); + } catch (error) { + logger.error(`An unexpected error: ${error}. ${error.stack}`); + process.exit(1); + } +} + +module.exports = run; + +let cliFilePath; + +if (require.main === module) { + cliFilePath = process.argv[1]; + const consoleInputParameters = [...process.argv].slice(2); + main(consoleInputParameters); +} diff --git a/plugins/main/scripts/tag.py b/plugins/main/scripts/tag.py deleted file mode 100644 index 68f4ce6747..0000000000 --- a/plugins/main/scripts/tag.py +++ /dev/null @@ -1,151 +0,0 @@ -import json -import logging -import os -import subprocess - -# ==================== CONFIGURATION ==================== # -# Fill the variables below with the desired values -# -# Values to modify: -# - version - sent to the package.json -# - revision - sent to the package.json -# - stage - sent to the package.json -# - tag_suffix - used by the tag generation -# - supported_versions & kbn_versions ONLY IF NEEDED (e.g. new Kibana version) -# ======================================================= # - -# Wazuh version: major.minor.patch -version = '4.6.0' -# App's revision number (previous rev + 1) -revision = '01' -# One of 'pre-alpha', 'alpha', 'beta', 'release-candidate', 'stable' -stage = 'stable' -# Tag suffix. Usually set to stage + stage iteration. -tag_suffix = '-alpha1' - -# ================================================ # -# Constants and global variables # -# ================================================ # -LOG_FILE = 'output.log' -TAGS_FILE = 'tags.log' -# Global variable. Will be set later -branch = None -minor = version - -# Supported versions of Kibana -kbn_versions = [ - [f'7.16.{x}' for x in range(0, 4)], - [f'7.17.{x}' for x in range(0, 10)] -] - -# Platforms versions -supported_versions = { - 'OpenDistro': { - 'branch': f'{minor}-7.10', - 'versions': ['7.10.2'] - }, - 'Kibana': { - 'branch': f'{minor}-7.16', - # Flatten 2D list kbn_versions using lists comprehension - 'versions': [item for sublist in kbn_versions for item in sublist] - }, - 'Wazuh Dashboard': { - 'branch': f'{minor}', - 'versions': ['2.6.0'] - } -} - -# ================================================ # -# Functions # -# ================================================ # - -def require_confirmation(): - """Ask for confirmation before running the script.""" - print('WARNING! This script will commit and push the tags to the remote ' - + 'repository, deleting any unpushed changes.') - confirmation = input('Do you want to continue? [y/N] ') - - if confirmation.lower() != 'y': - logging.info('Aborting...') - exit(0) - - -def get_git_revision_short_hash() -> str: - return subprocess.check_output(['git', 'rev-parse', '--short', branch]).decode('ascii').strip() - - -def update_package_json(v: str) -> tuple: - """Update package.json with the new version and revision.""" - logging.info(f'Updating package.json') - data, success = {}, True - - # Read JSON and update keys. - with open('package.json', 'r') as f: - data, success = json.load(f), False - - # Update file - data['commit'] = get_git_revision_short_hash() - data['version'] = version - data['revision'] = revision - data['stage'] = stage - data['pluginPlatform']['version'] = v - - with open('package.json', 'w') as f: - json.dump(data, f, indent=2) - - os.system('node scripts/generate-build-version') - - return data, success - - -def setup(): - """Sync the repo.""" - logging.info( - f'Switching to branch "{branch}" and removing outdated tags...') - os.system(f'git checkout {branch}') - os.system('git fetch --prune --prune-tags') - - -def main(platform: str, versions: list): - """Main function.""" - for v in versions: - # if stage == 'stable': - # pass # skipped as we have been asked to - # tag = f'v{version}-{v}' - # else: - tag = f'v{version}-{v}{tag_suffix}' - logging.info(f'Generating tag "{tag}"') - update_package_json(v) - os.system(f'git commit -am "Bump {tag}"') - os.system( - f'git tag -a {tag} -m "Wazuh {version} for {platform} {v}"') - logging.info(f'Pushing tag "{tag}" to remote.') - os.system(f'git push origin {tag}') - # Undo latest commit - os.system(f'git reset --hard origin/{branch}') - - # Save created tags to file - os.system(f'git tag | grep -P -i "^v{version}-.*-{tag_suffix}" > {TAGS_FILE}') - -# ================================================ # -# Main program # -# ================================================ # - -if __name__ == '__main__': - logging.basicConfig( - filename=LOG_FILE, - level=logging.INFO, - format='%(asctime)s %(message)s' - ) - logging.info( - f'Wazuh version is "{version}". App revision is "{revision}". Stage is "{stage}"') - require_confirmation() - - for platform_name, platform_data in supported_versions.items(): - branch, versions = platform_data['branch'], platform_data['versions'] - setup() - main(platform_name, versions) - - - print(f'\nCOMPLETED. \nCheck {LOG_FILE} for more details.') - print(f'Tags are stored in {TAGS_FILE}') From 0419bd8752e75010aaa283a4a42a66958ef0b427 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 26 Jul 2023 15:48:54 -0300 Subject: [PATCH 26/46] Add method to scape special chars in wazuh password --- .../services/wazuh-password-service.test.ts | 15 +++++++++++++++ .../services/wazuh-password-service.ts | 11 +++++++++++ 2 files changed, 26 insertions(+) create mode 100644 plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts create mode 100644 plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts new file mode 100644 index 0000000000..63b04b99c9 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -0,0 +1,15 @@ +import { getPasswordForCommand } from './wazuh-password-service'; + +describe('Wazuh Password Service', () => { + it('should return the password wrapped with singlequote and scaped the inner special characters', () => { + const password = `password'with'singlequote`; + const result = getPasswordForCommand(password, 'singlequote'); + expect(result).toBe('password\'with\'singlequote'); + }); + + it('should return the password wrapped with doublequote and scaped the inner special characters', () => { + const password = `password"with"doublequote`; + const result = getPasswordForCommand(password, 'doublequote'); + expect(result).toBe("password\"with\"doublequote"); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts new file mode 100644 index 0000000000..a38e030ffd --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -0,0 +1,11 @@ +export const getPasswordForCommand = ( + password: string, + wrapperType: 'doublequote' | 'singlequote', +) => { + // when the password contains the same wrapper type, we need to escape it + const escapedPassword = password.replace( + new RegExp(`\\${wrapperType}`, 'g'), + `\\${wrapperType}`, + ); + return escapedPassword; +}; From e84f88eb291735dfd03332c0b11936ff4329b4b8 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 28 Jul 2023 11:28:03 +0200 Subject: [PATCH 27/46] Upgrade environments to 4.6 and 4.7 (#5741) * Add wzd-dev.dockerfile * Update osd dev.sh --- docker/images/wzd-dev.Dockerfile | 24 ++++++++++++++++++++++++ docker/osd-dev/dev.sh | 13 ++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 docker/images/wzd-dev.Dockerfile diff --git a/docker/images/wzd-dev.Dockerfile b/docker/images/wzd-dev.Dockerfile new file mode 100644 index 0000000000..07ff77cf60 --- /dev/null +++ b/docker/images/wzd-dev.Dockerfile @@ -0,0 +1,24 @@ +# Usage: docker build --build-arg NODE_VERSION=16.20.0 --build-arg WAZUH_DASHBOARD_VERSION=4.6.0 -t quay.io/wazuh/osd-dev:4.6.0 -f wzd-dev.Dockerfile . + +ARG NODE_VERSION +FROM node:${NODE_VERSION} AS base +ARG WAZUH_DASHBOARD_VERSION +USER node +RUN git clone --depth 1 --branch ${WAZUH_DASHBOARD_VERSION} https://github.com/wazuh/wazuh-dashboard.git /home/node/kbn +RUN chown node.node /home/node/kbn + +WORKDIR /home/node/kbn +RUN yarn osd bootstrap --production + + +WORKDIR /home/node/kbn/plugins +RUN git clone --depth 1 --branch ${WAZUH_DASHBOARD_VERSION} https://github.com/wazuh/wazuh-security-dashboards-plugin.git +WORKDIR /home/node/kbn/plugins/wazuh-security-dashboards-plugin +RUN yarn install + +RUN mkdir -p /home/node/kbn/data/wazuh/config + +FROM node:${NODE_VERSION} +USER node +COPY --chown=node:node --from=base /home/node/kbn /home/node/kbn +WORKDIR /home/node/kbn diff --git a/docker/osd-dev/dev.sh b/docker/osd-dev/dev.sh index b58e218728..426eade63a 100755 --- a/docker/osd-dev/dev.sh +++ b/docker/osd-dev/dev.sh @@ -9,6 +9,8 @@ os_versions=( '2.4.0' '2.4.1' '2.6.0' + '2.8.0' + '2.9.0' ) osd_versions=( @@ -20,6 +22,8 @@ osd_versions=( '2.4.0' '2.4.1' '2.6.0' + '4.6.0' + '4.7.0' ) usage() { @@ -71,9 +75,16 @@ export OSD_VERSION=$2 export OSD_PORT=${PORT:-5601} export IMPOSTER_PORT=8081 export SRC=$3 -export OSD_MAJOR=$(echo $OSD_VERSION | cut -d. -f1).x +export OSD_MAJOR_NUMBER=$(echo $OSD_VERSION | cut -d. -f1) export COMPOSE_PROJECT_NAME=os-dev-${OSD_VERSION//./} +if [[ "$OSD_MAJOR_NUMBER" -ge 2 ]]; +then + export OSD_MAJOR="2.x" +else + export OSD_MAJOR="1.x" +fi + profile="standard" export WAZUH_DASHBOARD_CONF=./config/${OSD_MAJOR}/osd/opensearch_dashboards.yml export SEC_CONFIG_FILE=./config/${OSD_MAJOR}/os/config.yml From 97ecb9308a160766791c7a330bf4ac2e335cd416 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 31 Jul 2023 10:30:58 -0300 Subject: [PATCH 28/46] Applied special chars scape un command password --- .../components/command-output/command-output.tsx | 3 ++- .../containers/register-agent/register-agent.tsx | 8 +++++--- .../register-agent/containers/steps/steps.tsx | 13 ++++++++++++- .../services/wazuh-password-service.ts | 15 +++++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index dafb26d4b5..d460951359 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -18,7 +18,7 @@ interface ICommandSectionProps { } export default function CommandOutput(props: ICommandSectionProps) { - const { commandText, showCommand, onCopy, os, password } = props; + const { commandText, showCommand, onCopy, os, password} = props; const getHighlightCodeLanguage = (os: 'WINDOWS' | string) => { if (os.toLowerCase() === 'windows') { return 'powershell'; @@ -69,6 +69,7 @@ export default function CommandOutput(props: ICommandSectionProps) { return ( + { password }
      setInstallCommandWasCopied(true)} - password={registerAgentFormValues.optionalParams.wazuhPassword} + password={ + registerAgentFormValues.operatingSystem.name === 'macOS' + ? getPasswordForCommand( + registerAgentFormValues.optionalParams.wazuhPassword, + 'doublequote', + ) + : getPasswordForCommand( + registerAgentFormValues.optionalParams.wazuhPassword, + 'singlequote', + ) + } /> ) : null} diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index a38e030ffd..f66e46e524 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -2,10 +2,13 @@ export const getPasswordForCommand = ( password: string, wrapperType: 'doublequote' | 'singlequote', ) => { - // when the password contains the same wrapper type, we need to escape it - const escapedPassword = password.replace( - new RegExp(`\\${wrapperType}`, 'g'), - `\\${wrapperType}`, - ); - return escapedPassword; + + + if(wrapperType === 'doublequote') { + // scape doublequote in password + return password.replace(/"/g, '\"'); + } else { + // scape singlequote in password + return password.replace(/'/g, "\'"); + } }; From b8298b46cd4fb28cf4db6691bf46352678577d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chantal=20Bel=C3=A9n=20kelm?= <99441266+chantal-kelm@users.noreply.github.com> Date: Tue, 1 Aug 2023 16:24:01 -0300 Subject: [PATCH 29/46] Redesign deploy new agent page (#5457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * parent component * Added a title to the container and updated filenames * Update register-agent.scss * [Redesign add agent] Register agent reuse common/form component (Settings > Configuration) (#5446) * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * Remove react use inside hook test file * Fix review requested changes * [Redesign add agent] Add register agent command generator (#5469) * Create reusable card for operating systems (#5462) * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * reuse of common form on the card * Card with logic * CheckboxGroup component logic update * CheckboxGroup component logic update * Adding card icons * update checkbox logic, styles, and card styles * clean code * clean code * gitignore Mac files * updating checkbox logic, styles, and card styles * Update os-card.scss * macos card update * undoing merging as it was causing checkboxes not to work * test * file ds_store * file ds_store * file ds_store * remove files DS_store * remove files DS_store --------- Co-authored-by: Maximiliano Ibarra Co-authored-by: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> * 5518 inputs logic server address name password and group (#5554) * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * reuse of common form on the card * Card with logic * CheckboxGroup component logic update * CheckboxGroup component logic update * Adding card icons * update checkbox logic, styles, and card styles * clean code * clean code * gitignore Mac files * updating checkbox logic, styles, and card styles * step component * Passing interfaces to a separate file, updating styles, and component logic * Update interfaces and clean up code * update of folder structure and step logic * tcp, udp, protocols, password, groups, logics * input logic server address name password groups and styles * group input logic * oscards input logic * oscards input logic * styles * regex * styles and settings * styles * various adjustments * cleaning up code and changing some styles * cleaning up code * cleaning code * update password * gitignore * gitignore * correcting validation text in input agent name * correcting validation text in input agent name * corrección de validación de input de nombre del agente * cleaning code * cleaning code * regex that differentiates between FQDN and IP * Use of PLUGIN_VERSION_SHORT * Use of PLUGIN_VERSION_SHORT * link * Revert "Merge branch '4205-redesign-add-agent-page' into 5518-inputs-logic-server-address-name-password-and-group" This reverts commit a4c6fb5d24a482e80f9595a879d141ff2d7fa5bb, reversing changes made to 5a0d2cb0e71972eb8f68b16f035ebc977220379f. * link and revert * characteres valid * correction of styles when bringing changes from parent branch * change tooltip to popover * moving validations to a separate file with their tests * corrections and cleaning of comments * camel case * change in function * type * remove type * fullWidth * type * change * conditional * change label a to Euilink * change label a to Euilink * conditional * delete usePrevious * delete usePrevious * deleted files ds store * test correction and placeholder * show architecture instead of id * removing console css warnings * fixed regex fqdn * fixed regex fqdn * data * changelog * changelog --------- Co-authored-by: Maximiliano Ibarra Co-authored-by: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> * [Redesign add agent] Integration commands generator with UI (#5593) * Add useForm hook types * Add custom field use in useForm hook * Add some code redeability fixes * Refactored useForm types and unit tests * Move types to types file * reuse of common form on the card * Card with logic * CheckboxGroup component logic update * CheckboxGroup component logic update * Adding card icons * update checkbox logic, styles, and card styles * clean code * clean code * gitignore Mac files * updating checkbox logic, styles, and card styles * step component * Passing interfaces to a separate file, updating styles, and component logic * Update interfaces and clean up code * update of folder structure and step logic * tcp, udp, protocols, password, groups, logics * input logic server address name password groups and styles * group input logic * oscards input logic * oscards input logic * styles * regex * styles and settings * styles * various adjustments * cleaning up code and changing some styles * cleaning up code * cleaning code * update password * gitignore * gitignore * correcting validation text in input agent name * correcting validation text in input agent name * corrección de validación de input de nombre del agente * cleaning code * cleaning code * regex that differentiates between FQDN and IP * Use of PLUGIN_VERSION_SHORT * Use of PLUGIN_VERSION_SHORT * link * Revert "Merge branch '4205-redesign-add-agent-page' into 5518-inputs-logic-server-address-name-password-and-group" This reverts commit a4c6fb5d24a482e80f9595a879d141ff2d7fa5bb, reversing changes made to 5a0d2cb0e71972eb8f68b16f035ebc977220379f. * link and revert * characteres valid * correction of styles when bringing changes from parent branch * change tooltip to popover * moving validations to a separate file with their tests * corrections and cleaning of comments * camel case * change in function * type * remove type * fullWidth * type * change * conditional * change label a to Euilink * change label a to Euilink * conditional * delete usePrevious * delete usePrevious * deleted files ds store * test correction and placeholder * show architecture instead of id * Add register agent form values parser * Remove extension on operating system type * Add command sections with form values * Create new components for steps inputs * Fix some types * Renamed some options * Move commands config inside core folder * Fix server address error message display * Create methods to get form steps status * Allow select more than group * Hide agent group param when is empty * Fix steps form statuses * Remove break lines in commands * Add white space in error messages * Fix steps form status * Added new command component white custom copy and language * Fixed step form status --------- Co-authored-by: chantal.kelm Co-authored-by: Chantal Belén kelm <99441266+chantal-kelm@users.noreply.github.com> * [Redesign add agent] Dark mode (#5620) * remove custom color styles to make the elastic dark mode work by default on the agent registration page * add development for images to have dark mode in the section deploy a new agent * changelog about dark mode * Cleaning console.log from assets file * Adding suggested style modifications in the agent registration section * add a style hint so that text cannot be selected on cards * add suggested changes to the styles in the register an agent section * correction added to the word wizard * added coding enhancements in the agent registration section * adding an enhancement to eliminate the console error * adding an enhancement to eliminate the console error * [Redesign add agent] Add and validate register agent commands (#5622) * Add show/hide password in command component * Add protocol and password types * Add more step status methods * Add os commands service * Resolve strings replacements in command component * Change macos packages name by arch * Add \n to the macos params * Fixed parsed macos params inside echo * Add -e in mac os install command * Remove sudo from macos install command with echo * Add sudo to linux before optional params * Fix PR review comments * Fixed imports in tests * Fix components unit tests * Fix unit test checkbox group component * Fix os card unit test with mock uiSettings * modify the fqdn regex because it interferes with an ipv4 instance * [Redesign add page] Add form status callout message (#5634) * Add form status manager and unit tests * Add empty and invalid fields messages * Hide commands code block when exists warning messages * Fix fields names in warning messages * Updated CHANGELOG * Step 2: the design triggers warnings (#5649) * changing design to remove console warnings * update changelog * Changes in the display of pop-up windows in the agents log section * semicolon is added * update changelog * update changelog * Add requested fixs on texts * Add new rpm and deb install commands * modify fqdn regex * Fix server address validation unit test * Add type in command output types --------- Co-authored-by: Maximiliano Ibarra <6089438+Machi3mfl@users.noreply.github.com> Co-authored-by: Maximiliano Ibarra --- .DS_Store | Bin 8196 -> 0 bytes CHANGELOG.md | 1 + .../public/assets/images/icons/linux-icon.svg | 3 + .../public/assets/images/icons/mac-icon.svg | 4 + .../assets/images/icons/windows-icon.svg | 13 + .../assets/images/themes/dark/linux-icon.svg | 3 + .../assets/images/themes/dark/mac-icon.svg | 4 + .../images/themes/dark/windows-icon.svg | 13 + .../assets/images/themes/light/linux-icon.svg | 3 + .../assets/images/themes/light/mac-icon.svg | 4 + .../images/themes/light/windows-icon.svg | 13 + .../components/common/form/hooks.test.tsx | 387 ++++++++++-------- .../public/components/common/form/hooks.tsx | 174 +++++--- .../public/components/common/form/index.tsx | 74 +++- .../components/common/form/input_select.tsx | 30 +- .../components/common/form/input_text.tsx | 27 +- .../public/components/common/form/types.ts | 95 ++++- .../main/public/controllers/agent/index.js | 4 +- .../steps/wz-manager-address.tsx | 6 +- .../controllers/agent/wazuh-config/index.ts | 2 +- .../command-output/command-output.tsx | 103 +++++ .../components/group-input/group-input.scss | 5 + .../components/group-input/group-input.tsx | 102 +++++ .../optionals-inputs/optionals-inputs.tsx | 110 +++++ .../checkbox-group/checkbox-group.scss | 46 +++ .../checkbox-group/checkbox-group.test.tsx | 59 +++ .../checkbox-group/checkbox-group.tsx | 53 +++ .../os-selector/os-card/os-card.scss | 59 +++ .../os-selector/os-card/os-card.test.tsx | 47 +++ .../os-selector/os-card/os-card.tsx | 73 ++++ .../server-address/server-address.tsx | 103 +++++ .../register-agent/register-agent.scss | 27 ++ .../register-agent/register-agent.tsx | 242 +++++++++++ .../containers/steps/steps.scss | 55 +++ .../register-agent/containers/steps/steps.tsx | 258 ++++++++++++ .../core/config/os-commands-definitions.ts | 189 +++++++++ .../core/register-commands/README.md | 327 +++++++++++++++ .../command-generator.test.ts | 380 +++++++++++++++++ .../command-generator/command-generator.ts | 171 ++++++++ .../register-commands/exceptions/index.ts | 80 ++++ .../optional-parameters-manager.test.ts | 229 +++++++++++ .../optional-parameters-manager.ts | 49 +++ .../get-install-command.service.test.ts | 112 +++++ .../services/get-install-command.service.ts | 37 ++ .../search-os-definitions.service.test.ts | 176 ++++++++ .../services/search-os-definitions.service.ts | 84 ++++ .../core/register-commands/types.ts | 96 +++++ .../register-agent/hooks/README.md | 167 ++++++++ .../hooks/use-register-agent-commands.test.ts | 229 +++++++++++ .../hooks/use-register-agent-commands.ts | 109 +++++ .../controllers/register-agent/index.tsx | 1 + .../register-agent/interfaces/types.ts | 18 + .../services/form-status-manager.test.tsx | 145 +++++++ .../services/form-status-manager.tsx | 116 ++++++ .../register-agent-os-commands-services.tsx | 138 +++++++ .../services/register-agent-services.tsx | 295 +++++++++++++ .../register-agent-steps-status-services.tsx | 200 +++++++++ .../utils/register-agent-data.tsx | 47 +++ .../register-agent/utils/validations.test.tsx | 68 +++ .../register-agent/utils/validations.tsx | 57 +++ plugins/main/public/services/routes.js | 169 ++++---- .../public/styles/theme/dark/index.dark.scss | 174 ++++---- .../templates/agents-prev/agents-prev.html | 42 +- .../templates/visualize/dashboards.html | 35 +- plugins/main/public/utils/assets.ts | 3 +- 65 files changed, 5659 insertions(+), 486 deletions(-) delete mode 100644 .DS_Store create mode 100644 plugins/main/public/assets/images/icons/linux-icon.svg create mode 100644 plugins/main/public/assets/images/icons/mac-icon.svg create mode 100644 plugins/main/public/assets/images/icons/windows-icon.svg create mode 100644 plugins/main/public/assets/images/themes/dark/linux-icon.svg create mode 100644 plugins/main/public/assets/images/themes/dark/mac-icon.svg create mode 100644 plugins/main/public/assets/images/themes/dark/windows-icon.svg create mode 100644 plugins/main/public/assets/images/themes/light/linux-icon.svg create mode 100644 plugins/main/public/assets/images/themes/light/mac-icon.svg create mode 100644 plugins/main/public/assets/images/themes/light/windows-icon.svg create mode 100644 plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/group-input/group-input.scss create mode 100644 plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss create mode 100644 plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.scss create mode 100644 plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.tsx create mode 100644 plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx create mode 100644 plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.scss create mode 100644 plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx create mode 100644 plugins/main/public/controllers/register-agent/containers/steps/steps.scss create mode 100644 plugins/main/public/controllers/register-agent/containers/steps/steps.tsx create mode 100644 plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/README.md create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/exceptions/index.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts create mode 100644 plugins/main/public/controllers/register-agent/core/register-commands/types.ts create mode 100644 plugins/main/public/controllers/register-agent/hooks/README.md create mode 100644 plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts create mode 100644 plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts create mode 100644 plugins/main/public/controllers/register-agent/index.tsx create mode 100644 plugins/main/public/controllers/register-agent/interfaces/types.ts create mode 100644 plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx create mode 100644 plugins/main/public/controllers/register-agent/services/form-status-manager.tsx create mode 100644 plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx create mode 100644 plugins/main/public/controllers/register-agent/services/register-agent-services.tsx create mode 100644 plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx create mode 100644 plugins/main/public/controllers/register-agent/utils/register-agent-data.tsx create mode 100644 plugins/main/public/controllers/register-agent/utils/validations.test.tsx create mode 100644 plugins/main/public/controllers/register-agent/utils/validations.tsx diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index bd241bd50263ce7ea237ae2bf16dba59846221ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM-EPw`82!AJHdVK27l0-tq)6P3G1!4mRVhzPLiLY}m zq#1qYHc&uMv`bCeqZh!H0^6K04j2cF1I7X4fN|h|Z~*UYPS%9?zPf5k?dZY7d;jFljjPTRu97qQJeZJMJsLH|i|j+>XM58}d`z%7P;G0PcbI`;Lw5geMht+A_D1`GKeDBDa@@U8^O0v2c9!=d&J;R_G(j z;ws=3^DVVv=Fp-eSAj!)v^q)0t0%21LpMZ^0V3{Z$1PV+np>rPIJreT@TrL&3hP*~ zqiIDAWA7r8o{shikqq+w2=+Cts%Vvuu@%_T%{05lGmANXiP5#_fZkj;GQPvaLdvf) z`oR4{XIy6ECU}(L&Z7rzcMtvSqMsHVxITW|Zy9A7eV z@aEYQxQaDFyiWU*=9-<#C>Q9H98E~~W$ZE9h@lRE{p{$ZU!PNcRj;&)kLOKJ!LFqh zm{gOD15@e1tcrT625e6L{y&x8n4paV)8c?A?X-6qi1zXaY(8+_Yum^lkU24LtWZi& m$n7{#ZpVRF|1d<|29-5+jbnv4gY@q|1eo`~G~N_b?Z6+4d}LSv diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f3723f55..8ae71b32b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Migrate the timeFilter, metaFields, maxBuckets health checks inside the pattern check. [#5384](https://github.com/wazuh/wazuh-kibana-app/pull/5384) - Changed the query to search for an agent in `management/configuration`. [#5485](https://github.com/wazuh/wazuh-kibana-app/pull/5485) - Changed the search bar in management/log to the one used in the rest of the app. [#5476](https://github.com/wazuh/wazuh-kibana-app/pull/5476) +- Changed the design of the wizard to add agents. [#5457](https://github.com/wazuh/wazuh-kibana-app/pull/5457) ### Fixed diff --git a/plugins/main/public/assets/images/icons/linux-icon.svg b/plugins/main/public/assets/images/icons/linux-icon.svg new file mode 100644 index 0000000000..85613a6872 --- /dev/null +++ b/plugins/main/public/assets/images/icons/linux-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/main/public/assets/images/icons/mac-icon.svg b/plugins/main/public/assets/images/icons/mac-icon.svg new file mode 100644 index 0000000000..dbfed2e61f --- /dev/null +++ b/plugins/main/public/assets/images/icons/mac-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/plugins/main/public/assets/images/icons/windows-icon.svg b/plugins/main/public/assets/images/icons/windows-icon.svg new file mode 100644 index 0000000000..5ef43e4d08 --- /dev/null +++ b/plugins/main/public/assets/images/icons/windows-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/plugins/main/public/assets/images/themes/dark/linux-icon.svg b/plugins/main/public/assets/images/themes/dark/linux-icon.svg new file mode 100644 index 0000000000..c76c7d6328 --- /dev/null +++ b/plugins/main/public/assets/images/themes/dark/linux-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/main/public/assets/images/themes/dark/mac-icon.svg b/plugins/main/public/assets/images/themes/dark/mac-icon.svg new file mode 100644 index 0000000000..2eae996a06 --- /dev/null +++ b/plugins/main/public/assets/images/themes/dark/mac-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/plugins/main/public/assets/images/themes/dark/windows-icon.svg b/plugins/main/public/assets/images/themes/dark/windows-icon.svg new file mode 100644 index 0000000000..74d5b551f8 --- /dev/null +++ b/plugins/main/public/assets/images/themes/dark/windows-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/plugins/main/public/assets/images/themes/light/linux-icon.svg b/plugins/main/public/assets/images/themes/light/linux-icon.svg new file mode 100644 index 0000000000..85613a6872 --- /dev/null +++ b/plugins/main/public/assets/images/themes/light/linux-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/main/public/assets/images/themes/light/mac-icon.svg b/plugins/main/public/assets/images/themes/light/mac-icon.svg new file mode 100644 index 0000000000..dbfed2e61f --- /dev/null +++ b/plugins/main/public/assets/images/themes/light/mac-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/plugins/main/public/assets/images/themes/light/windows-icon.svg b/plugins/main/public/assets/images/themes/light/windows-icon.svg new file mode 100644 index 0000000000..5ef43e4d08 --- /dev/null +++ b/plugins/main/public/assets/images/themes/light/windows-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/plugins/main/public/components/common/form/hooks.test.tsx b/plugins/main/public/components/common/form/hooks.test.tsx index 38d3f19a27..283c4809bf 100644 --- a/plugins/main/public/components/common/form/hooks.test.tsx +++ b/plugins/main/public/components/common/form/hooks.test.tsx @@ -1,178 +1,219 @@ +import { fireEvent, render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import { renderHook, act } from '@testing-library/react-hooks'; +import React, { useState } from 'react'; import { useForm } from './hooks'; +import { FormConfiguration, IInputForm } from './types'; describe('[hook] useForm', () => { - - it(`[hook] useForm. Verify the initial state`, async () => { - - const initialFields = { - text1: { - type: 'text', - initialValue: '' - }, - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe('text'); - expect(result.current.fields.text1.value).toBe(''); - expect(result.current.fields.text1.initialValue).toBe(''); - expect(result.current.fields.text1.onChange).toBeDefined(); - }); - - it(`[hook] useForm. Verify the initial state. Multiple fields.`, async () => { - - const initialFields = { - text1: { - type: 'text', - initialValue: '' - }, - number1: { - type: 'number', - initialValue: 1 - }, - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe('text'); - expect(result.current.fields.text1.value).toBe(''); - expect(result.current.fields.text1.initialValue).toBe(''); - expect(result.current.fields.text1.onChange).toBeDefined(); - - expect(result.current.fields.number1.changed).toBe(false); - expect(result.current.fields.number1.error).toBeUndefined(); - expect(result.current.fields.number1.type).toBe('number'); - expect(result.current.fields.number1.value).toBe(1); - expect(result.current.fields.number1.initialValue).toBe(1); - expect(result.current.fields.number1.onChange).toBeDefined(); - }); - - it(`[hook] useForm lifecycle. Set the initial value. Change the field value. Undo changes. Change the field. Do changes.`, async () => { - - const initialFieldValue = ''; - const fieldType = 'text'; - - const initialFields = { - text1: { - type: fieldType, - initialValue: initialFieldValue - } - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(initialFieldValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - expect(result.current.fields.text1.onChange).toBeDefined(); - - // change the input - const changedValue = 't'; - act(() => { - result.current.fields.text1.onChange({ - target: { - value: changedValue - } - }); - }); - - // assert changed state - expect(result.current.fields.text1.changed).toBe(true); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - - // undone changes - act(() => { - result.current.undoChanges(); - }); - - // assert undo changes state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(initialFieldValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - - // change the input - const changedValue2 = 'e'; - act(() => { - result.current.fields.text1.onChange({ - target: { - value: changedValue2 - } - }); - }); - - // assert changed state - expect(result.current.fields.text1.changed).toBe(true); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue2); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - - // done changes - act(() => { - result.current.doChanges() - }); - - // assert do changes state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue2); - expect(result.current.fields.text1.initialValue).toBe(changedValue2); - }); - - it(`[hook] useForm lifecycle. Set the initial value. Change the field value to invalid value`, async () => { - - const initialFieldValue = 'test'; - const fieldType = 'text'; - - const initialFields = { - text1: { - type: fieldType, - initialValue: initialFieldValue, - validate: (value: string): string | undefined => value.length ? undefined : `Validation error: string can be empty.` - } - }; - - const { result } = renderHook(() => useForm(initialFields)); - - // assert initial state - expect(result.current.fields.text1.changed).toBe(false); - expect(result.current.fields.text1.error).toBeUndefined(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(initialFieldValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - expect(result.current.fields.text1.onChange).toBeDefined(); - - // change the input - const changedValue = ''; - act(() => { - result.current.fields.text1.onChange({ - target: { - value: changedValue - } - }); - }); - - // assert changed state - expect(result.current.fields.text1.changed).toBe(true); - expect(result.current.fields.text1.error).toBeTruthy(); - expect(result.current.fields.text1.type).toBe(fieldType); - expect(result.current.fields.text1.value).toBe(changedValue); - expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); - }); + it(`[hook] useForm. Verify the initial state`, async () => { + const initialFields: FormConfiguration = { + text1: { + type: 'text', + initialValue: '', + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe('text'); + expect(result.current.fields.text1.value).toBe(''); + expect(result.current.fields.text1.initialValue).toBe(''); + expect(result.current.fields.text1.onChange).toBeDefined(); + }); + + it(`[hook] useForm. Verify the initial state. Multiple fields.`, async () => { + const initialFields: FormConfiguration = { + text1: { + type: 'text', + initialValue: '', + }, + number1: { + type: 'number', + initialValue: 1, + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe('text'); + expect(result.current.fields.text1.value).toBe(''); + expect(result.current.fields.text1.initialValue).toBe(''); + expect(result.current.fields.text1.onChange).toBeDefined(); + + expect(result.current.fields.number1.changed).toBe(false); + expect(result.current.fields.number1.error).toBeUndefined(); + expect(result.current.fields.number1.type).toBe('number'); + expect(result.current.fields.number1.value).toBe(1); + expect(result.current.fields.number1.initialValue).toBe(1); + expect(result.current.fields.number1.onChange).toBeDefined(); + }); + + it(`[hook] useForm lifecycle. Set the initial value. Change the field value. Undo changes. Change the field. Do changes.`, async () => { + const initialFieldValue = ''; + const fieldType = 'text'; + + const initialFields: FormConfiguration = { + text1: { + type: fieldType, + initialValue: initialFieldValue, + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(initialFieldValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + expect(result.current.fields.text1.onChange).toBeDefined(); + + // change the input + const changedValue = 't'; + act(() => { + result.current.fields.text1.onChange({ + target: { + value: changedValue, + }, + }); + }); + + // assert changed state + expect(result.current.fields.text1.changed).toBe(true); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + + // undone changes + act(() => { + result.current.undoChanges(); + }); + + // assert undo changes state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(initialFieldValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + + // change the input + const changedValue2 = 'e'; + act(() => { + result.current.fields.text1.onChange({ + target: { + value: changedValue2, + }, + }); + }); + + // assert changed state + expect(result.current.fields.text1.changed).toBe(true); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue2); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + + // done changes + act(() => { + result.current.doChanges(); + }); + + // assert do changes state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue2); + expect(result.current.fields.text1.initialValue).toBe(changedValue2); + }); + + it(`[hook] useForm lifecycle. Set the initial value. Change the field value to invalid value`, async () => { + const initialFieldValue = 'test'; + const fieldType = 'text'; + + const initialFields: FormConfiguration = { + text1: { + type: fieldType, + initialValue: initialFieldValue, + validate: (value: string): string | undefined => + value.length ? undefined : `Validation error: string can be empty.`, + }, + }; + + const { result } = renderHook(() => useForm(initialFields)); + + // assert initial state + expect(result.current.fields.text1.changed).toBe(false); + expect(result.current.fields.text1.error).toBeUndefined(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(initialFieldValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + expect(result.current.fields.text1.onChange).toBeDefined(); + + // change the input + const changedValue = ''; + act(() => { + result.current.fields.text1.onChange({ + target: { + value: changedValue, + }, + }); + }); + + // assert changed state + expect(result.current.fields.text1.changed).toBe(true); + expect(result.current.fields.text1.error).toBeTruthy(); + expect(result.current.fields.text1.type).toBe(fieldType); + expect(result.current.fields.text1.value).toBe(changedValue); + expect(result.current.fields.text1.initialValue).toBe(initialFieldValue); + }); + + it('[hook] useForm. Verify the hook behavior when receives a custom field type', async () => { + const CustomComponent = (props: any) => { + const { onChange, field, initialValue } = props; + const [value, setValue] = useState(initialValue || ''); + + const handleOnChange = (e: any) => { + setValue(e.target.value); + onChange(e); + }; + + return ( + <> + {field} + + + ); + }; + + const formFields: FormConfiguration = { + customField: { + type: 'custom', + initialValue: 'default value', + component: props => CustomComponent(props), + }, + }; + + const { result } = renderHook(() => useForm(formFields)); + const { container, getByRole } = render( + , + ); + + expect(container).toBeInTheDocument(); + const input = getByRole('textbox'); + expect(input).toHaveValue('default value'); + fireEvent.change(input, { target: { value: 'new value' } }); + expect(result.current.fields.customField.component).toBeInstanceOf( + Function, + ); + expect(result.current.fields.customField.value).toBe('new value'); + }); }); diff --git a/plugins/main/public/components/common/form/hooks.tsx b/plugins/main/public/components/common/form/hooks.tsx index f3837b4b32..63ff8fdb72 100644 --- a/plugins/main/public/components/common/form/hooks.tsx +++ b/plugins/main/public/components/common/form/hooks.tsx @@ -1,91 +1,143 @@ import { useState, useRef } from 'react'; import { isEqual } from 'lodash'; import { EpluginSettingType } from '../../../../common/constants'; +import { + CustomSettingType, + EnhancedFields, + FormConfiguration, + SettingTypes, + UseFormReturn, +} from './types'; -function getValueFromEvent(event, type){ +interface IgetValueFromEventType { + [key: string]: (event: any) => any; +} + +/** + * Returns the value of the event according to the type of field + * When the type is not found, it returns the value defined in the default key + * + * @param event + * @param type + * @returns event value + */ +function getValueFromEvent( + event: any, + type: SettingTypes | CustomSettingType, +): any { return (getValueFromEventType[type] || getValueFromEventType.default)(event); -}; +} -const getValueFromEventType = { - [EpluginSettingType.switch] : (event: any) => event.target.checked, +const getValueFromEventType: IgetValueFromEventType = { + [EpluginSettingType.switch]: (event: any) => event.target.checked, [EpluginSettingType.editor]: (value: any) => value, [EpluginSettingType.filepicker]: (value: any) => value, + [EpluginSettingType.select]: (event: any) => event.target.value, + [EpluginSettingType.text]: (event: any) => event.target.value, + [EpluginSettingType.textarea]: (event: any) => event.target.value, + [EpluginSettingType.number]: (event: any) => event.target.value, + custom: (event: any) => event.target.value, default: (event: any) => event.target.value, }; -export const useForm = (fields) => { - const [formFields, setFormFields] = useState(Object.entries(fields).reduce((accum, [fieldKey, fieldConfiguration]) => ({ - ...accum, - [fieldKey]: { - currentValue: fieldConfiguration.initialValue, - initialValue: fieldConfiguration.initialValue, - } - }), {})); +export const useForm = (fields: FormConfiguration): UseFormReturn => { + const [formFields, setFormFields] = useState<{ + [key: string]: { currentValue: any; initialValue: any }; + }>( + Object.entries(fields).reduce( + (accum, [fieldKey, fieldConfiguration]) => ({ + ...accum, + [fieldKey]: { + currentValue: fieldConfiguration.initialValue, + initialValue: fieldConfiguration.initialValue, + }, + }), + {}, + ), + ); - const fieldRefs = useRef({}); + const fieldRefs = useRef<{ [key: string]: any }>({}); - const enhanceFields = Object.entries(formFields).reduce((accum, [fieldKey, {currentValue: value, ...restFieldState}]) => ({ - ...accum, - [fieldKey]: { - ...fields[fieldKey], - ...restFieldState, - type: fields[fieldKey].type, - value, - changed: !isEqual(restFieldState.initialValue, value), - error: fields[fieldKey]?.validate?.(value), - setInputRef: (reference) => {fieldRefs.current[fieldKey] = reference}, - inputRef: fieldRefs.current[fieldKey], - onChange: (event) => { - const inputValue = getValueFromEvent(event, fields[fieldKey].type); - const currentValue = fields[fieldKey]?.transformChangedInputValue?.(inputValue) ?? inputValue; - setFormFields(state => ({ - ...state, - [fieldKey]: { - ...state[fieldKey], - currentValue, - } - })) + const enhanceFields = Object.entries(formFields).reduce( + (accum, [fieldKey, { currentValue: value, ...restFieldState }]) => ({ + ...accum, + [fieldKey]: { + ...fields[fieldKey], + ...restFieldState, + type: fields[fieldKey].type, + value, + changed: !isEqual(restFieldState.initialValue, value), + error: fields[fieldKey]?.validate?.(value), + setInputRef: (reference: any) => { + fieldRefs.current[fieldKey] = reference; + }, + inputRef: fieldRefs.current[fieldKey], + onChange: (event: any) => { + const inputValue = getValueFromEvent(event, fields[fieldKey].type); + const currentValue = + fields[fieldKey]?.transformChangedInputValue?.(inputValue) ?? + inputValue; + setFormFields(state => ({ + ...state, + [fieldKey]: { + ...state[fieldKey], + currentValue, + }, + })); + }, }, - } - }), {}); + }), + {}, + ); const changed = Object.fromEntries( - Object.entries(enhanceFields).filter(([, {changed}]) => changed).map(([fieldKey, {value}]) => ([fieldKey, fields[fieldKey]?.transformChangedOutputValue?.(value) ?? value])) + Object.entries(enhanceFields as EnhancedFields) + .filter(([, { changed }]) => changed) + .map(([fieldKey, { value }]) => [ + fieldKey, + fields[fieldKey]?.transformChangedOutputValue?.(value) ?? value, + ]), ); const errors = Object.fromEntries( - Object.entries(enhanceFields).filter(([, {error}]) => error).map(([fieldKey, {error}]) => ([fieldKey, error])) + Object.entries(enhanceFields as EnhancedFields) + .filter(([, { error }]) => error) + .map(([fieldKey, { error }]) => [fieldKey, error]), ); - function undoChanges(){ - setFormFields(state => Object.fromEntries( - Object.entries(state).map(([fieldKey, fieldConfiguration]) => ([ - fieldKey, - { - ...fieldConfiguration, - currentValue: fieldConfiguration.initialValue - } - ])) - )); - }; + function undoChanges() { + setFormFields(state => + Object.fromEntries( + Object.entries(state).map(([fieldKey, fieldConfiguration]) => [ + fieldKey, + { + ...fieldConfiguration, + currentValue: fieldConfiguration.initialValue, + }, + ]), + ), + ); + } - function doChanges(){ - setFormFields(state => Object.fromEntries( - Object.entries(state).map(([fieldKey, fieldConfiguration]) => ([ - fieldKey, - { - ...fieldConfiguration, - initialValue: fieldConfiguration.currentValue - } - ])) - )); - }; + function doChanges() { + setFormFields(state => + Object.fromEntries( + Object.entries(state).map(([fieldKey, fieldConfiguration]) => [ + fieldKey, + { + ...fieldConfiguration, + initialValue: fieldConfiguration.currentValue, + }, + ]), + ), + ); + } return { fields: enhanceFields, changed, errors, undoChanges, - doChanges + doChanges, }; }; diff --git a/plugins/main/public/components/common/form/index.tsx b/plugins/main/public/components/common/form/index.tsx index 2a6da4610e..d10797ca0d 100644 --- a/plugins/main/public/components/common/form/index.tsx +++ b/plugins/main/public/components/common/form/index.tsx @@ -7,6 +7,31 @@ import { InputFormSwitch } from './input_switch'; import { InputFormFilePicker } from './input_filepicker'; import { InputFormTextArea } from './input_text_area'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { SettingTypes } from './types'; + +export interface InputFormProps { + type: SettingTypes; + value: any; + onChange: (event: React.ChangeEvent) => void; + error?: string; + label?: string | React.ReactNode; + header?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); + footer?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); + preInput?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); + postInput?: + | React.ReactNode + | ((props: { value: any; error?: string }) => React.ReactNode); +} + +interface InputFormComponentProps extends InputFormProps { + rest: any; +} export const InputForm = ({ type, @@ -18,13 +43,15 @@ export const InputForm = ({ footer, preInput, postInput, -...rest}) => { - - const ComponentInput = Input[type]; + ...rest +}: InputFormComponentProps) => { + const ComponentInput = Input[ + type as keyof typeof Input + ] as React.ComponentType; - if(!ComponentInput){ + if (!ComponentInput) { return null; - }; + } const isInvalid = Boolean(error); @@ -37,23 +64,25 @@ export const InputForm = ({ /> ); - return label - ? ( - - <> - {typeof header === 'function' ? header({value, error}) : header} - - {typeof preInput === 'function' ? preInput({value, error}) : preInput} - - {input} - - {typeof postInput === 'function' ? postInput({value, error}) : postInput} - - {typeof footer === 'function' ? footer({value, error}) : footer} - - ) - : input; - + return label ? ( + + <> + {typeof header === 'function' ? header({ value, error }) : header} + + {typeof preInput === 'function' + ? preInput({ value, error }) + : preInput} + {input} + {typeof postInput === 'function' + ? postInput({ value, error }) + : postInput} + + {typeof footer === 'function' ? footer({ value, error }) : footer} + + + ) : ( + input + ); }; const Input = { @@ -64,4 +93,5 @@ const Input = { select: InputFormSelect, text: InputFormText, textarea: InputFormTextArea, + custom: ({ component, ...rest }) => component(rest), }; diff --git a/plugins/main/public/components/common/form/input_select.tsx b/plugins/main/public/components/common/form/input_select.tsx index a8f02e99d7..b212f3f068 100644 --- a/plugins/main/public/components/common/form/input_select.tsx +++ b/plugins/main/public/components/common/form/input_select.tsx @@ -2,12 +2,26 @@ import React from 'react'; import { EuiSelect } from '@elastic/eui'; import { IInputFormType } from './types'; -export const InputFormSelect = ({ options, value, onChange }: IInputFormType) => { - return ( - - ) +export const InputFormSelect = ({ + options, + value, + onChange, + placeholder, + selectedOptions, + isDisabled, + isClearable, + dataTestSubj, +}: IInputFormType) => { + return ( + + ); }; diff --git a/plugins/main/public/components/common/form/input_text.tsx b/plugins/main/public/components/common/form/input_text.tsx index feb0d218ee..c8e3d730d4 100644 --- a/plugins/main/public/components/common/form/input_text.tsx +++ b/plugins/main/public/components/common/form/input_text.tsx @@ -1,14 +1,21 @@ import React from 'react'; import { EuiFieldText } from '@elastic/eui'; -import { IInputFormType } from "./types"; +import { IInputFormType } from './types'; -export const InputFormText = ({ value, isInvalid, onChange }: IInputFormType) => { - return ( - - ); +export const InputFormText = ({ + value, + isInvalid, + onChange, + placeholder, + fullWidth, +}: IInputFormType) => { + return ( + + ); }; diff --git a/plugins/main/public/components/common/form/types.ts b/plugins/main/public/components/common/form/types.ts index e064804790..762f73962f 100644 --- a/plugins/main/public/components/common/form/types.ts +++ b/plugins/main/public/components/common/form/types.ts @@ -1,19 +1,84 @@ -import { TPluginSettingWithKey } from "../../../../common/constants"; +import { TPluginSettingWithKey } from '../../../../common/constants'; export interface IInputFormType { - field: TPluginSettingWithKey - value: any - onChange: (event: any) => void - isInvalid?: boolean - options: any - setInputRef: (reference: any) => void -}; + field: TPluginSettingWithKey; + value: any; + onChange: (event: any) => void; + isInvalid?: boolean; + options: any; + setInputRef: (reference: any) => void; +} export interface IInputForm { - field: TPluginSettingWithKey - initialValue: any - onChange: (event: any) => void - label?: string - preInput?: ((options: {value: any, error: string | null}) => JSX.Element) - postInput?: ((options: {value: any, error: string | null}) => JSX.Element) -}; + field: TPluginSettingWithKey; + initialValue: any; + onChange: (event: any) => void; + label?: string; + preInput?: (options: { value: any; error: string | null }) => JSX.Element; + postInput?: (options: { value: any; error: string | null }) => JSX.Element; +} + +/// use form hook types + +export type SettingTypes = + | 'text' + | 'textarea' + | 'number' + | 'select' + | 'switch' + | 'editor' + | 'filepicker'; + +interface FieldConfiguration { + initialValue: any; + validate?: (value: any) => string | undefined; + transformChangedInputValue?: (value: any) => any; + transformChangedOutputValue?: (value: any) => any; +} + +export interface DefaultFieldConfiguration extends FieldConfiguration { + type: SettingTypes; +} + +export type CustomSettingType = 'custom'; +interface CustomFieldConfiguration extends FieldConfiguration { + type: CustomSettingType; + component: (props: any) => JSX.Element; +} + +export interface FormConfiguration { + [key: string]: DefaultFieldConfiguration | CustomFieldConfiguration; +} + +interface EnhancedField { + currentValue: any; + initialValue: any; + value: any; + changed: boolean; + error: string | null | undefined; + setInputRef: (reference: any) => void; + inputRef: any; + onChange: (event: any) => void; +} + +interface EnhancedDefaultField extends EnhancedField { + type: SettingTypes; +} + +interface EnhancedCustomField extends EnhancedField { + type: CustomSettingType; + component: (props: any) => JSX.Element; +} + +export type EnhancedFieldConfiguration = EnhancedDefaultField | EnhancedCustomField; +export interface EnhancedFields { + [key: string]: EnhancedFieldConfiguration; +} + +export interface UseFormReturn { + fields: EnhancedFields; + changed: { [key: string]: any }; + errors: { [key: string]: string }; + undoChanges: () => void; + doChanges: () => void; +} diff --git a/plugins/main/public/controllers/agent/index.js b/plugins/main/public/controllers/agent/index.js index c0fefdc072..51a445bb00 100644 --- a/plugins/main/public/controllers/agent/index.js +++ b/plugins/main/public/controllers/agent/index.js @@ -11,10 +11,10 @@ */ import { AgentsPreviewController } from './agents-preview'; import { AgentsController } from './agents'; -import { RegisterAgent } from './components/register-agent'; +import { RegisterAgent } from '../../controllers/register-agent/containers/register-agent/register-agent'; import { ExportConfiguration } from './components/export-configuration'; import { AgentsWelcome } from '../../components/common/welcome/agents-welcome'; -import { Mitre } from '../../components/overview' +import { Mitre } from '../../components/overview'; import { AgentsPreview } from './components/agents-preview'; import { AgentsTable } from './components/agents-table'; import { MainModule } from '../../components/common/modules/main'; diff --git a/plugins/main/public/controllers/agent/register-agent/steps/wz-manager-address.tsx b/plugins/main/public/controllers/agent/register-agent/steps/wz-manager-address.tsx index 0c46c70676..8bfd679e2f 100644 --- a/plugins/main/public/controllers/agent/register-agent/steps/wz-manager-address.tsx +++ b/plugins/main/public/controllers/agent/register-agent/steps/wz-manager-address.tsx @@ -11,14 +11,14 @@ const WzManagerAddressInput = (props: Props) => { const [value, setValue] = useState(''); useEffect(() => { - if(defaultValue){ + if (defaultValue) { setValue(defaultValue); onChange(defaultValue); - }else{ + } else { setValue(''); onChange(''); } - },[]) + }, []); /** * Handles the change of the selected node IP * @param value diff --git a/plugins/main/public/controllers/agent/wazuh-config/index.ts b/plugins/main/public/controllers/agent/wazuh-config/index.ts index f10c7994c1..c8bafbbe2a 100644 --- a/plugins/main/public/controllers/agent/wazuh-config/index.ts +++ b/plugins/main/public/controllers/agent/wazuh-config/index.ts @@ -108,7 +108,7 @@ const architectureButtonsMacos = [ }, { id: 'arm64', - label: 'Apple Silicon', + label: 'Apple silicon', }, ]; diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx new file mode 100644 index 0000000000..ce42d7aaa0 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -0,0 +1,103 @@ +import { + EuiCodeBlock, + EuiCopy, + EuiIcon, + EuiSpacer, + EuiSwitch, + EuiSwitchEvent, + EuiText, +} from '@elastic/eui'; +import React, { Fragment, useEffect, useState } from 'react'; +import { tOperatingSystem } from '../../core/config/os-commands-definitions'; + +interface ICommandSectionProps { + commandText: string; + showCommand: boolean; + onCopy: () => void; + os?: tOperatingSystem['name']; + password?: string; +} + +export default function CommandOutput(props: ICommandSectionProps) { + const { commandText, showCommand, onCopy, os, password } = props; + const getHighlightCodeLanguage = (os: 'WINDOWS' | string) => { + if (os.toLowerCase() === 'windows') { + return 'powershell'; + } else { + return 'bash'; + } + }; + const [havePassword, setHavePassword] = useState(false); + const [showPassword, setShowPassword] = useState(false); + + const onHandleCopy = (command: any) => { + onCopy && onCopy(); + return command; // the return is needed to avoid a bug in EuiCopy + }; + + const [commandToShow, setCommandToShow] = useState(commandText); + + useEffect(() => { + if (password) { + setHavePassword(true); + osdfucatePassword(password); + } else { + setHavePassword(false); + setCommandToShow(commandText); + } + }, [password, commandText, showPassword]) + + const osdfucatePassword = (password: string) => { + if(!password) return; + if(!commandText) return; + + if(showPassword){ + setCommandToShow(commandText); + }else{ + // search password in commandText and replace with * for every character + const findPassword = commandText.search(password); + if (findPassword > -1) { + let command = commandText; + setCommandToShow(command.replace(/WAZUH_REGISTRATION_PASSWORD='([^']+)'/,`WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`)); + } + } + } + + const onChangeShowPassword = (event: EuiSwitchEvent) => { + setShowPassword(event.target.checked); + } + + return ( + + + +
      + + {showCommand ? commandToShow : ''} + + {showCommand && ( + + {copy => ( +
      onHandleCopy(copy())} + > +

      + Copy command +

      +
      + )} +
      + )} +
      + + {showCommand && havePassword ? : null} +
      +
      + ); +} diff --git a/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss b/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss new file mode 100644 index 0000000000..575880c792 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/group-input/group-input.scss @@ -0,0 +1,5 @@ +.registerAgentLabels { + font-weight: 700; + font-size: 12px; + line-height: 20px; +} diff --git a/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx b/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx new file mode 100644 index 0000000000..e12c301850 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/group-input/group-input.tsx @@ -0,0 +1,102 @@ +import React, { Fragment, useState } from 'react'; +import { + EuiComboBox, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiButtonEmpty, + EuiLink, +} from '@elastic/eui'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +import './group-input.scss'; + +const popoverAgentGroup = ( + + Learn about{' '} + + Select a group. + + +); + +const GroupInput = ({ value, options, onChange }) => { + const [isPopoverAgentGroup, setIsPopoverAgentGroup] = useState(false); + + const onButtonAgentGroup = () => + setIsPopoverAgentGroup(isPopoverAgentGroup => !isPopoverAgentGroup); + const closeAgentGroup = () => setIsPopoverAgentGroup(false); + return ( + <> + + +

      + Select one or more existing groups +

      +
      + + + } + isOpen={isPopoverAgentGroup} + closePopover={closeAgentGroup} + anchorPosition='rightCenter' + > + {popoverAgentGroup} + + +
      + { + onChange({ + target: { value: group }, + }); + }} + isDisabled={!options?.groups.length} + isClearable={true} + data-test-subj='demoComboBox' + data-testid='group-input-combobox' + /> + {!options?.groups.length && ( + <> + + + )} + + ); +}; + +export default GroupInput; diff --git a/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx b/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx new file mode 100644 index 0000000000..317e3b6c41 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/optionals-inputs/optionals-inputs.tsx @@ -0,0 +1,110 @@ +import React, { Fragment, useState } from 'react'; +import { UseFormReturn } from '../../../../components/common/form/types'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPopover, + EuiButtonEmpty, + EuiCallOut, + EuiLink, +} from '@elastic/eui'; +import { InputForm } from '../../../../components/common/form'; +import { OPTIONAL_PARAMETERS_TEXT } from '../../utils/register-agent-data'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +import '../group-input/group-input.scss'; +interface OptionalsInputsProps { + formFields: UseFormReturn['fields']; +} + +const OptionalsInputs = (props: OptionalsInputsProps) => { + const { formFields } = props; + const [isPopoverAgentName, setIsPopoverAgentName] = useState(false); + const onButtonAgentName = () => + setIsPopoverAgentName(isPopoverAgentName => !isPopoverAgentName); + const closeAgentName = () => setIsPopoverAgentName(false); + const agentNameDocLink = webDocumentationLink( + 'user-manual/reference/ossec-conf/client.html#enrollment-agent-name', + PLUGIN_VERSION_SHORT, + ) + const popoverAgentName = ( + + Learn about{' '} + + Assigning an agent name. + + + ); + + const warningForAgentName = + 'The agent name must be unique. It can’t be changed once the agent has been enrolled.'; + return ( + + + {OPTIONAL_PARAMETERS_TEXT.map((data, index) => ( + + {data.subtitle} + + ))} + + + + +

      Assign an agent name

      +
      + + + } + isOpen={isPopoverAgentName} + closePopover={closeAgentName} + anchorPosition='rightCenter' + > + {popoverAgentName} + + +
      + + } + placeholder='Agent name' + /> + {warningForAgentName}} + iconType='iInCircle' + className='warningForAgentName' + /> + +
      + ); +}; + +export default OptionalsInputs; diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss new file mode 100644 index 0000000000..b8b985d165 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.scss @@ -0,0 +1,46 @@ +.checkbox-group-container { + display: grid; + grid-template-columns: 1fr 1fr; + margin-top: 26px; + justify-content: center; +} + +.checkbox-item { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: left; +} + +.checkbox-group-container.single-architecture { + margin-top: 44px; + display: flex; + justify-content: center; +} + +.checkbox-group-container.double-architecture { + margin-top: 24px; + display: flex; + flex-direction: column; + .checkbox-item:first-child { + margin-bottom: 13px; + } + .checkbox-item { + display: flex; + flex-direction: row-reverse; + justify-content: left; + align-self: baseline; + } +} + +.architecture-label { + margin-left: 8px; + font-style: normal; + font-weight: 400; + font-size: 12px; +} +.first-card-four-items { + .checkbox-item:nth-child(n + 3) { + padding-top: 16px; + } +} diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx new file mode 100644 index 0000000000..186fc0d240 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.test.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; + +describe('CheckboxGroupComponent', () => { + const data = ['Option 1', 'Option 2', 'Option 3']; + const cardIndex = 0; + const selectedOption = 'Option 1'; + const onOptionChange = jest.fn(); + + test('renders checkbox items with correct labels', () => { + render( + , + ); + + const checkboxItems = screen.getAllByRole('radio'); + expect(checkboxItems).toHaveLength(data.length); + + expect(checkboxItems[0]).toHaveAttribute('id', 'Option 1'); + expect(checkboxItems[1]).toHaveAttribute('id', 'Option 2'); + expect(checkboxItems[2]).toHaveAttribute('id', 'Option 3'); + + expect(checkboxItems[0]).toBeChecked(); + expect(checkboxItems[1]).not.toBeChecked(); + expect(checkboxItems[2]).not.toBeChecked(); + + expect(screen.getByText('Option 1')).toBeInTheDocument(); + expect(screen.getByText('Option 2')).toBeInTheDocument(); + expect(screen.getByText('Option 3')).toBeInTheDocument(); + }); + + test('calls onOptionChange when a checkbox is selected', () => { + render( + , + ); + + const checkboxItems = screen.getAllByRole('radio'); + + fireEvent.click(checkboxItems[1]); + + expect(onOptionChange).toHaveBeenCalledTimes(1); + expect(onOptionChange).toHaveBeenCalledWith( + expect.objectContaining({ + target: { value: `Option 2` }, + }), + ); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx new file mode 100644 index 0000000000..461e6e0943 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/os-selector/checkbox-group/checkbox-group.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { EuiRadioGroup } from '@elastic/eui'; +import './checkbox-group.scss'; + +interface Props { + data: string[]; + cardIndex: number; + selectedOption: string | undefined; + onOptionChange: (optionId: string) => void; + onChange: (id: string) => void; +} + +const CheckboxGroupComponent: React.FC = ({ + data, + cardIndex, + selectedOption, + onOptionChange, +}) => { + const isSingleArchitecture = data.length === 1; + const isDoubleArchitecture = data.length === 2; + const isFirstCardWithFourItems = cardIndex === 0 && data.length === 4; + return ( +
      + {data.map((arch, idx) => ( +
      + + { + onOptionChange({ target: { value: id } }); + }} + /> +
      + ))} +
      + ); +}; + +export { CheckboxGroupComponent }; diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.scss b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.scss new file mode 100644 index 0000000000..55dd4092fa --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.scss @@ -0,0 +1,59 @@ +.card { + height: 183px; + + label { + cursor: pointer; + } +} + +.cardTitle { + display: flex; + align-items: center; + margin-top: 28px; + justify-content: center; + user-select: none; +} + +.cardIcon { + margin-right: 10px; +} + +.euiCard__content .euiCard__titleButton { + text-decoration: none !important; +} + +.cardText { + font-style: normal; + font-weight: 700; + font-size: 18px; + display: flex; + align-items: center; + text-align: center; + letter-spacing: 0.6px; +} + +.hr { + border: 1px solid #d3dae6; +} + +.cardContent { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.checkboxGroupContainer { + flex-basis: 50%; +} + +.architectureItem { + margin-bottom: 8px; +} + +.last-card { + margin-right: 63px; +} + +.cardsCallOut { + margin-top: 16px; +} diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx new file mode 100644 index 0000000000..d01a27c141 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.test.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import { OsCard } from '../os-card/os-card'; + +jest.mock('../../../../../kibana-services', () => ({ + ...(jest.requireActual('../../../../../kibana-services') as object), + getHttp: jest.fn().mockReturnValue({ + basePath: { + get: () => { + return 'http://localhost:5601'; + }, + prepend: (url) => { + return `http://localhost:5601${url}`; + }, + }, + }), + getCookies: jest.fn().mockReturnValue({ + set: (name, value, options) => { + return true; + }, + get: () => { + return '{}'; + }, + remove: () => { + return; + }, + }), + getUiSettings: jest.fn().mockReturnValue({ + get: (name) => { + return true; + }, + }), +})); + +describe('OsCard', () => { + test('renders three cards with different titles', () => { + render(); + + const cardTitles = screen.getAllByTestId('card-title'); + expect(cardTitles).toHaveLength(3); + + expect(cardTitles[0]).toHaveTextContent('LINUX'); + expect(cardTitles[1]).toHaveTextContent('WINDOWS'); + expect(cardTitles[2]).toHaveTextContent('macOS'); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.tsx b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.tsx new file mode 100644 index 0000000000..1e88422e5b --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/os-selector/os-card/os-card.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiCallOut, + EuiLink, + EuiCheckbox, +} from '@elastic/eui'; +import { OPERATING_SYSTEMS_OPTIONS } from '../../../utils/register-agent-data'; +import { CheckboxGroupComponent } from '../checkbox-group/checkbox-group'; +import './os-card.scss'; +import { webDocumentationLink } from '../../../../../../common/services/web_documentation'; + +interface Props { + setStatusCheck: string; + onChange: React.ChangeEventHandler; + value: any; +} + +export const OsCard = ({ onChange, value }: Props) => { + return ( +
      + + {OPERATING_SYSTEMS_OPTIONS.map((data, index) => ( + + + Icon + {data.title} +
      + } + display='plain' + hasBorder + className='card' + > + {data.hr &&
      } + + + + ))} + + + For additional systems and architectures, please check our{' '} + + documentation + + . + + } + > +
      + ); +}; diff --git a/plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx b/plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx new file mode 100644 index 0000000000..8b9a981e6d --- /dev/null +++ b/plugins/main/public/controllers/register-agent/components/server-address/server-address.tsx @@ -0,0 +1,103 @@ +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPopover, + EuiButtonEmpty, + EuiLink, +} from '@elastic/eui'; +import React, { Fragment, useState } from 'react'; +import { SERVER_ADDRESS_TEXTS } from '../../utils/register-agent-data'; +import { EnhancedFieldConfiguration } from '../../../../components/common/form/types'; +import { InputForm } from '../../../../components/common/form'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; +import { PLUGIN_VERSION_SHORT } from '../../../../../common/constants'; +import '../group-input/group-input.scss'; + +interface ServerAddressInputProps { + formField: EnhancedFieldConfiguration; +} + +const popoverServerAddress = ( + + Learn about{' '} + + Server address. + + +); + +const ServerAddressInput = (props: ServerAddressInputProps) => { + const { formField } = props; + const [isPopoverServerAddress, setIsPopoverServerAddress] = useState(false); + const onButtonServerAddress = () => + setIsPopoverServerAddress( + isPopoverServerAddress => !isPopoverServerAddress, + ); + const closeServerAddress = () => setIsPopoverServerAddress(false); + + return ( + + + {SERVER_ADDRESS_TEXTS.map((data, index) => ( + + + {data.subtitle} + + + ))} + + + + + + Assign a server address + + + + + } + isOpen={isPopoverServerAddress} + closePopover={closeServerAddress} + anchorPosition='rightCenter' + > + {popoverServerAddress} + + + + + } + fullWidth={false} + placeholder='Server address' + /> + + ); +}; + +export default ServerAddressInput; diff --git a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.scss b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.scss new file mode 100644 index 0000000000..f51d4384e0 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.scss @@ -0,0 +1,27 @@ +.register-agent-wizard-container { + box-sizing: border-box; + min-height: 1271px; + margin-top: 44px; + background: #ffffff; + border: 1px solid rgba(52, 55, 65, 0.2); + max-width: 1030px; + padding-left: 72px; + padding-right: 63px; +} + +.register-agent-wizard-title { + margin-top: 51px; + margin-bottom: 51px; + font-style: normal; + font-weight: 400; + font-size: 30px; + line-height: 36px; + display: flex; + justify-content: center; +} + +.register-agent-wizard-close { + display: flex; + margin-top: 17px; + float: right; +} diff --git a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx new file mode 100644 index 0000000000..8ae23213cd --- /dev/null +++ b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -0,0 +1,242 @@ +import React, { useState, useEffect } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiTitle, + EuiButtonEmpty, + EuiPage, + EuiPageBody, + EuiSpacer, + EuiProgress, + EuiButton, +} from '@elastic/eui'; +import { WzRequest } from '../../../../react-services/wz-request'; +import { UI_LOGGER_LEVELS } from '../../../../../common/constants'; +import { UI_ERROR_SEVERITIES } from '../../../../react-services/error-orchestrator/types'; +import { ErrorHandler } from '../../../../react-services/error-management'; +import { getMasterRemoteConfiguration } from '../../../agent/components/register-agent-service'; +import './register-agent.scss'; +import { Steps } from '../steps/steps'; +import { InputForm } from '../../../../components/common/form'; +import { getGroups } from '../../services/register-agent-services'; +import { useForm } from '../../../../components/common/form/hooks'; +import { FormConfiguration } from '../../../../components/common/form/types'; +import { useSelector } from 'react-redux'; +import { withReduxProvider } from '../../../../components/common/hocs'; +import GroupInput from '../../components/group-input/group-input'; +import { OsCard } from '../../components/os-selector/os-card/os-card'; +import { + validateServerAddress, + validateAgentName, +} from '../../utils/validations'; + +interface IRegisterAgentProps { + getWazuhVersion: () => Promise; + hasAgents: () => Promise; + addNewAgent: (agent: any) => Promise; + reload: () => void; +} + +export const RegisterAgent = withReduxProvider( + ({ + getWazuhVersion, + hasAgents, + addNewAgent, + reload, + }: IRegisterAgentProps) => { + const configuration = useSelector( + (state: { appConfig: { data: any } }) => state.appConfig.data, + ); + const [wazuhVersion, setWazuhVersion] = useState(''); + const [haveUdpProtocol, setHaveUdpProtocol] = useState( + false, + ); + const [loading, setLoading] = useState(false); + const [wazuhPassword, setWazuhPassword] = useState(''); + const [groups, setGroups] = useState([]); + const [needsPassword, setNeedsPassword] = useState(false); + + const initialFields: FormConfiguration = { + operatingSystemSelection: { + type: 'custom', + initialValue: '', + component: props => { + return ; + }, + options: { + groups, + }, + }, + serverAddress: { + type: 'text', + initialValue: configuration['enrollment.dns'] || '', + validate: validateServerAddress, + }, + agentName: { + type: 'text', + initialValue: '', + validate: validateAgentName, + }, + + agentGroups: { + type: 'custom', + initialValue: [], + component: props => { + return ; + }, + options: { + groups, + }, + }, + }; + + const form = useForm(initialFields); + + const getRemoteConfig = async () => { + const remoteConfig = await getMasterRemoteConfiguration(); + if (remoteConfig) { + setHaveUdpProtocol(remoteConfig.isUdp); + } + }; + + const getAuthInfo = async () => { + try { + const result = await WzRequest.apiReq( + 'GET', + '/agents/000/config/auth/auth', + {}, + ); + return (result.data || {}).data || {}; + } catch (error) { + ErrorHandler.handleError(error); + } + }; + + useEffect(() => { + const fetchData = async () => { + try { + const wazuhVersion = await getWazuhVersion(); + await getRemoteConfig(); + const authInfo = await getAuthInfo(); + // get wazuh password configuration + let wazuhPassword = ''; + const needsPassword = (authInfo.auth || {}).use_password === 'yes'; + if (needsPassword) { + wazuhPassword = + configuration['enrollment.password'] || + authInfo['authd.pass'] || + ''; + } + const groups = await getGroups(); + setNeedsPassword(needsPassword); + setWazuhPassword(wazuhPassword); + setWazuhVersion(wazuhVersion); + setGroups(groups); + setLoading(false); + } catch (error) { + setWazuhVersion(wazuhVersion); + setLoading(false); + const options = { + context: 'RegisterAgent', + level: UI_LOGGER_LEVELS.ERROR, + severity: UI_ERROR_SEVERITIES.BUSINESS, + display: true, + store: false, + error: { + error: error, + message: error.message || error, + title: error.name || error, + }, + }; + ErrorHandler.handleError(error, options); + } + }; + + fetchData(); + }, []); + + const osCard = ( + + ); + + return ( +
      + + + + + +
      + {hasAgents() ? ( + addNewAgent(false)} + iconType='cross' + > + Close + + ) : ( + reload()} + iconType='refresh' + > + Refresh + + )} +
      + + + +

      + Deploy new agent +

      +
      +
      +
      + + {loading ? ( + <> + + + + + + ) : ( + + + + )} + + + reload()} + > + Close + + + +
      +
      +
      +
      +
      +
      + ); + }, +); diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.scss b/plugins/main/public/controllers/register-agent/containers/steps/steps.scss new file mode 100644 index 0000000000..337cc41298 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.scss @@ -0,0 +1,55 @@ +.register-agent-wizard-container { + .euiStep__title { + font-style: normal; + font-weight: 700; + font-size: 16px; + letter-spacing: 0.6px; + flex-direction: row; + } +} + +.stepSubtitleServerAddress { + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 24px; + margin-bottom: 9px; +} + +.stepSubtitle { + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 24px; + margin-bottom: 20px; +} + +.titleAndIcon { + display: flex; + flex-direction: row; +} + +.warningForAgentName { + margin-top: 10px; +} + +.euiToolTipAnchor { + margin-left: 7px; +} + +.subtitleAgentName { + flex-direction: 'row'; + font-style: 'normal'; + font-weight: 700; + font-size: '12px'; + line-height: '20px'; + color: '#343741'; +} + +.euiStep__titleWrapper { + align-items: center; +} + +.euiButtonEmpty .euiButtonEmpty__content { + padding: 0; +} diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx new file mode 100644 index 0000000000..596e282e86 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx @@ -0,0 +1,258 @@ +import React, { Fragment, useEffect, useState } from 'react'; +import { EuiCallOut, EuiLink, EuiSteps, EuiTitle } from '@elastic/eui'; +import './steps.scss'; +import { OPERATING_SYSTEMS_OPTIONS } from '../../utils/register-agent-data'; +import { + IParseRegisterFormValues, + getRegisterAgentFormValues, + parseRegisterAgentFormValues, +} from '../../services/register-agent-services'; + +import { useRegisterAgentCommands } from '../../hooks/use-register-agent-commands'; +import { + osCommandsDefinitions, + optionalParamsDefinitions, + tOperatingSystem, + tOptionalParameters, +} from '../../core/config/os-commands-definitions'; +import { UseFormReturn } from '../../../../components/common/form/types'; +import CommandOutput from '../../components/command-output/command-output'; +import ServerAddress from '../../components/server-address/server-address'; +import OptionalsInputs from '../../components/optionals-inputs/optionals-inputs'; +import { + getAgentCommandsStepStatus, + tFormStepsStatus, + getOSSelectorStepStatus, + getServerAddressStepStatus, + getOptionalParameterStepStatus, + showCommandsSections, + getPasswordStepStatus, + getIncompleteSteps, + getInvalidFields, + tFormFieldsLabel, + tFormStepsLabel, +} from '../../services/register-agent-steps-status-services'; +import { webDocumentationLink } from '../../../../../common/services/web_documentation'; + +interface IStepsProps { + needsPassword: boolean; + form: UseFormReturn; + osCard: React.ReactElement; + connection: { + isUDP: boolean; + }; + wazuhPassword: string; +} + +export const Steps = ({ + needsPassword, + form, + osCard, + connection, + wazuhPassword, +}: IStepsProps) => { + const initialParsedFormValues = { + operatingSystem: { + name: '', + architecture: '', + }, + optionalParams: { + agentGroups: '', + agentName: '', + serverAddress: '', + wazuhPassword, + protocol: connection.isUDP ? 'UDP' : '', + }, + } as IParseRegisterFormValues; + const [missingStepsName, setMissingStepsName] = useState( + [], + ); + const [invalidFieldsName, setInvalidFieldsName] = useState< + tFormFieldsLabel[] + >([]); + const [registerAgentFormValues, setRegisterAgentFormValues] = + useState(initialParsedFormValues); + + const FORM_MESSAGE_CONJUNTION = ' and '; + + useEffect(() => { + // get form values and parse them divided in OS and optional params + const registerAgentFormValuesParsed = parseRegisterAgentFormValues( + getRegisterAgentFormValues(form), + OPERATING_SYSTEMS_OPTIONS, + initialParsedFormValues, + ); + setRegisterAgentFormValues(registerAgentFormValuesParsed); + setInstallCommandStepStatus( + getAgentCommandsStepStatus(form.fields, installCommandWasCopied), + ); + setStartCommandStepStatus( + getAgentCommandsStepStatus(form.fields, startCommandWasCopied), + ); + setMissingStepsName(getIncompleteSteps(form.fields) || []); + setInvalidFieldsName(getInvalidFields(form.fields) || []); + }, [form.fields]); + + const { installCommand, startCommand, selectOS, setOptionalParams } = + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }); + + // install - start commands step state + const [installCommandWasCopied, setInstallCommandWasCopied] = useState(false); + const [installCommandStepStatus, setInstallCommandStepStatus] = + useState(getAgentCommandsStepStatus(form.fields, false)); + const [startCommandWasCopied, setStartCommandWasCopied] = useState(false); + const [startCommandStepStatus, setStartCommandStepStatus] = + useState(getAgentCommandsStepStatus(form.fields, false)); + + useEffect(() => { + if ( + registerAgentFormValues.operatingSystem.name !== '' && + registerAgentFormValues.operatingSystem.architecture !== '' + ) { + selectOS(registerAgentFormValues.operatingSystem as tOperatingSystem); + } + setOptionalParams({ ...registerAgentFormValues.optionalParams }); + setInstallCommandWasCopied(false); + setStartCommandWasCopied(false); + }, [registerAgentFormValues]); + + useEffect(() => { + setInstallCommandStepStatus( + getAgentCommandsStepStatus(form.fields, installCommandWasCopied), + ); + }, [installCommandWasCopied]); + + useEffect(() => { + setStartCommandStepStatus( + getAgentCommandsStepStatus(form.fields, startCommandWasCopied), + ); + }, [startCommandWasCopied]); + + const registerAgentFormSteps = [ + { + title: 'Select the package to download and install on your system:', + children: osCard, + status: getOSSelectorStepStatus(form.fields), + }, + { + title: 'Server address', + children: , + status: getServerAddressStepStatus(form.fields), + }, + ...(needsPassword && !wazuhPassword + ? [ + { + title: 'Wazuh password', + children: ( + + The Wazuh password is required but wasn't defined. Please + check our{' '} + + documentation + + + } + iconType='iInCircle' + className='warningForAgentName' + /> + ), + status: getPasswordStepStatus(form.fields), + }, + ] + : []), + { + title: 'Optional settings', + children: , + status: getOptionalParameterStepStatus( + form.fields, + installCommandWasCopied, + ), + }, + { + title: + 'Run the following commands to download and install the Wazuh agent:', + children: ( + <> + {missingStepsName?.length ? ( + + ) : null} + {invalidFieldsName?.length ? ( + + ) : null} + {!missingStepsName?.length && !invalidFieldsName?.length ? ( + setInstallCommandWasCopied(true)} + password={registerAgentFormValues.optionalParams.wazuhPassword} + /> + ) : null} + + ), + status: installCommandStepStatus, + }, + { + title: 'Start the Wazuh agent:', + children: ( + <> + {missingStepsName?.length ? ( + + ) : null} + {invalidFieldsName?.length ? ( + + ) : null} + {!missingStepsName?.length && !invalidFieldsName?.length ? ( + setStartCommandWasCopied(true)} + /> + ) : null} + + ), + status: startCommandStepStatus, + }, + ]; + + return ; +}; diff --git a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts new file mode 100644 index 0000000000..1391c825a6 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -0,0 +1,189 @@ +import { + getDEBInstallCommand, + getRPMInstallCommand, + getLinuxStartCommand, + getMacOsInstallCommand, + getMacosStartCommand, + getWindowsInstallCommand, + getWindowsStartCommand } from '../../services/register-agent-os-commands-services'; +import { IOSDefinition, tOptionalParams } from '../register-commands/types'; + +// Defined OS combinations + +/** Linux options **/ +export interface ILinuxAMDRPM { + name: 'LINUX'; + architecture: 'RPM amd64'; +} + +export interface ILinuxAARCHRPM { + name: 'LINUX'; + architecture: 'RPM aarch64'; +} + +export interface ILinuxAMDDEB { + name: 'LINUX'; + architecture: 'DEB amd64'; +} + +export interface ILinuxAARCHDEB { + name: 'LINUX'; + architecture: 'DEB aarch64'; +} + +type ILinuxOSTypes = + | ILinuxAMDRPM + | ILinuxAARCHRPM + | ILinuxAMDDEB + | ILinuxAARCHDEB; + +/** Windows options **/ +export interface IWindowsOSTypes { + name: 'WINDOWS'; + architecture: 'MSI 32/64 bits'; +} + +/** MacOS options **/ +export interface IMacOSIntel { + name: 'macOS'; + architecture: 'Intel'; +} + +export interface IMacOSApple { + name: 'macOS'; + architecture: 'Apple silicon'; +} + +type IMacOSTypes = IMacOSApple | IMacOSIntel; + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +export type tOptionalParameters = + | 'serverAddress' + | 'agentName' + | 'agentGroups' + | 'wazuhPassword' + | 'protocol'; + +/////////////////////////////////////////////////////////////////// +/// Operating system commands definitions +/////////////////////////////////////////////////////////////////// + +const linuxDefinition: IOSDefinition = { + name: 'LINUX', + options: [ + { + architecture: 'DEB amd64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64.deb`, + installCommand: props => getDEBInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), + }, + { + architecture: 'DEB aarch64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_${props.wazuhVersion}-1_amd64.deb`, + installCommand: props => getDEBInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), + }, + { + architecture: 'RPM amd64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.rpm`, + installCommand: props => getRPMInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), + }, + { + architecture: 'RPM aarch64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64.rpm`, + installCommand: props => getRPMInstallCommand(props), + startCommand: props => getLinuxStartCommand(props), + }, + ], +}; + +const windowsDefinition: IOSDefinition = { + name: 'WINDOWS', + options: [ + { + architecture: 'MSI 32/64 bits', + urlPackage: props => + `https://packages.wazuh.com/4.x/windows/wazuh-agent-${props.wazuhVersion}-1.msi`, + installCommand: props => getWindowsInstallCommand(props), + startCommand: props => getWindowsStartCommand(props), + }, + ], +}; + +const macDefinition: IOSDefinition = { + name: 'macOS', + options: [ + { + architecture: 'Intel', + urlPackage: props => + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1.intel64.pkg`, + installCommand: props => getMacOsInstallCommand(props), + startCommand: props => getMacosStartCommand(props), + }, + { + architecture: 'Apple silicon', + urlPackage: props => + `https://packages.wazuh.com/4.x/macos/wazuh-agent-${props.wazuhVersion}-1.arm64.pkg`, + installCommand: props => getMacOsInstallCommand(props), + startCommand: props => getMacosStartCommand(props), + }, + ], +}; + +export const osCommandsDefinitions = [ + linuxDefinition, + windowsDefinition, + macDefinition, +]; + +/////////////////////////////////////////////////////////////////// +/// Optional parameters definitions +/////////////////////////////////////////////////////////////////// + +export const optionalParamsDefinitions: tOptionalParams = { + serverAddress: { + property: 'WAZUH_MANAGER', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, + agentName: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, + agentGroups: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: props => { + const { property, value } = props; + let parsedValue = value; + if (Array.isArray(value)) { + parsedValue = value.length > 0 ? value.join(',') : ''; + } + return parsedValue ? `${property}='${parsedValue}'` : ''; + }, + }, + protocol: { + property: 'WAZUH_PROTOCOL', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, + wazuhPassword: { + property: 'WAZUH_REGISTRATION_PASSWORD', + getParamCommand: props => { + const { property, value } = props; + return value !== '' ? `${property}='${value}'` : ''; + }, + }, +}; diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/README.md b/plugins/main/public/controllers/register-agent/core/register-commands/README.md new file mode 100644 index 0000000000..02e7600561 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/README.md @@ -0,0 +1,327 @@ +# Documentation + +- [Register Agent](#register-agent) + - [Solution details](#solution-details) + - [Configuration details](#configuration-details) + - [OS Definitions](#os-definitions) + - [Configuration example](#configuration-example) + - [Validations](#validations) + - [Optional Parameters Configuration](#optional-parameters-configuration) + - [Configuration example](#configuration-example-1) + - [Validations](#validations-1) + - [Command Generator](#command-generator) + - [Get install command](#get-install-command) + - [Get start command](#get-start-command) + - [Get url package](#get-url-package) + - [Get all commands](#get-all-commands) + +# Register Agent + +The register agent is a process that will allow the user to register an agent in the Wazuh Manager. The plugin will provide a form where the user will be able to select the OS and the package that he wants to install. The plugin will generate the registration commands and will show them to the user. + +# Solution details + +To optimize and make more easier the process to generate the registration commands we have created a class called `Command Generator` that given a set of parameters it will generate the registration commands. + +## Configuration + +To make the command generator works we need to configure the following parameters and pass them to the class: + +## OS Definitions + +The OS definitions are a set of parameters that will be used to generate the registration commands. The parameters are the following: + +```ts + + +// global types + +export interface IOptionsParamConfig { + property: string; + getParamCommand: (props: tOptionalParamsCommandProps) => string; +} + +export type tOptionalParams = { + [key in T]: IOptionsParamConfig; +}; + +export interface IOperationSystem { + name: string; + architecture: string; +} + +/// .... + +interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; +} +interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; +} + +interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; +} + +type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; // add the necessary OS options + +type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +export interface IOSDefinition { + name: OS['name']; + options: IOSCommandsDefinition[]; +} + +export interface IOSCommandsDefinition { + architecture: OS['architecture']; + urlPackage: (props: tOSEntryProps) => string; + installCommand: (props: tOSEntryProps & { urlPackage: string }) => string; + startCommand: (props: tOSEntryProps) => string; +} + +``` + +This configuration will define the different OS that we want to support and the different packages that we want to support for each OS. The `urlPackage` function will be used to generate the URL to download the package, the `installCommand` function will be used to generate the command to install the package and the `startCommand` function will be used to generate the command to start the agent. + +### Configuration example + +```ts + +const osDefinitions: IOSDefinition[] = [{ + name: 'linux', + options: [ + { + architecture: 'amd64', + urlPackage: props => 'add url package', + installCommand: props => 'add install command', + startCommand: props => `add start command`, + }, + { + architecture: 'amd64', + urlPackage: props => 'add url package', + installCommand: props => 'add install command', + startCommand: props => `add start command`, + } + ], +}, +{ + name: 'windows', + options: [ + { + architecture: '32/64', + urlPackage: props => 'add url package', + installCommand: props => 'add install command', + startCommand: props => `add start command`, + }, + ], + } +}; +``` + +## Validations + +The `Command Generator` will validate the OS Definitions received and will throw an error if the configuration is not valid. The validations are the following: + +- The OS Definitions must not have duplicated OS names defined. +- The OS Definitions must not have duplicated options defined. +- The OS names would be defined in the `tOS` type. +- The Package Extensions would be defined in the `tPackageExtensions` type. + +Another validations will be provided in development time and will be provided by the types added to the project. You can find the types definitions in the `types` file. + + +## Optional Parameters Configuration + +The optional parameters are a set of parameters that will be added to the registration commands. The parameters are the following: + +```ts + +export type tOptionalParamsName = + | 'server_address' + | 'agent_name' + | 'protocol' + | 'agent_group' + | 'wazuh_password'; + +export type tOptionalParams = { + [key in tOptionalParamsName]: { + property: string; + getParamCommand: (props) => string; + }; +} + +``` + +This configuration will define the different optional parameters that we want to support and the way how to we will process and show in the commands.The `getParamCommand` is the function that will process the props received and show the final command format. + +### Configuration example + +```ts + +export const optionalParameters: tOptionalParams = { + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: props => 'returns the optional param command' + } + }, + any_other: { + property: 'PARAM NAME IN THE COMMAND', + getParamCommand: props => 'returns the optional param command' + }, +} + +``` + +## Validations + +The `Command Generator` will validate the Optional Parameters received and will throw an error if the configuration is not valid. The validations are the following: + +- The Optional Parameters must not have duplicated names defined. +- The Optional Parameters name would be defined in the `tOptionalParamsName` type. + + +Another validations will be provided in development time and will be provided by the types added to the project. You can find the types definitions in the `types` file. + + +## Command Generator + +To use the command generator we need to import the class and create a new instance of the class. The class will receive the OS Definitions and the Optional Parameters as parameters. + +```ts +import { CommandGenerator } from 'path/command-generator'; + +// Commange Generator interface/contract + +export interface ICommandGenerator extends ICommandGeneratorMethods { + osDefinitions: IOSDefinition[]; + wazuhVersion: string; +} + +export interface ICommandGeneratorMethods { + selectOS(params: IOperationSystem): void; + addOptionalParams(props: IOptionalParameters): void; + getInstallCommand(): string; + getStartCommand(): string; + getUrlPackage(): string; + getAllCommands(): ICommandsResponse; +} + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +``` + +When the class is created the definitions provided will be validated and if the configuration is not valid an error will be thrown. The errors were mentioned in the configurations `Validations` section. + +### Get install command + +To generate the install command we need to call the `getInstallComand` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested command. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', +}); + +// get install command +const installCommand = commandGenerator.getInstallCommand(); + +``` + +The `Command Generator` will search the OS provided and search in the OS Definitions and will process the command using the `installCommand` function defined in the OS Definition. If the OS is not found an error will be thrown. +If the `getInstallCommand` but the OS is not selected an error will be thrown. + +## Get start command + +To generate the install command we need to call the `getStartCommand` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested command. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', +}); + +// get start command +const installCommand = commandGenerator.getStartCommand(); + +``` + +## Get url package + +To generate the install command we need to call the `getUrlPackage` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested command. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', +}); + +const urlPackage = commandGenerator.getUrlPackage(); + +``` + +## Get all commands + +To generate the install command we need to call the `getAllCommands` function. To perform this function the `Command Generator` must receive the OS name and/or the optional parameters as parameters before. The function will return the requested commands. + +```ts + +import { CommandGenerator } from 'path/command-generator'; + +const commandGenerator = new CommandGenerator(osDefinitions, optionalParameters); + +// specify to the command generator the OS that we want to use +commandGenerator.selectOS({ + name: 'linux', + architecture: 'amd64', +}); + +// specify to the command generator the optional parameters that we want to use +commandGenerator.addOptionalParams({ + server_address: 'server-ip', + agent_name: 'agent-name', + any_parameter: 'any-value' +}); + +// get all commands +const installCommand = commandGenerator.getAllCommands(); + +``` + +If we specify the optional parameters the `Command Generator` will process the commands and will add the optional parameters to the commands. The optional parameters processing will be only applied to the commands that have the optional parameters defined in the Optional Parameters Definitions. If the OS Definition does not have the optional parameters defined the `Command Generator` will ignore the optional parameters. + +### getAllComands output + +```ts + +export interface ICommandsResponse { + wazuhVersion: string; + os: string; + architecture: string; + url_package: string; + install_command: string; + start_command: string; + optionals: IOptionalParameters | object; +} + +``` \ No newline at end of file diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts b/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts new file mode 100644 index 0000000000..ae9af6d24e --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.test.ts @@ -0,0 +1,380 @@ +import { CommandGenerator } from './command-generator'; +import { + IOSDefinition, + IOptionalParameters, + tOptionalParams, +} from '../types'; +import { DuplicatedOSException, DuplicatedOSOptionException, NoOSSelectedException, WazuhVersionUndefinedException } from '../exceptions'; + +const mockedCommandValue = 'mocked command'; +const mockedCommandsResponse = jest.fn().mockReturnValue(mockedCommandValue); + +// Defined OS combinations +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +// Defined Optional Parameters + + +export type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + { + architecture: 'x86', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, +]; + +const optionalParams: tOptionalParams = { + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: props => `${props.property}=${props.value}`, + }, + agent_name: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => `${props.property}=${props.value}`, + }, + protocol: { + property: 'WAZUH_MANAGER_PROTOCOL', + getParamCommand: props => `${props.property}=${props.value}`, + }, + agent_group: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: props => `${props.property}=${props.value}`, + }, + wazuh_password: { + property: 'WAZUH_PASSWORD', + getParamCommand: props => `${props.property}=${props.value}`, + }, +}; + +const optionalValues: IOptionalParameters = { + server_address: '', + agent_name: '', + protocol: '', + agent_group: '', + wazuh_password: '', +}; + +describe('Command Generator', () => { + it('should create an valid instance', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + expect(commandGenerator).toBeDefined(); + }); + + it('should return the install command for the os selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + commandGenerator.selectOS({ + name: 'linux', + architecture: 'x64', + }); + commandGenerator.addOptionalParams(optionalValues); + const command = commandGenerator.getInstallCommand(); + expect(command).toBe(mockedCommandValue); + }); + + it('should return the start command for the os selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + commandGenerator.selectOS({ + name: 'linux', + architecture: 'x64', + }); + commandGenerator.addOptionalParams(optionalValues); + const command = commandGenerator.getStartCommand(); + expect(command).toBe(mockedCommandValue); + }); + + it('should return all the commands for the os selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + commandGenerator.selectOS({ + name: 'linux', + architecture: 'x64', + }); + commandGenerator.addOptionalParams(optionalValues); + const commands = commandGenerator.getAllCommands(); + expect(commands).toEqual({ + os: 'linux', + architecture: 'x64', + wazuhVersion: '4.4', + install_command: mockedCommandValue, + start_command: mockedCommandValue, + url_package: mockedCommandValue, + optionals: {}, + }); + }); + + it('should return commands with the filled optional params', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + + const selectedOs: tOperatingSystem = { + name: 'linux', + architecture: 'x64', + }; + commandGenerator.selectOS(selectedOs); + + const optionalValues = { + server_address: '10.10.10.121', + agent_name: 'agent1', + protocol: 'tcp', + agent_group: '', + wazuh_password: '123456', + }; + commandGenerator.addOptionalParams(optionalValues); + + const commands = commandGenerator.getAllCommands(); + expect(commands).toEqual({ + os: selectedOs.name, + architecture: selectedOs.architecture, + wazuhVersion: '4.4', + install_command: mockedCommandValue, + start_command: mockedCommandValue, + url_package: mockedCommandValue, + optionals: { + server_address: optionalParams.server_address.getParamCommand({ + property: optionalParams.server_address.property, + value: optionalValues.server_address, + name: 'server_address', + }), + agent_name: optionalParams.agent_name.getParamCommand({ + property: optionalParams.agent_name.property, + value: optionalValues.agent_name, + name: 'agent_name', + }), + protocol: optionalParams.protocol.getParamCommand({ + property: optionalParams.protocol.property, + value: optionalValues.protocol, + name: 'protocol', + }), + wazuh_password: optionalParams.wazuh_password.getParamCommand({ + property: optionalParams.wazuh_password.property, + value: optionalValues.wazuh_password, + name: 'wazuh_password', + }), + }, + }); + }); + + it('should return an ERROR when the os definitions received has a os with options duplicated', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + { + architecture: 'x64', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, + ]; + + try { + new CommandGenerator(osDefinitions, optionalParams, '4.4'); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(DuplicatedOSOptionException); + } + }); + + it('should return an ERROR when the os definitions received has a os with options duplicated', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedCommandsResponse, + startCommand: mockedCommandsResponse, + urlPackage: mockedCommandsResponse, + }, + ], + }, + ]; + + try { + new CommandGenerator(osDefinitions, optionalParams, '4.4'); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(DuplicatedOSException); + } + }); + + it('should return an ERROR when we want to get commands and the os is not selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + try { + commandGenerator.getAllCommands(); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(NoOSSelectedException); + } + }); + + it('should return an ERROR when we want to get the install command and the os is not selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + try { + commandGenerator.getInstallCommand(); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(NoOSSelectedException); + } + }); + + it('should return an ERROR when we want to get the start command and the os is not selected', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + try { + commandGenerator.getStartCommand(); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(NoOSSelectedException); + } + }); + + it('should return an ERROR when receive an empty version', () => { + try { + new CommandGenerator(osDefinitions, optionalParams, ''); + } catch (error) { + if (error instanceof Error) + expect(error).toBeInstanceOf(WazuhVersionUndefinedException); + } + }); + + it('should receives the solved optional params when the install command is called', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + + const selectedOs: tOperatingSystem = { + name: 'linux', + architecture: 'x64', + }; + commandGenerator.selectOS(selectedOs); + + const optionalValues = { + server_address: 'wazuh-ip', + }; + + commandGenerator.addOptionalParams(optionalValues as IOptionalParameters); + commandGenerator.getInstallCommand(); + expect(mockedCommandsResponse).toHaveBeenCalledWith( + expect.objectContaining({ + optionals: { + server_address: optionalParams.server_address.getParamCommand({ + property: optionalParams.server_address.property, + value: optionalValues.server_address, + name: 'server_address', + }), + }, + }), + ); + }); + + it('should receives the solved optional params when the start command is called', () => { + const commandGenerator = new CommandGenerator( + osDefinitions, + optionalParams, + '4.4', + ); + + const selectedOs: tOperatingSystem = { + name: 'linux', + architecture: 'x64', + }; + commandGenerator.selectOS(selectedOs); + + const optionalValues = { + server_address: 'wazuh-ip', + }; + + commandGenerator.addOptionalParams(optionalValues as IOptionalParameters); + commandGenerator.getStartCommand(); + expect(mockedCommandsResponse).toHaveBeenCalledWith( + expect.objectContaining({ + optionals: { + server_address: optionalParams.server_address.getParamCommand({ + property: optionalParams.server_address.property, + value: optionalValues.server_address, + name: 'server_address', + }), + }, + }), + ); + }); +}); \ No newline at end of file diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts b/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts new file mode 100644 index 0000000000..6974478eac --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts @@ -0,0 +1,171 @@ +import { + ICommandsResponse, + IOSCommandsDefinition, + IOSDefinition, + IOperationSystem, + IOptionalParameters, + IOptionalParametersManager, + tOptionalParams, +} from '../types'; +import { ICommandGenerator } from '../types'; +import { + searchOSDefinitions, + validateOSDefinitionHasDuplicatedOptions, + validateOSDefinitionsDuplicated, +} from '../services/search-os-definitions.service'; +import { OptionalParametersManager } from '../optional-parameters-manager/optional-parameters-manager'; +import { NoArchitectureSelectedException, NoOSSelectedException, WazuhVersionUndefinedException } from '../exceptions'; +import { version } from '../../../../../../package.json'; + +export class CommandGenerator implements ICommandGenerator { + os: OS['name'] | null = null; + osDefinitionSelected: IOSCommandsDefinition | null = null; + optionalsManager: IOptionalParametersManager; + protected optionals: IOptionalParameters | object = {}; + constructor( + public osDefinitions: IOSDefinition[], + protected optionalParams: tOptionalParams, + public wazuhVersion: string = version, + ) { + // validate os definitions received + validateOSDefinitionsDuplicated(this.osDefinitions); + validateOSDefinitionHasDuplicatedOptions(this.osDefinitions); + if(wazuhVersion == ''){ + throw new WazuhVersionUndefinedException(); + } + this.optionalsManager = new OptionalParametersManager(optionalParams); + } + + /** + * This method selects the operating system to use based on the given parameters + * @param params - The operating system parameters to select + */ + selectOS(params: OS) { + try { + // Check if the selected operating system is valid + this.osDefinitionSelected = this.checkIfOSisValid(params); + // Set the selected operating system + this.os = params.name; + } catch (error) { + // If the selected operating system is not valid, reset the selected OS and OS definition + this.osDefinitionSelected = null; + this.os = null; + throw error; + } + } + + /** + * This method adds the optional parameters to use based on the given parameters + * @param props - The optional parameters to select + * @returns The selected optional parameters + */ + addOptionalParams(props: IOptionalParameters): void { + // Get all the optional parameters based on the given parameters + this.optionals = this.optionalsManager.getAllOptionalParams(props); + } + + /** + * This method checks if the selected operating system is valid + * @param params - The operating system parameters to check + * @returns The selected operating system definition + * @throws An error if the operating system is not valid + */ + private checkIfOSisValid(params: OS): IOSCommandsDefinition { + const { name, architecture } = params; + if (!name) { + throw new NoOSSelectedException(); + } + if (!architecture) { + throw new NoArchitectureSelectedException(); + } + + const option = searchOSDefinitions(this.osDefinitions, { + name, + architecture, + }); + return option; + } + + /** + * This method gets the URL package for the selected operating system + * @returns The URL package for the selected operating system + * @throws An error if the operating system is not selected + */ + getUrlPackage(): string { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + return this.osDefinitionSelected.urlPackage({ + wazuhVersion: this.wazuhVersion, + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + name: this.os as OS['name'], + }); + } + + /** + * This method gets the install command for the selected operating system + * @returns The install command for the selected operating system + * @throws An error if the operating system is not selected + */ + getInstallCommand(): string { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + + return this.osDefinitionSelected.installCommand({ + name: this.os as OS['name'], + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + urlPackage: this.getUrlPackage(), + wazuhVersion: this.wazuhVersion, + optionals: this.optionals as IOptionalParameters, + }); + } + + /** + * This method gets the start command for the selected operating system + * @returns The start command for the selected operating system + * @throws An error if the operating system is not selected + */ + getStartCommand(): string { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + + return this.osDefinitionSelected.startCommand({ + name: this.os as OS['name'], + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + wazuhVersion: this.wazuhVersion, + optionals: this.optionals as IOptionalParameters, + }); + } + + /** + * This method gets all the commands for the selected operating system + * @returns An object containing all the commands for the selected operating system + * @throws An error if the operating system is not selected + */ + getAllCommands(): ICommandsResponse { + if (!this.osDefinitionSelected) { + throw new NoOSSelectedException(); + } + + return { + wazuhVersion: this.wazuhVersion, + os: this.os as OS['name'], + architecture: this.osDefinitionSelected.architecture as OS['architecture'], + url_package: this.getUrlPackage(), + install_command: this.getInstallCommand(), + start_command: this.getStartCommand(), + optionals: this.optionals, + }; + } + + /** + * Returns the optional paramaters processed + * @returns optionals + */ + getOptionalParamsCommands(): IOptionalParameters | object { + return this.optionals; + } + +} diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/exceptions/index.ts b/plugins/main/public/controllers/register-agent/core/register-commands/exceptions/index.ts new file mode 100644 index 0000000000..8ecf6d1be6 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/exceptions/index.ts @@ -0,0 +1,80 @@ +export class NoOptionFoundException extends Error { + constructor(osName: string, architecture: string) { + super( + `No OS option found for "${osName}" "${architecture}". Please check the OS definitions."`, + ); + } +} + +export class NoOSOptionFoundException extends Error { + constructor(osName: string) { + super( + `No OS option found for "${osName}". Please check the OS definitions."`, + ); + } +} + +export class NoStartCommandDefinitionException extends Error { + constructor(osName: string, architecture: string) { + super( + `No start command definition found for "${osName}" "${architecture}". Please check the OS definitions.`, + ); + } +} + +export class NoInstallCommandDefinitionException extends Error { + constructor(osName: string, architecture: string) { + super( + `No install command definition found for "${osName}" "${architecture}". Please check the OS definitions.`, + ); + } +} + +export class NoPackageURLDefinitionException extends Error { + constructor(osName: string, architecture: string) { + super( + `No package URL definition found for "${osName}" "${architecture}". Please check the OS definitions.`, + ); + } +} + +export class NoOptionalParamFoundException extends Error { + constructor(paramName: string) { + super( + `Optional parameter "${paramName}" not found. Please check the optional parameters definitions.`, + ); + } +} + +export class DuplicatedOSException extends Error { + constructor(osName: string) { + super(`Duplicate OS name found: ${osName}`); + } +} + +export class DuplicatedOSOptionException extends Error { + constructor(osName: string, architecture: string) { + super( + `Duplicate OS option found for "${osName}" "${architecture}"`, + ); + } +} + +export class WazuhVersionUndefinedException extends Error { + constructor() { + super(`Wazuh version not defined`); + } +} + +export class NoOSSelectedException extends Error { + constructor() { + super(`OS not selected. Please select`); + } +} + +export class NoArchitectureSelectedException extends Error { + constructor() { + super(`Architecture not selected. Please select`); + } +} + diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts b/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts new file mode 100644 index 0000000000..af6bef5b15 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.test.ts @@ -0,0 +1,229 @@ +import { NoOptionalParamFoundException } from '../exceptions'; +import { + IOptionalParameters, + tOptionalParams, + tOptionalParamsCommandProps, +} from '../types'; +import { OptionalParametersManager } from './optional-parameters-manager'; + +type tOptionalParamsFieldname = + | 'server_address' + | 'protocol' + | 'agent_group' + | 'wazuh_password' + | 'another_valid_fieldname'; + +const returnOptionalParam = ( + props: tOptionalParamsCommandProps, +) => { + const { property, value } = props; + return `${property}=${value}`; +}; +const optionalParametersDefinition: tOptionalParams = + { + protocol: { + property: 'WAZUH_MANAGER_PROTOCOL', + getParamCommand: returnOptionalParam, + }, + agent_group: { + property: 'WAZUH_AGENT_GROUP', + getParamCommand: returnOptionalParam, + }, + wazuh_password: { + property: 'WAZUH_PASSWORD', + getParamCommand: returnOptionalParam, + }, + server_address: { + property: 'WAZUH_MANAGER', + getParamCommand: returnOptionalParam, + }, + another_valid_fieldname: { + property: 'WAZUH_ANOTHER_PROPERTY', + getParamCommand: returnOptionalParam, + }, + }; + +describe('Optional Parameters Manager', () => { + it('should create an instance successfully', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + expect(optParamManager).toBeDefined(); + }); + + it.each([ + ['server_address', '10.10.10.27'], + ['protocol', 'TCP'], + ['agent_group', 'group1'], + ['wazuh_password', '123456'], + ['another_valid_fieldname', 'another_valid_value'] + ])( + `should return the corresponding command for "%s" param with "%s" value`, + (name, value) => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const commandParam = optParamManager.getOptionalParam({ + name: name as tOptionalParamsFieldname, + value, + }); + const defs = + optionalParametersDefinition[ + name as keyof typeof optionalParametersDefinition + ]; + expect(commandParam).toBe( + defs.getParamCommand({ + property: defs.property, + value, + name: name as tOptionalParamsFieldname, + }), + ); + }, + ); + + it('should return ERROR when the param received is not defined in the params definition', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const invalidParam = 'invalid_optional_param'; + try { + // @ts-ignore + optParamManager.getOptionalParam({ name: invalidParam, value: 'value' }); + } catch (error) { + expect(error).toBeInstanceOf(NoOptionalParamFoundException); + } + }); + + it('should return the corresponding command for all the params', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues: IOptionalParameters = { + protocol: 'TCP', + agent_group: 'group1', + wazuh_password: '123456', + server_address: 'server', + another_valid_fieldname: 'another_valid_value', + }; + const resolvedParams = optParamManager.getAllOptionalParams(paramsValues); + expect(resolvedParams).toEqual({ + agent_group: optionalParametersDefinition.agent_group.getParamCommand({ + name: 'agent_group', + property: optionalParametersDefinition.agent_group.property, + value: paramsValues.agent_group, + }), + protocol: optionalParametersDefinition.protocol.getParamCommand({ + name: 'protocol', + property: optionalParametersDefinition.protocol.property, + value: paramsValues.protocol, + }), + server_address: + optionalParametersDefinition.server_address.getParamCommand({ + name: 'server_address', + property: optionalParametersDefinition.server_address.property, + value: paramsValues.server_address, + }), + wazuh_password: + optionalParametersDefinition.wazuh_password.getParamCommand({ + name: 'wazuh_password', + property: optionalParametersDefinition.wazuh_password.property, + value: paramsValues.wazuh_password, + }), + another_valid_fieldname: + optionalParametersDefinition.another_valid_fieldname.getParamCommand({ + name: 'another_valid_fieldname', + property: + optionalParametersDefinition.another_valid_fieldname.property, + value: paramsValues.another_valid_fieldname, + }), + } as IOptionalParameters); + }); + + it('should return the corresponse command for all the params with NOT empty values', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues: IOptionalParameters = { + protocol: 'TCP', + agent_group: 'group1', + wazuh_password: '123456', + server_address: 'server', + another_valid_fieldname: 'another_valid_value', + }; + + const resolvedParams = optParamManager.getAllOptionalParams(paramsValues); + expect(resolvedParams).toEqual({ + agent_group: optionalParametersDefinition.agent_group.getParamCommand({ + name: 'agent_group', + property: optionalParametersDefinition.agent_group.property, + value: paramsValues.agent_group, + }), + protocol: optionalParametersDefinition.protocol.getParamCommand({ + name: 'protocol', + property: optionalParametersDefinition.protocol.property, + value: paramsValues.protocol, + }), + server_address: + optionalParametersDefinition.server_address.getParamCommand({ + name: 'server_address', + property: optionalParametersDefinition.server_address.property, + value: paramsValues.server_address, + }), + wazuh_password: + optionalParametersDefinition.wazuh_password.getParamCommand({ + name: 'wazuh_password', + property: optionalParametersDefinition.wazuh_password.property, + value: paramsValues.wazuh_password, + }), + another_valid_fieldname: + optionalParametersDefinition.another_valid_fieldname.getParamCommand({ + name: 'another_valid_fieldname', + property: + optionalParametersDefinition.another_valid_fieldname.property, + value: paramsValues.another_valid_fieldname, + }), + } as IOptionalParameters); + }); + + it('should return ERROR when the param received is not defined in the params definition', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues = { + serverAddress: 'invalid server address property value', + }; + + try { + // @ts-ignore + optParamManager.getAllOptionalParams(paramsValues); + } catch (error) { + expect(error).toBeInstanceOf(NoOptionalParamFoundException); + } + }); + + it('should return empty object response when receive an empty params object', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues = {}; + // @ts-ignore + const optionals = optParamManager.getAllOptionalParams(paramsValues); + expect(optionals).toEqual({}); + }); + + it('should return empty object response when receive all the params values with empty string ("")', () => { + const optParamManager = new OptionalParametersManager( + optionalParametersDefinition, + ); + const paramsValues = { + server_address: '', + agent_name: '', + protocol: '', + agent_group: '', + wazuh_password: '', + }; + // @ts-ignore + const optionals = optParamManager.getAllOptionalParams(paramsValues); + expect(optionals).toEqual({}); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts b/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts new file mode 100644 index 0000000000..34b943f797 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts @@ -0,0 +1,49 @@ +import { NoOptionalParamFoundException } from '../exceptions'; +import { IOptionalParamInput, IOptionalParameters, IOptionalParametersManager, tOptionalParams } from '../types'; + +export class OptionalParametersManager implements IOptionalParametersManager { + constructor(private optionalParamsConfig: tOptionalParams) {} + + /** + * Returns the command string for a given optional parameter. + * @param props - An object containing the optional parameter name and value. + * @returns The command string for the given optional parameter. + * @throws NoOptionalParamFoundException if the given optional parameter name is not found in the configuration. + */ + getOptionalParam(props: IOptionalParamInput) { + const { value, name } = props; + if (!this.optionalParamsConfig[name]) { + throw new NoOptionalParamFoundException(name); + } + return this.optionalParamsConfig[name].getParamCommand({ + value, + property: this.optionalParamsConfig[name].property, + name + }); + } + + /** + * Returns an object containing the command strings for all optional parameters with non-empty values. + * @param paramsValues - An object containing the optional parameter names and values. + * @returns An object containing the command strings for all optional parameters with non-empty values. + * @throws NoOptionalParamFoundException if any of the given optional parameter names is not found in the configuration. + */ + getAllOptionalParams(paramsValues: IOptionalParameters){ + // get keys for only the optional params with values !== '' + const optionalParams = Object.keys(paramsValues).filter(key => paramsValues[key as keyof typeof paramsValues] !== '') as Array; + const resolvedOptionalParams: any = {}; + for(const param of optionalParams){ + if(!this.optionalParamsConfig[param]){ + throw new NoOptionalParamFoundException(param as string); + } + + const paramDef = this.optionalParamsConfig[param]; + resolvedOptionalParams[param as string] = paramDef.getParamCommand({ + name: param as Params, + value: paramsValues[param] as string, + property: paramDef.property + }) as string; + } + return resolvedOptionalParams; + } +} diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts b/plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts new file mode 100644 index 0000000000..a4d16fcf32 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.test.ts @@ -0,0 +1,112 @@ +import { getInstallCommandByOS } from './get-install-command.service'; +import { IOSCommandsDefinition, IOSDefinition, IOptionalParameters } from '../types'; +import { + NoInstallCommandDefinitionException, + NoPackageURLDefinitionException, + WazuhVersionUndefinedException, +} from '../exceptions'; + + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + + +export type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password' | 'another_optional_parameter'; + +const validOsDefinition: IOSCommandsDefinition = { + architecture: 'x64', + installCommand: props => 'install command mocked', + startCommand: props => 'start command mocked', + urlPackage: props => 'https://package-url.com', +}; +describe('getInstallCommandByOS', () => { + it('should return the correct install command for each OS', () => { + const installCommand = getInstallCommandByOS( + validOsDefinition, + 'https://package-url.com', + '4.4', + 'linux', + ); + expect(installCommand).toBe('install command mocked'); + }); + + it('should return ERROR when the version is not received', () => { + try { + getInstallCommandByOS( + validOsDefinition, + 'https://package-url.com', + '', + 'linux', + ); + } catch (error) { + expect(error).toBeInstanceOf(WazuhVersionUndefinedException); + } + }); + it('should return ERROR when the OS has no install command', () => { + // @ts-ignore + const osDefinition: IOSCommandsDefinition = { + architecture: 'x64', + startCommand: props => 'start command mocked', + urlPackage: props => 'https://package-url.com', + }; + try { + getInstallCommandByOS( + osDefinition, + 'https://package-url.com', + '4.4', + 'linux', + ); + } catch (error) { + expect(error).toBeInstanceOf(NoInstallCommandDefinitionException); + } + }); + it('should return ERROR when the OS has no package url', () => { + try { + getInstallCommandByOS(validOsDefinition, '', '4.4', 'linux'); + } catch (error) { + expect(error).toBeInstanceOf(NoPackageURLDefinitionException); + } + }); + + it('should return install command with optional parameters', () => { + const mockedInstall = jest.fn(); + const validOsDefinition: IOSCommandsDefinition = { + architecture: 'x64', + installCommand: mockedInstall, + startCommand: props => 'start command mocked', + urlPackage: props => 'https://package-url.com', + }; + + const optionalParams: IOptionalParameters = { + agent_group: 'WAZUH_GROUP=agent_group', + agent_name: 'WAZUH_NAME=agent_name', + protocol: 'WAZUH_PROTOCOL=UDP', + server_address: 'WAZUH_MANAGER=server_address', + wazuh_password: 'WAZUH_PASSWORD=1231323', + another_optional_parameter: 'params value' + }; + + getInstallCommandByOS( + validOsDefinition, + 'https://package-url.com', + '4.4', + 'linux', + optionalParams + ); + expect(mockedInstall).toBeCalledTimes(1); + expect(mockedInstall).toBeCalledWith(expect.objectContaining({ optionals: optionalParams })); + }) +}); diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts b/plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts new file mode 100644 index 0000000000..c8fabc3ebf --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/services/get-install-command.service.ts @@ -0,0 +1,37 @@ +import { NoInstallCommandDefinitionException, NoPackageURLDefinitionException, WazuhVersionUndefinedException } from "../exceptions"; +import { IOSCommandsDefinition, IOperationSystem, IOptionalParameters } from "../types"; + +/** + * Returns the installation command for a given operating system. + * @param {IOSCommandsDefinition} osDefinition - The definition of the operating system. + * @param {string} packageUrl - The URL of the package to install. + * @param {string} version - The version of Wazuh to install. + * @param {string} osName - The name of the operating system. + * @param {IOptionalParameters} [optionals] - Optional parameters to include in the command. + * @returns {string} The installation command for the given operating system. + * @throws {NoInstallCommandDefinitionException} If the installation command is not defined for the given operating system. + * @throws {NoPackageURLDefinitionException} If the package URL is not defined. + * @throws {WazuhVersionUndefinedException} If the Wazuh version is not defined. + */ +export function getInstallCommandByOS(osDefinition: IOSCommandsDefinition, packageUrl: string, version: string, osName: string, optionals?: IOptionalParameters) { + + if (!osDefinition.installCommand) { + throw new NoInstallCommandDefinitionException(osName, osDefinition.architecture); + } + + if(!packageUrl || packageUrl === ''){ + throw new NoPackageURLDefinitionException(osName, osDefinition.architecture); + } + + if(!version || version === ''){ + throw new WazuhVersionUndefinedException(); + } + + return osDefinition.installCommand({ + urlPackage: packageUrl, + wazuhVersion: version, + name: osName as OS['name'], + architecture: osDefinition.architecture, + optionals, + }); +} \ No newline at end of file diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts b/plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts new file mode 100644 index 0000000000..73412b9fdb --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.test.ts @@ -0,0 +1,176 @@ +import { + NoOSOptionFoundException, +} from '../exceptions'; +import { IOSDefinition } from '../types'; +import { + searchOSDefinitions, + validateOSDefinitionHasDuplicatedOptions, + validateOSDefinitionsDuplicated, +} from './search-os-definitions.service'; + +const mockedInstallCommand = (props: any) => 'install command mocked'; +const mockedStartCommand = (props: any) => 'start command mocked'; +const mockedUrlPackage = (props: any) => 'https://package-url.com'; + +type tOptionalParamsNames = 'optional1' | 'optional2'; + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +const validOSDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + { + name: 'windows', + options: [ + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, +]; + +describe('search OS definitions services', () => { + describe('searchOSDefinitions', () => { + it('should return the OS definition if the OS name is found', () => { + const result = searchOSDefinitions(validOSDefinitions, { + name: 'linux', + architecture: 'x64', + }); + expect(result).toMatchObject(validOSDefinitions[0].options[0]); + }); + + it('should throw an error if the OS name is not found', () => { + expect(() => + searchOSDefinitions(validOSDefinitions, { + // @ts-ignore + name: 'invalid-os', + architecture: 'x64', + }), + ).toThrow(NoOSOptionFoundException); + }); + + }); + + describe('validateOSDefinitionsDuplicated', () => { + it('should not throw an error if there are no duplicated OS definitions', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + { + name: 'windows', + options: [ + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + ]; + + expect(() => + validateOSDefinitionsDuplicated(osDefinitions), + ).not.toThrow(); + }); + + it('should throw an error if there are duplicated OS definitions', () => { + const osDefinition: IOSDefinition = { + name: 'linux', + options: [ + { + architecture: 'x64', + // @ts-ignore + packageManager: 'aix', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }; + const osDefinitions: IOSDefinition[] = [osDefinition, osDefinition]; + + expect(() => validateOSDefinitionsDuplicated(osDefinitions)).toThrow(); + }); + }); + + describe('validateOSDefinitionHasDuplicatedOptions', () => { + it('should not throw an error if there are no duplicated OS definitions with different options', () => { + expect(() => + validateOSDefinitionHasDuplicatedOptions(validOSDefinitions), + ).not.toThrow(); + }); + + it('should throw an error if there are duplicated OS definitions with different options', () => { + const osDefinitions: IOSDefinition[] = [ + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + { + name: 'linux', + options: [ + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + { + architecture: 'x64', + installCommand: mockedInstallCommand, + startCommand: mockedStartCommand, + urlPackage: mockedUrlPackage, + }, + ], + }, + ]; + + expect(() => + validateOSDefinitionHasDuplicatedOptions(osDefinitions), + ).toThrow(); + }); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts b/plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts new file mode 100644 index 0000000000..0ceefada65 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/services/search-os-definitions.service.ts @@ -0,0 +1,84 @@ +import { + DuplicatedOSException, + DuplicatedOSOptionException, + NoOSOptionFoundException, + NoOptionFoundException, +} from '../exceptions'; +import { IOSDefinition, IOperationSystem } from '../types'; + +/** + * Searches for the OS definition option that matches the given operation system parameters. + * Throws an exception if no matching option is found. + * + * @param osDefinitions - The list of OS definitions to search through. + * @param params - The operation system parameters to match against. + * @returns The matching OS definition option. + * @throws NoOSOptionFoundException - If no matching OS definition is found. + */ +export function searchOSDefinitions( + osDefinitions: IOSDefinition[], + params: IOperationSystem, +){ + const { name, architecture } = params; + + const osDefinition = osDefinitions.find(os => os.name === name); + if (!osDefinition) { + throw new NoOSOptionFoundException(name); + } + + const osDefinitionOption = osDefinition.options.find( + option => + option.architecture === architecture, + ); + + if (!osDefinitionOption) { + throw new NoOptionFoundException(name, architecture); + } + + return osDefinitionOption; +}; + +/** + * Validates that there are no duplicated OS definitions in the given list. + * Throws an exception if a duplicated OS definition is found. + * + * @param osDefinitions - The list of OS definitions to validate. + * @throws DuplicatedOSException - If a duplicated OS definition is found. + */ +export function validateOSDefinitionsDuplicated( + osDefinitions: IOSDefinition[], +){ + const osNames = new Set(); + + for (const osDefinition of osDefinitions) { + if (osNames.has(osDefinition.name)) { + throw new DuplicatedOSException(osDefinition.name); + } + osNames.add(osDefinition.name); + } +}; + +/** + * Validates that there are no duplicated OS definition options in the given list. + * Throws an exception if a duplicated OS definition option is found. + * + * @param osDefinitions - The list of OS definitions to validate. + * @throws DuplicatedOSOptionException - If a duplicated OS definition option is found. + */ +export function validateOSDefinitionHasDuplicatedOptions( + osDefinitions: IOSDefinition[], +){ + for (const osDefinition of osDefinitions) { + const options = new Set(); + for (const option of osDefinition.options) { + let ext_arch_manager = `${option.architecture}`; + if (options.has(ext_arch_manager)) { + throw new DuplicatedOSOptionException( + osDefinition.name, + option.architecture, + ); + } + options.add(ext_arch_manager); + } + } +}; diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/types.ts b/plugins/main/public/controllers/register-agent/core/register-commands/types.ts new file mode 100644 index 0000000000..243477a999 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/core/register-commands/types.ts @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////// +/// Domain +///////////////////////////////////////////////////////// +export interface IOperationSystem { + name: string; + architecture: string; +} + +export type IOptionalParameters = { + [key in Params]: string; +}; + +/////////////////////////////////////////////////////////////////// +/// Operating system commands definitions +/////////////////////////////////////////////////////////////////// + +export interface IOSDefinition { + name: OS['name']; + options: IOSCommandsDefinition[]; +} + +interface IOptionalParamsWithValues { + optionals?: IOptionalParameters +} + + +export type tOSEntryProps = IOSProps & IOptionalParamsWithValues; +export type tOSEntryInstallCommand = tOSEntryProps & { urlPackage: string }; + +export interface IOSCommandsDefinition { + architecture: OS['architecture']; + urlPackage: (props: tOSEntryProps) => string; + installCommand: (props: tOSEntryInstallCommand) => string; + startCommand: (props: tOSEntryProps) => string; +} + +export interface IOSProps extends IOperationSystem { + wazuhVersion: string; +} + +/////////////////////////////////////////////////////////////////// +//// Commands optional parameters +/////////////////////////////////////////////////////////////////// +interface IOptionalParamProps { + property: string; + value: string; +} + +export type tOptionalParamsCommandProps = IOptionalParamProps & { + name: T; +}; +export interface IOptionsParamConfig { + property: string; + getParamCommand: (props: tOptionalParamsCommandProps) => string; +} + +export type tOptionalParams = { + [key in T]: IOptionsParamConfig; +}; + +export interface IOptionalParamInput { + value: any; + name: T; +} +export interface IOptionalParametersManager { + getOptionalParam(props: IOptionalParamInput): string; + getAllOptionalParams(paramsValues: IOptionalParameters): object; +} + +/////////////////////////////////////////////////////////////////// +/// Command creator class +/////////////////////////////////////////////////////////////////// + +export type IOSInputs = IOperationSystem & IOptionalParameters; +export interface ICommandGenerator extends ICommandGeneratorMethods { + osDefinitions: IOSDefinition[]; + wazuhVersion: string; +} + +export interface ICommandGeneratorMethods { + selectOS(params: IOperationSystem): void; + addOptionalParams(props: IOptionalParameters): void; + getInstallCommand(): string; + getStartCommand(): string; + getUrlPackage(): string; + getAllCommands(): ICommandsResponse; +} +export interface ICommandsResponse { + wazuhVersion: string; + os: string; + architecture: string; + url_package: string; + install_command: string; + start_command: string; + optionals: IOptionalParameters | object; +} diff --git a/plugins/main/public/controllers/register-agent/hooks/README.md b/plugins/main/public/controllers/register-agent/hooks/README.md new file mode 100644 index 0000000000..d3ec96adc1 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/hooks/README.md @@ -0,0 +1,167 @@ +# Documentation + +- [useRegisterAgentCommand hook](#useregisteragentcommand-hook) +- [Advantages](#advantages) +- [Usage](#usage) +- [Types](#types) + - [Hook props](#hook-props) + - [Hook output](#hook-output) +- [Hook with Generic types](#hook-with-generic-types) + - [Operating systems types example](#operating-systems-types-example) + +## useRegisterAgentCommand hook + +This hook makes use of the `Command Generator class` to generate the commands to register agents in the manager and allows to use it in React components. + +## Advantages + +- Ease of use of the Command Generator class. +- The hook returns the methods envolved to create the register commands by the operating system and optionas specified. +- The commands generate are stored in the state of the hook and can be used in the component. + + +## Usage + +```ts + +import { useRegisterAgentCommands } from 'path/to/use-register-agent-commands'; + +import { OSdefintions, paramsDefinitions} from 'path/config/os-definitions'; + +/* + the props recived by the hook must implement types: + - OS: IOSDefinition[] + - optional parameters: tOptionalParams +*/ + +const { + selectOS, + setOptionalParams, + installCommand, + startCommand, + optionalParamsParsed + } = useRegisterAgentCommands(); + +// select OS depending on the specified OS defined in the hook configuration +selectOS({ + name: 'name-OS', + architecture: 'architecture-OS', +}) + +// add optionals params depending on the specified optional parameters in the hook configuration +setOptionalParams({ + field_1: 'value_1', + field_2: 'value_2', + ... +}) + +/** the commands and the optional params will be processed and stored in the hook state **/ + +// install command +console.log('install command for the selected OS with optionals params', installCommand); +// start command +console.log('start command for the selected OS with optionals params', startCommand); +// optionals params processed +console.log('optionals params processed', optionalParamsParsed); + +``` + +## Types + +### Hook props + +```ts + +export interface IOperationSystem { + name: string; + architecture: string; +} + +interface IUseRegisterCommandsProps { + osDefinitions: IOSDefinition[]; + optionalParamsDefinitions: tOptionalParams; +} +``` + +### Hook output + +```ts + +export interface IOperationSystem { + name: string; + architecture: string; +} + +interface IUseRegisterCommandsOutput { + selectOS: (params: OS) => void; + setOptionalParams: (params: IOptionalParameters) => void; + installCommand: string; + startCommand: string; + optionalParamsParsed: IOptionalParameters | {}; +} +``` + +## Hook with Generic types + +We can pass the types with the OS posibilities options and the optionals params defined. +And the hook will validate and show warning in compilation and development time. + +#### Operating systems types example + +```ts +// global types + +export interface IOptionsParamConfig { + property: string; + getParamCommand: (props: tOptionalParamsCommandProps) => string; +} + +export type tOptionalParams = { + [key in T]: IOptionsParamConfig; +}; + +export interface IOperationSystem { + name: string; + architecture: string; +} + +/// .... + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +type tOptionalParameters = 'server_address' | 'agent_name' | 'agent_group' | 'protocol' | 'wazuh_password'; + +import { OSdefintions, paramsDefinitions} from 'path/config/os-definitions'; + + +// pass it to the hook and it will use the types when we are selecting the OS +const { + selectOS, + setOptionalParams, + installCommand, + startCommand, + optionalParamsParsed + } = useRegisterAgentCommands(OSdefintions, paramsDefinitions); + +// when the options are not valid depending on the types defined, the IDE will show a warning +selectOS({ + name: 'linux', + architecture: 'x64', +}) + +```` + diff --git a/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts b/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts new file mode 100644 index 0000000000..20a4de7b32 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.test.ts @@ -0,0 +1,229 @@ +import React from 'react'; +import { act, renderHook } from '@testing-library/react-hooks'; +import { useRegisterAgentCommands } from './use-register-agent-commands'; +import { + IOSDefinition, + tOptionalParams, +} from '../core/register-commands/types'; + +type tOptionalParamsNames = 'optional1' | 'optional2'; + +export interface ILinuxOSTypes { + name: 'linux'; + architecture: 'x64' | 'x86'; +} +export interface IWindowsOSTypes { + name: 'windows'; + architecture: 'x86'; +} + +export interface IMacOSTypes { + name: 'mac'; + architecture: '32/64'; +} + +export type tOperatingSystem = ILinuxOSTypes | IMacOSTypes | IWindowsOSTypes; + +const linuxDefinition: IOSDefinition = { + name: 'linux', + options: [ + { + architecture: '32/64', + urlPackage: props => + `https://packages.wazuh.com/4.x/yum/wazuh-agent-${props.wazuhVersion}-1.x86_64`, + installCommand: props => `sudo yum install -y ${props.urlPackage}`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + { + architecture: 'x64', + urlPackage: props => + `https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/ wazuh-agent_${props.wazuhVersion}-1_${props.architecture}`, + installCommand: props => + `curl -so wazuh-agent.deb ${props.urlPackage} && sudo dpkg -i ./wazuh-agent.deb`, + startCommand: props => `sudo systemctl start wazuh-agent`, + }, + ], +}; + +export const osCommandsDefinitions = [linuxDefinition]; + +/////////////////////////////////////////////////////////////////// +/// Optional parameters definitions +/////////////////////////////////////////////////////////////////// + +export const optionalParamsDefinitions: tOptionalParams = + { + optional1: { + property: 'WAZUH_MANAGER', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + }, + }, + optional2: { + property: 'WAZUH_AGENT_NAME', + getParamCommand: props => { + const { property, value } = props; + return `${property}=${value}`; + }, + }, + }; + +describe('useRegisterAgentCommands hook', () => { + it('should return installCommand and startCommand null when the hook is initialized', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + expect(hook.result.current.installCommand).toBe(''); + expect(hook.result.current.startCommand).toBe(''); + }); + + it('should return ERROR when get installCommand and the OS received is NOT valid', () => { + const { + result: { + current: { selectOS }, + }, + } = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + try { + act(() => { + selectOS({ + name: 'linux', + architecture: 'x64', + }); + }); + } catch (error) { + if (error instanceof Error) + expect(error.message).toContain('No OS option found for'); + } + }); + + it('should change the commands when the OS is selected successfully', async () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { selectOS } = hook.result.current; + const { result } = hook; + + const optionSelected = osCommandsDefinitions + .find(os => os.name === 'linux') + ?.options.find( + item => item.architecture === 'x64', + ); + const spyInstall = jest.spyOn(optionSelected!, 'installCommand'); + const spyStart = jest.spyOn(optionSelected!, 'startCommand'); + + act(() => { + selectOS({ + name: 'linux', + architecture: 'x64', + }); + }); + expect(result.current.installCommand).not.toBe(''); + expect(result.current.startCommand).not.toBe(''); + expect(spyInstall).toBeCalledTimes(1); + expect(spyStart).toBeCalledTimes(1); + }); + + it('should return commands empty when set optional params and OS is NOT selected', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { setOptionalParams } = hook.result.current; + + act(() => { + setOptionalParams({ + optional1: 'value 1', + optional2: 'value 2', + }); + }); + + expect(hook.result.current.installCommand).toBe(''); + expect(hook.result.current.startCommand).toBe(''); + }); + + it('should return optional params empty when optional params are not added', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { optionalParamsParsed } = hook.result.current; + expect(optionalParamsParsed).toEqual({}); + }); + + it('should return optional params when optional params are added', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { setOptionalParams } = hook.result.current; + const spy1 = jest.spyOn( + optionalParamsDefinitions.optional1, + 'getParamCommand', + ); + const spy2 = jest.spyOn( + optionalParamsDefinitions.optional2, + 'getParamCommand', + ); + act(() => { + setOptionalParams({ + optional1: 'value 1', + optional2: 'value 2', + }); + }); + + expect(spy1).toBeCalledTimes(1); + expect(spy2).toBeCalledTimes(1); + }); + + it('should update the commands when the OS is selected and optional params are added', () => { + const hook = renderHook(() => + useRegisterAgentCommands({ + osDefinitions: osCommandsDefinitions, + optionalParamsDefinitions: optionalParamsDefinitions, + }), + ); + const { selectOS, setOptionalParams } = hook.result.current; + const optionSelected = osCommandsDefinitions + .find(os => os.name === 'linux') + ?.options.find( + item => item.architecture === 'x64', + ); + const spyInstall = jest.spyOn(optionSelected!, 'installCommand'); + const spyStart = jest.spyOn(optionSelected!, 'startCommand'); + + act(() => { + selectOS({ + name: 'linux', + architecture: 'x64', + }); + + setOptionalParams({ + optional1: 'value 1', + optional2: 'value 2', + }); + }); + + expect(hook.result.current.installCommand).not.toBe(''); + expect(hook.result.current.startCommand).not.toBe(''); + expect(spyInstall).toBeCalledTimes(2); + expect(spyStart).toBeCalledTimes(2); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts b/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts new file mode 100644 index 0000000000..800c198039 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts @@ -0,0 +1,109 @@ +import React, { useEffect, useState } from 'react'; +import { CommandGenerator } from '../core/register-commands/command-generator/command-generator'; +import { + IOSDefinition, + IOperationSystem, + IOptionalParameters, + tOptionalParams, +} from '../core/register-commands/types'; +import { version } from '../../../../package.json'; + +interface IUseRegisterCommandsProps { + osDefinitions: IOSDefinition[]; + optionalParamsDefinitions: tOptionalParams; +} + +interface IUseRegisterCommandsOutput { + selectOS: (params: OS) => void; + setOptionalParams: (params: IOptionalParameters) => void; + installCommand: string; + startCommand: string; + optionalParamsParsed: IOptionalParameters | {}; +} + + +/** + * Custom hook that generates install and start commands based on the selected OS and optional parameters. + * + * @template T - The type of the selected OS. + * @param {IUseRegisterCommandsProps} props - The properties to configure the command generator. + * @returns {IUseRegisterCommandsOutput} - An object containing the generated commands and methods to update the selected OS and optional parameters. + */ +export function useRegisterAgentCommands(props: IUseRegisterCommandsProps): IUseRegisterCommandsOutput { + const { osDefinitions, optionalParamsDefinitions } = props; + // command generator settings + const wazuhVersion = version; + const osCommands: IOSDefinition[] = osDefinitions as IOSDefinition[]; + const optionalParams: tOptionalParams = optionalParamsDefinitions as tOptionalParams; + const commandGenerator = new CommandGenerator( + osCommands, + optionalParams, + wazuhVersion, + ); + + const [osSelected, setOsSelected] = useState(null); + const [optionalParamsValues, setOptionalParamsValues] = useState< + IOptionalParameters| {} + >({}); + const [optionalParamsParsed, setOptionalParamsParsed] = useState | {}>({}); + const [installCommand, setInstallCommand] = useState(''); + const [startCommand, setStartCommand] = useState(''); + + + /** + * Generates the install and start commands based on the selected OS and optional parameters. + * If no OS is selected, the method returns early without generating any commands. + * The generated commands are then set as state variables for later use. + */ + const generateCommands = () => { + if (!osSelected) return; + if (osSelected) { + commandGenerator.selectOS(osSelected); + } + if (optionalParamsValues) { + commandGenerator.addOptionalParams( + optionalParamsValues as IOptionalParameters, + ); + } + const installCommand = commandGenerator.getInstallCommand(); + const startCommand = commandGenerator.getStartCommand(); + setInstallCommand(installCommand); + setStartCommand(startCommand); + } + + useEffect(() => { + generateCommands(); + }, [osSelected, optionalParamsValues]); + + + /** + * Sets the selected OS for the command generator and updates the state variables accordingly. + * + * @param {T} params - The selected OS to be set. + * @returns {void} + */ + const selectOS = (params: OS) => { + commandGenerator.selectOS(params); + setOsSelected(params); + }; + + /** + * Sets the optional parameters for the command generator and updates the state variables accordingly. + * + * @param {IOptionalParameters} params - The optional parameters to be set. + * @returns {void} + */ + const setOptionalParams = (params: IOptionalParameters): void => { + commandGenerator.addOptionalParams(params); + setOptionalParamsValues(params); + setOptionalParamsParsed(commandGenerator.getOptionalParamsCommands()); + }; + + return { + selectOS, + setOptionalParams, + installCommand, + startCommand, + optionalParamsParsed + } +}; diff --git a/plugins/main/public/controllers/register-agent/index.tsx b/plugins/main/public/controllers/register-agent/index.tsx new file mode 100644 index 0000000000..146589950a --- /dev/null +++ b/plugins/main/public/controllers/register-agent/index.tsx @@ -0,0 +1 @@ +export { RegisterAgent } from './containers/register-agent/register-agent'; diff --git a/plugins/main/public/controllers/register-agent/interfaces/types.ts b/plugins/main/public/controllers/register-agent/interfaces/types.ts new file mode 100644 index 0000000000..f9fe6c02fc --- /dev/null +++ b/plugins/main/public/controllers/register-agent/interfaces/types.ts @@ -0,0 +1,18 @@ +import { tOperatingSystem } from '../config/os-commands-definitions'; + +interface RegisterAgentData { + icon: string; + title: tOperatingSystem['name']; + hr: boolean; + architecture: tOperatingSystem['architecture'][] +} + +interface CheckboxGroupComponentProps { + data: string[]; + cardIndex: number; + selectedOption: string | undefined; + onOptionChange: (optionId: string) => void; + onChange: (id: string) => void; +} + +export type { RegisterAgentData, CheckboxGroupComponentProps }; diff --git a/plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx b/plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx new file mode 100644 index 0000000000..db512362f5 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/form-status-manager.test.tsx @@ -0,0 +1,145 @@ +import { + EnhancedFieldConfiguration, + UseFormReturn, +} from '../../../components/common/form/types'; +import { + FormStepsDependencies, + RegisterAgentFormStatusManager, +} from './form-status-manager'; + +const defaultFormFieldData: EnhancedFieldConfiguration = { + changed: true, + value: 'value1', + error: '', + currentValue: '', + initialValue: '', + type: 'text', + onChange: () => { + console.log('onChange'); + }, + setInputRef: () => { + console.log('setInputRef'); + }, + inputRef: null, +}; + +const formFieldsDefault: UseFormReturn['fields'] = { + field1: { + ...defaultFormFieldData, + value: '', + error: null, + }, + field2: { + ...defaultFormFieldData, + value: '', + error: 'error message', + }, + field3: { + ...defaultFormFieldData, + value: 'value valid', + error: null, + }, +}; + +describe('RegisterAgentFormStatusManager', () => { + it('should create a instance', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + }); + + it('should return the form status', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + const formStatus = registerAgentFormStatusManager.getFormStatus(); + expect(formStatus).toEqual({ + field1: 'empty', + field2: 'invalid', + field3: 'complete', + }); + }); + + it('should return the field status', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + const fieldStatus = registerAgentFormStatusManager.getFieldStatus('field1'); + expect(fieldStatus).toEqual('empty'); + }); + + it('should return error if fieldname not found', () => { + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + ); + expect(() => + registerAgentFormStatusManager.getFieldStatus('field4'), + ).toThrowError('Fieldname not found'); + }); + + it('should return a INVALID when the step have an error', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1', 'field2'], + step2: ['field3'], + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getStepStatus('step1')).toEqual( + 'invalid', + ); + }); + + it('should return COMPLETE when the step have no errors and is not empty', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1', 'field2'], + step2: ['field3'], + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getStepStatus('step2')).toEqual( + 'complete', + ); + }); + + it('should return EMPTY when the step all fields empty', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1'], + step2: [ 'field2', + 'field3' ], + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getStepStatus('step1')).toEqual( + 'empty', + ); + }); + + it('should return all the steps status', () => { + const formSteps: FormStepsDependencies = { + step1: ['field1'], + step2: [ 'field2', + 'field3' ], + step3: ['field3'] + }; + const registerAgentFormStatusManager = new RegisterAgentFormStatusManager( + formFieldsDefault, + formSteps, + ); + expect(registerAgentFormStatusManager).toBeDefined(); + expect(registerAgentFormStatusManager.getFormStepsStatus()).toEqual({ + step1: 'empty', + step2: 'invalid', + step3: 'complete' + }); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/services/form-status-manager.tsx b/plugins/main/public/controllers/register-agent/services/form-status-manager.tsx new file mode 100644 index 0000000000..a250e2d413 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/form-status-manager.tsx @@ -0,0 +1,116 @@ +import { UseFormReturn } from '../../../components/common/form/types'; + +type FieldStatus = 'invalid' | 'empty' | 'complete'; +type FormStatus = { + [key: string]: FieldStatus; +}; + +type FormFields = UseFormReturn['fields']; +type FormFieldName = keyof FormFields; + +export type FormStepsDependencies = { + [key: string]: FormFieldName[]; +}; + +type FormStepsStatus = { + [key: string]: FieldStatus; +}; + +interface FormFieldsStatusManager { + getFieldStatus: (fieldname: FormFieldName) => FieldStatus; + getFormStatus: () => FormStatus; + getStepStatus: (stepName: string) => FieldStatus; + getFormStepsStatus: () => FormStepsStatus; +} + +export class RegisterAgentFormStatusManager implements FormFieldsStatusManager { + constructor( + private formFields: FormFields, + private formSteps?: FormStepsDependencies, + ) {} + + getFieldStatus = (fieldname: FormFieldName): FieldStatus => { + const field = this.formFields[fieldname]; + if (!field) { + throw Error('Fieldname not found'); + } + + if (field.error) { + return 'invalid'; + } + + if (field.value?.length === 0) { + return 'empty'; + } + + return 'complete'; + }; + + getFormStatus = (): FormStatus => { + const fieldNames = Object.keys(this.formFields); + const formStatus: FormStatus | object = {}; + + fieldNames.forEach((fieldName: string) => { + formStatus[fieldName] = this.getFieldStatus(fieldName); + }); + + return formStatus as FormStatus; + }; + + getStepStatus = (stepName: string): FieldStatus => { + if (!this.formSteps) { + throw Error('Form steps not defined'); + } + const stepFields = this.formSteps[stepName]; + if (!stepFields) { + throw Error('Step name not found'); + } + + const formStepStatus: FormStepsStatus | object = {}; + stepFields.forEach((fieldName: FormFieldName) => { + formStepStatus[fieldName] = this.getFieldStatus(fieldName); + }); + + const stepStatus = Object.values(formStepStatus); + + // if any is invalid + if (stepStatus.includes('invalid')) { + return 'invalid'; + } else if (stepStatus.includes('empty')) { + // if all are empty + return 'empty'; + } else { + // if all are complete + return 'complete'; + } + }; + + getFormStepsStatus = (): FormStepsStatus => { + if (!this.formSteps) { + throw Error('Form steps not defined'); + } + + const formStepsStatus: FormStepsStatus | object = {}; + Object.keys(this.formSteps).forEach((stepName: string) => { + formStepsStatus[stepName] = this.getStepStatus(stepName); + }); + + return formStepsStatus as FormStepsStatus; + }; + + getIncompleteSteps = (): string[] => { + const formStepsStatus = this.getFormStepsStatus(); + const notCompleteSteps = Object.entries(formStepsStatus).filter( + ([ _, status ]) => status === 'empty', + ); + return notCompleteSteps.map(( [ stepName, _]) => stepName); + }; + + getInvalidFields = (): string[] => { + const formStatus = this.getFormStatus(); + const invalidFields = Object.entries(formStatus).filter( + ([ _, status ]) => status === 'invalid', + ); + return invalidFields.map(([ fieldName, _ ]) => fieldName); + } +} diff --git a/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx b/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx new file mode 100644 index 0000000000..77adb03712 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx @@ -0,0 +1,138 @@ +import { tOptionalParameters } from '../core/config/os-commands-definitions'; +import { + IOptionalParameters, + tOSEntryInstallCommand, + tOSEntryProps, +} from '../core/register-commands/types'; +import { tOperatingSystem } from '../hooks/use-register-agent-commands.test'; + +const getAllOptionals = ( + optionals: IOptionalParameters, + osName?: tOperatingSystem['name'], +) => { + // create paramNameOrderList, which is an array of the keys of optionals add interface + const paramNameOrderList: (keyof IOptionalParameters)[] = + ['serverAddress', 'wazuhPassword', 'agentGroups', 'agentName', 'protocol']; + + if (!optionals) return ''; + let paramsText = Object.entries(paramNameOrderList).reduce( + (acc, [key, value]) => { + if (optionals[value]) { + acc += `${optionals[value]} `; + } + return acc; + }, + '', + ); + + if (osName && osName.toLowerCase() === 'windows' && optionals.serverAddress) { + // when os is windows we must to add wazuh registration server with server address + paramsText = + paramsText + `WAZUH_REGISTRATION_SERVER=${optionals.serverAddress.replace('WAZUH_MANAGER=','')} `; + } + + return paramsText; +}; + +const getAllOptionalsMacos = ( + optionals: IOptionalParameters +) => { + // create paramNameOrderList, which is an array of the keys of optionals add interface + const paramNameOrderList: (keyof IOptionalParameters)[] = + ['serverAddress', 'wazuhPassword', 'agentGroups', 'agentName', 'protocol']; + + if (!optionals) return ''; + return Object.entries(paramNameOrderList).reduce( + (acc, [key, value]) => { + if (optionals[value]) { + acc += `${optionals[value]}\\n`; + } + return acc; + }, + '', + ); +}; + + +/******* RPM *******/ + +// curl -o wazuh-agent-4.4.5-1.x86_64.rpm https://packages.wazuh.com/4.x/yum/wazuh-agent-4.4.5-1.x86_64.rpm && sudo WAZUH_MANAGER='172.30.30.20' rpm -ihv wazuh-agent-4.4.5-1.x86_64.rpm + +export const getRPMInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage, wazuhVersion } = props; + const packageName = `wazuh-agent-${wazuhVersion}-1.x86_64.rpm` + return `curl -o ${packageName} ${urlPackage} && sudo ${ + optionals && getAllOptionals(optionals) + }rpm -ihv ${packageName}`; +}; + +/******* DEB *******/ + +// wget https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_4.4.5-1_amd64.deb && sudo WAZUH_MANAGER='172.30.30.20' dpkg -i ./wazuh-agent_4.4.5-1_amd64.deb + +export const getDEBInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage, wazuhVersion } = props; + const packageName = `wazuh-agent_${wazuhVersion}-1_amd64.deb` + return `wget ${urlPackage} && sudo ${ + optionals && getAllOptionals(optionals) + }dpkg -i ./${packageName}`; +}; + +/******* Linux *******/ + +// Start command +export const getLinuxStartCommand = ( + _props: tOSEntryProps, +) => { + return `sudo systemctl daemon-reload\nsudo systemctl enable wazuh-agent\nsudo systemctl start wazuh-agent`; +}; + +/******** Windows ********/ + +export const getWindowsInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage, name } = props; + return `Invoke-WebRequest -Uri ${urlPackage} -OutFile \${env.tmp}\\wazuh-agent; msiexec.exe /i \${env.tmp}\\wazuh-agent /q ${ + optionals && getAllOptionals(optionals, name) + }`; +}; + +export const getWindowsStartCommand = ( + _props: tOSEntryProps, +) => { + return `NET START WazuhSvc`; +}; + +/******** MacOS ********/ + +export const getMacOsInstallCommand = ( + props: tOSEntryInstallCommand, +) => { + const { optionals, urlPackage } = props; + // Set macOS installation script with environment variables + const optionalsText = optionals && getAllOptionalsMacos(optionals); + const macOSInstallationOptions = `${optionalsText}` + .replace(/\' ([a-zA-Z])/g, "' && $1") // Separate environment variables with && + .replace(/\"/g, '\\"') // Escape double quotes + .trim(); + + // If no variables are set, the echo will be empty + const macOSInstallationSetEnvVariablesScript = macOSInstallationOptions + ? `echo -e "${macOSInstallationOptions}" > /tmp/wazuh_envs && ` + : ``; + + // Merge environment variables with installation script + const macOSInstallationScript = `curl -so wazuh-agent.pkg ${urlPackage} && ${macOSInstallationSetEnvVariablesScript}sudo installer -pkg ./wazuh-agent.pkg -target /`; + return macOSInstallationScript; +}; + +export const getMacosStartCommand = ( + _props: tOSEntryProps, +) => { + return `sudo /Library/Ossec/bin/wazuh-control start`; +}; diff --git a/plugins/main/public/controllers/register-agent/services/register-agent-services.tsx b/plugins/main/public/controllers/register-agent/services/register-agent-services.tsx new file mode 100644 index 0000000000..8200224bb2 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/register-agent-services.tsx @@ -0,0 +1,295 @@ +import { UseFormReturn } from '../../../components/common/form/types'; +import { WzRequest } from '../../../react-services/wz-request'; +import { + tOperatingSystem, + tOptionalParameters, +} from '../core/config/os-commands-definitions'; +import { RegisterAgentData } from '../interfaces/types'; + +type Protocol = 'TCP' | 'UDP'; + +type RemoteItem = { + connection: 'syslog' | 'secure'; + ipv6: 'yes' | 'no'; + protocol: Protocol[]; + allowed_ips?: string[]; + queue_size?: string; +}; + +type RemoteConfig = { + name: string; + isUdp: boolean | null; + haveSecureConnection: boolean | null; +}; + +/** + * Get the cluster status + */ +export const clusterStatusResponse = async (): Promise => { + const clusterStatus = await WzRequest.apiReq('GET', '/cluster/status', {}); + if ( + clusterStatus.data.data.enabled === 'yes' && + clusterStatus.data.data.running === 'yes' + ) { + // Cluster mode + return true; + } else { + // Manager mode + return false; + } +}; + +/** + * Get the remote configuration from api + */ +async function getRemoteConfiguration(nodeName: string): Promise { + let config: RemoteConfig = { + name: nodeName, + isUdp: false, + haveSecureConnection: false, + }; + + try { + const clusterStatus = await clusterStatusResponse(); + let result; + if (clusterStatus) { + result = await WzRequest.apiReq( + 'GET', + `/cluster/${nodeName}/configuration/request/remote`, + {}, + ); + } else { + result = await WzRequest.apiReq( + 'GET', + '/manager/configuration/request/remote', + {}, + ); + } + const items = ((result.data || {}).data || {}).affected_items || []; + const remote = items[0]?.remote; + if (remote) { + const remoteFiltered = remote.filter((item: RemoteItem) => { + return item.connection === 'secure'; + }); + + remoteFiltered.length > 0 + ? (config.haveSecureConnection = true) + : (config.haveSecureConnection = false); + + let protocolsAvailable: Protocol[] = []; + remote.forEach((item: RemoteItem) => { + // get all protocols available + item.protocol.forEach(protocol => { + protocolsAvailable = protocolsAvailable.concat(protocol); + }); + }); + + config.isUdp = + getRemoteProtocol(protocolsAvailable) === 'UDP' ? true : false; + } + return config; + } catch (error) { + return config; + } +} + +/** + * Get the remote protocol available from list of protocols + * @param protocols + */ +function getRemoteProtocol(protocols: Protocol[]) { + if (protocols.length === 1) { + return protocols[0]; + } else { + return !protocols.includes('TCP') ? 'UDP' : 'TCP'; + } +} + +/** + * Get the remote configuration from nodes registered in the cluster and decide the protocol to setting up in deploy agent param + * @param nodeSelected + * @param defaultServerAddress + */ +async function getConnectionConfig( + nodeSelected: any, + defaultServerAddress?: string, +) { + const nodeName = nodeSelected?.label; + const nodeIp = nodeSelected?.value; + if (!defaultServerAddress) { + if (nodeSelected.nodetype !== 'custom') { + const remoteConfig = await getRemoteConfiguration(nodeName); + return { + serverAddress: nodeIp, + udpProtocol: remoteConfig.isUdp, + connectionSecure: remoteConfig.haveSecureConnection, + }; + } else { + return { + serverAddress: nodeName, + udpProtocol: false, + connectionSecure: true, + }; + } + } else { + return { + serverAddress: defaultServerAddress, + udpProtocol: false, + connectionSecure: true, + }; + } +} + +type NodeItem = { + name: string; + ip: string; + type: string; +}; + +type NodeResponse = { + data: { + data: { + affected_items: NodeItem[]; + }; + }; +}; + +/** + * Get the list of the cluster nodes and parse it into a list of options + */ +export const getNodeIPs = async (): Promise => { + return await WzRequest.apiReq('GET', '/cluster/nodes', {}); +}; + +/** + * Get the list of the manager and parse it into a list of options + */ +export const getManagerNode = async (): Promise => { + const managerNode = await WzRequest.apiReq('GET', '/manager/api/config', {}); + return ( + managerNode?.data?.data?.affected_items?.map(item => ({ + label: item.node_name, + value: item.node_api_config.host, + nodetype: 'master', + })) || [] + ); +}; + +/** + * Parse the nodes list from the API response to a format that can be used by the EuiComboBox + * @param nodes + */ +export const parseNodesInOptions = (nodes: NodeResponse): any[] => { + return nodes.data.data.affected_items.map((item: NodeItem) => ({ + label: item.name, + value: item.ip, + nodetype: item.type, + })); +}; + +/** + * Get the list of the cluster nodes from API and parse it into a list of options + */ +export const fetchClusterNodesOptions = async (): Promise => { + const clusterStatus = await clusterStatusResponse(); + if (clusterStatus) { + // Cluster mode + // Get the cluster nodes + const nodes = await getNodeIPs(); + return parseNodesInOptions(nodes); + } else { + // Manager mode + // Get the manager node + return await getManagerNode(); + } +}; + +/** + * Get the master node data from the list of cluster nodes + * @param nodeIps + */ +export const getMasterNode = (nodeIps: any[]): any[] => { + return nodeIps.filter(nodeIp => nodeIp.nodetype === 'master'); +}; + +/** + * Get the remote configuration from manager + * This function get the config from manager mode or cluster mode + */ +export const getMasterRemoteConfiguration = async () => { + const nodes = await fetchClusterNodesOptions(); + const masterNode = getMasterNode(nodes); + return await getRemoteConfiguration(masterNode[0].label); +}; + +export { getConnectionConfig, getRemoteConfiguration }; + +export const getGroups = async () => { + try { + const result = await WzRequest.apiReq('GET', '/groups', {}); + return result.data.data.affected_items.map(item => ({ + label: item.name, + id: item.name, + })); + } catch (error) { + throw new Error(error); + } +}; + +export const getRegisterAgentFormValues = (form: UseFormReturn) => { + // return the values form the formFields and the value property + return Object.keys(form.fields).map(key => { + return { + name: key, + value: form.fields[key].value, + }; + }); +}; + +export interface IParseRegisterFormValues { + operatingSystem: { + name: tOperatingSystem['name'] | ''; + architecture: tOperatingSystem['architecture'] | ''; + }; + // optionalParams is an object that their key is defined in tOptionalParameters and value must be string + optionalParams: { + [FIELD in tOptionalParameters]: any; + }; +} + +export const parseRegisterAgentFormValues = ( + formValues: { name: keyof UseFormReturn['fields']; value: any }[], + OSOptionsDefined: RegisterAgentData[], + initialValues?: IParseRegisterFormValues +) => { + // return the values form the formFields and the value property + const parsedForm = initialValues || { + operatingSystem: { + architecture: '', + name: '', + }, + optionalParams: {}, + } as IParseRegisterFormValues; + formValues.forEach(field => { + if (field.name === 'operatingSystemSelection') { + // search the architecture defined in architecture array and get the os name defined in title array in the same index + const operatingSystem = OSOptionsDefined.find(os => + os.architecture.includes(field.value), + ); + if (operatingSystem) { + parsedForm.operatingSystem = { + name: operatingSystem.title, + architecture: field.value, + }; + } + } else { + if (field.name === 'agentGroups') { + parsedForm.optionalParams[field.name as any] = field.value.map(item => item.id) + } else { + parsedForm.optionalParams[field.name as any] = field.value; + } + } + }); + + return parsedForm; +}; \ No newline at end of file diff --git a/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx b/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx new file mode 100644 index 0000000000..0136b0ec2b --- /dev/null +++ b/plugins/main/public/controllers/register-agent/services/register-agent-steps-status-services.tsx @@ -0,0 +1,200 @@ +import { EuiStepStatus } from '@elastic/eui'; +import { UseFormReturn } from '../../../components/common/form/types'; +import { + FormStepsDependencies, + RegisterAgentFormStatusManager, +} from './form-status-manager'; + +const fieldsHaveErrors = ( + fieldsToCheck: string[], + formFields: UseFormReturn['fields'], +) => { + if (!fieldsToCheck) { + return true; + } + // check if the fieldsToCheck array NOT exists in formFields and get the field doesn't exists + if (!fieldsToCheck.every(key => formFields[key])) { + throw Error('fields to check are not defined in formFields'); + } + + const haveError = fieldsToCheck.some(key => { + return formFields[key]?.error; + }); + return haveError; +}; + +const fieldsAreEmpty = ( + fieldsToCheck: string[], + formFields: UseFormReturn['fields'], +) => { + if (!fieldsToCheck) { + return true; + } + // check if the fieldsToCheck array NOT exists in formFields and get the field doesn't exists + if (!fieldsToCheck.every(key => formFields[key])) { + throw Error('fields to check are not defined in formFields'); + } + + const notEmpty = fieldsToCheck.some(key => { + return formFields[key]?.value?.length > 0; + }); + return !notEmpty; +}; + +const anyFieldIsComplete = ( + fieldsToCheck: string[], + formFields: UseFormReturn['fields'], +) => { + if (!fieldsToCheck) { + return true; + } + // check if the fieldsToCheck array NOT exists in formFields and get the field doesn't exists + if (!fieldsToCheck.every(key => formFields[key])) { + throw Error('fields to check are not defined in formFields'); + } + + if (fieldsHaveErrors(fieldsToCheck, formFields)) { + return false; + } + + if (fieldsAreEmpty(fieldsToCheck, formFields)) { + return false; + } + + return true; +}; + + +export const showCommandsSections = ( + formFields: UseFormReturn['fields'], +): boolean => { + if ( + !formFields.operatingSystemSelection.value || + formFields.serverAddress.value === '' || + formFields.serverAddress.error + ) { + return false; + } else if ( + formFields.serverAddress.value === '' && + formFields.agentName.value === '' + ) { + return true; + } else if (!fieldsHaveErrors(['agentGroups', 'agentName'], formFields)) { + return true; + } else { + return false; + } +}; + +/******** Form Steps status getters ********/ + +export type tFormStepsStatus = EuiStepStatus | 'current' | 'disabled' | ''; + +export const getOSSelectorStepStatus = ( + formFields: UseFormReturn['fields'], +): tFormStepsStatus => { + return formFields.operatingSystemSelection.value ? 'complete' : 'current'; +}; + +export const getAgentCommandsStepStatus = ( + formFields: UseFormReturn['fields'], + wasCopied: boolean, +): tFormStepsStatus | 'disabled' => { + if (!showCommandsSections(formFields)) { + return 'disabled'; + } else if (showCommandsSections(formFields) && wasCopied) { + return 'complete'; + } else { + return 'current'; + } +}; + +export const getServerAddressStepStatus = ( + formFields: UseFormReturn['fields'], +): tFormStepsStatus => { + if ( + !formFields.operatingSystemSelection.value || + formFields.operatingSystemSelection.error + ) { + return 'disabled'; + } else if ( + !formFields.serverAddress.value || + formFields.serverAddress.error + ) { + return 'current'; + } else { + return 'complete'; + } +}; + +export const getOptionalParameterStepStatus = ( + formFields: UseFormReturn['fields'], + installCommandWasCopied: boolean, +): tFormStepsStatus => { + // when previous step are not complete + if ( + !formFields.operatingSystemSelection.value || + formFields.operatingSystemSelection.error || + !formFields.serverAddress.value || + formFields.serverAddress.error + ) { + return 'disabled'; + } else if ( + installCommandWasCopied || + anyFieldIsComplete(['agentName', 'agentGroups'], formFields) + ) { + return 'complete'; + } else { + return 'current'; + } +}; + +export const getPasswordStepStatus = ( + formFields: UseFormReturn['fields'], +): tFormStepsStatus => { + if ( + !formFields.operatingSystemSelection.value || + formFields.operatingSystemSelection.error || + !formFields.serverAddress.value || + formFields.serverAddress.error + ) { + return 'disabled'; + } else { + return 'complete'; + } +}; + +export enum tFormStepsLabel { + operatingSystemSelection = 'operating system', + serverAddress = 'server address', +} + +export const getIncompleteSteps = ( + formFields: UseFormReturn['fields'], +): tFormStepsLabel[] => { + const steps: FormStepsDependencies = { + operatingSystemSelection: ['operatingSystemSelection'], + serverAddress: ['serverAddress'], + }; + const statusManager = new RegisterAgentFormStatusManager(formFields, steps); + // replace fields array using label names + return statusManager.getIncompleteSteps().map(field => { + return tFormStepsLabel[field] || field; + }); +}; + +export enum tFormFieldsLabel { + agentName = 'agent name', + agentGroups = 'agent groups', + serverAddress = 'server address', +} + +export const getInvalidFields = ( + formFields: UseFormReturn['fields'], +): tFormFieldsLabel[] => { + const statusManager = new RegisterAgentFormStatusManager(formFields); + + return statusManager.getInvalidFields().map(field => { + return tFormFieldsLabel[field] || field; + }); +}; diff --git a/plugins/main/public/controllers/register-agent/utils/register-agent-data.tsx b/plugins/main/public/controllers/register-agent/utils/register-agent-data.tsx new file mode 100644 index 0000000000..378bf61d33 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/utils/register-agent-data.tsx @@ -0,0 +1,47 @@ +import { RegisterAgentData } from '../interfaces/types'; +import LinuxDarkIcon from '../../../../public/assets/images/themes/dark/linux-icon.svg'; +import LinuxLightIcon from '../../../../public/assets/images/themes/light/linux-icon.svg'; +import WindowsDarkIcon from '../../../../public/assets/images/themes/dark/windows-icon.svg'; +import WindowsLightIcon from '../../../../public/assets/images/themes/light/windows-icon.svg'; +import MacDarkIcon from '../../../../public/assets/images/themes/dark/mac-icon.svg'; +import MacLightIcon from '../../../../public/assets/images/themes/light/mac-icon.svg'; +import { getUiSettings } from '../../../kibana-services'; + +const darkMode = getUiSettings()?.get('theme:darkMode'); + +export const OPERATING_SYSTEMS_OPTIONS: RegisterAgentData[] = [ + { + icon: darkMode ? LinuxDarkIcon : LinuxLightIcon, + title: 'LINUX', + hr: true, + architecture: ['RPM amd64', 'RPM aarch64', 'DEB amd64', 'DEB aarch64'], + }, + { + icon: darkMode ? WindowsDarkIcon : WindowsLightIcon, + title: 'WINDOWS', + hr: true, + architecture: ['MSI 32/64 bits'], + }, + { + icon: darkMode ? MacDarkIcon : MacLightIcon, + title: 'macOS', + hr: true, + architecture: ['Intel', 'Apple silicon'], + }, +]; + +export const SERVER_ADDRESS_TEXTS = [ + { + title: 'Server address', + subtitle: + 'This is the address the agent uses to communicate with the Wazuh server. Enter an IP address or a fully qualified domain name (FDQN).', + }, +]; + +export const OPTIONAL_PARAMETERS_TEXT = [ + { + title: 'Optional settings', + subtitle: + 'The deployment sets the endpoint hostname as the agent name by default. Optionally, you can set your own name in the field below.', + }, +]; diff --git a/plugins/main/public/controllers/register-agent/utils/validations.test.tsx b/plugins/main/public/controllers/register-agent/utils/validations.test.tsx new file mode 100644 index 0000000000..edd7c4658d --- /dev/null +++ b/plugins/main/public/controllers/register-agent/utils/validations.test.tsx @@ -0,0 +1,68 @@ +import { validateServerAddress, validateAgentName } from './validations'; + +describe('Validations', () => { + it('should return undefined for an empty value', () => { + const result = validateServerAddress(''); + expect(result).toBeUndefined(); + }); + + it('should return undefined for a valid FQDN', () => { + const validFQDN = 'example.fqdn.valid'; + const result = validateServerAddress(validFQDN); + expect(result).toBeUndefined(); + }); + + it('should return undefined for a valid IP', () => { + const validIP = '192.168.1.1'; + const result = validateServerAddress(validIP); + expect(result).toBeUndefined(); + }); + + it('should return an error message for an invalid FQDN', () => { + const invalidFQDN = 'example.'; + const result = validateServerAddress(invalidFQDN); + expect(result).toBe( + 'Each label must have a letter or number at the beginning. The maximum length is 63 characters.', + ); + }); + + test('should return an error message for an invalid IP', () => { + const invalidIP = '999.999.999.999.999'; + const result = validateServerAddress(invalidIP); + expect(result).toBe('Not a valid IP'); + }); + + test('should return undefined for an empty value', () => { + const emptyValue = ''; + const result = validateAgentName(emptyValue); + expect(result).toBeUndefined(); + }); + + test('should return an error message for invalid format and length', () => { + const invalidAgentName = '?'; + const result = validateAgentName(invalidAgentName); + expect(result).toBe( + 'The minimum length is 2 characters. The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"', + ); + }); + + test('should return an error message for invalid format', () => { + const invalidAgentName = 'agent$name'; + const result = validateAgentName(invalidAgentName); + expect(result).toBe( + 'The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"', + ); + }); + + test('should return an error message for invalid length', () => { + const invalidAgentName = 'a'; + const result = validateAgentName(invalidAgentName); + expect(result).toBe('The minimum length is 2 characters.'); + }); + + test('should return an empty string for a valid agent name', () => { + const validAgentName = 'agent_name'; + const result = validateAgentName(validAgentName); + expect(result).toBe(''); + }); +}); diff --git a/plugins/main/public/controllers/register-agent/utils/validations.tsx b/plugins/main/public/controllers/register-agent/utils/validations.tsx new file mode 100644 index 0000000000..52705b5e53 --- /dev/null +++ b/plugins/main/public/controllers/register-agent/utils/validations.tsx @@ -0,0 +1,57 @@ +//IP: This is a set of four numbers, for example, 192.158.1.38. Each number in the set can range from 0 to 255. Therefore, the full range of IP addresses goes from 0.0.0.0 to 255.255.255.255 +// O ipv6: 2001:0db8:85a3:0000:0000:8a2e:0370:7334 + +// FQDN: Maximum of 63 characters per label. +// Can only contain numbers, letters and hyphens (-) +// Labels cannot begin or end with a hyphen +// Currently supports multilingual characters, i.e. letters not included in the English alphabet: e.g. á é í ó ú ü ñ. +// Minimum 3 labels +export const validateServerAddress = (value: any) => { + const isFQDN = + /^(?!-)(?!.*--)(?!.*\d$)[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*(?:\.[a-zA-Z0-9áéíóúüñ]{1,63}(?:-[a-zA-Z0-9áéíóúüñ]{1,63})*){1,}$/; + const isIP = + /^(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}|(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})$/; + const numbersAndPoints = /^[0-9.]+$/; + const areLettersNumbersAndColons = /^[a-zA-Z0-9:]+$/; + const letters = /[a-zA-Z]/; + const isFQDNFormatValid = isFQDN.test(value); + const isIPFormatValid = isIP.test(value); + const areNumbersAndPoints = numbersAndPoints.test(value); + const hasLetters = letters.test(value); + const hasPoints = value.includes('.'); + + let validation = undefined; + if (value.length === 0) { + return validation; + } else if (isFQDNFormatValid && value !== '') { + return validation; // FQDN valid + } else if (isIPFormatValid && value !== '') { + return validation; // IP valid + } else if (hasPoints && hasLetters && !isFQDNFormatValid) { + return (validation = + 'Each label must have a letter or number at the beginning. The maximum length is 63 characters.'); // FQDN invalid + } else if ( + (areNumbersAndPoints || areLettersNumbersAndColons) && + !isIPFormatValid + ) { + return (validation = 'Not a valid IP'); // IP invalid + } +}; + +export const validateAgentName = (value: any) => { + if (value.length === 0) { + return undefined; + } + const regex = /^[A-Za-z.\-_,]+$/; + + const isLengthValid = value.length >= 2 && value.length <= 63; + const isFormatValid = regex.test(value); + if (!isFormatValid && !isLengthValid) { + return 'The minimum length is 2 characters. The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"'; + } else if (!isLengthValid) { + return 'The minimum length is 2 characters.'; + } else if (!isFormatValid) { + return 'The character is not valid. Allowed characters are A-Z, a-z, ".", "-", "_"'; + } + return ''; +}; diff --git a/plugins/main/public/services/routes.js b/plugins/main/public/services/routes.js index 6b24043526..1f352ff20e 100644 --- a/plugins/main/public/services/routes.js +++ b/plugins/main/public/services/routes.js @@ -15,12 +15,7 @@ import 'angular-route'; // Functions to be executed before loading certain routes -import { - settingsWizard, - getSavedSearch, - getIp, - getWzConfig, -} from './resolves'; +import { settingsWizard, getSavedSearch, getIp, getWzConfig } from './resolves'; // HTML templates import healthCheckTemplate from '../templates/health-check/health-check.html'; @@ -52,12 +47,7 @@ const assignPreviousLocation = ($rootScope, $location) => { function ip($q, $rootScope, $window, $location) { const wzMisc = new WzMisc(); assignPreviousLocation($rootScope, $location); - return getIp( - $q, - $window, - $location, - wzMisc - ); + return getIp($q, $window, $location, wzMisc); } function nestedResolve($q, errorHandler, $rootScope, $location, $window) { @@ -77,25 +67,16 @@ function nestedResolve($q, errorHandler, $rootScope, $location, $window) { GenericRequest, errorHandler, wzMisc, - location && location.includes('/health-check') - ) + location && location.includes('/health-check'), + ), ); } -function savedSearch( - $location, - $window, - $rootScope, - $route -) { +function savedSearch($location, $window, $rootScope, $route) { const healthCheckStatus = $window.sessionStorage.getItem('healthCheck'); if (!healthCheckStatus) return; assignPreviousLocation($rootScope, $location); - return getSavedSearch( - $location, - $window, - $route - ); + return getSavedSearch($location, $window, $route); } function wzConfig($q, $rootScope, $location) { @@ -112,7 +93,7 @@ function clearRuleId(commonData) { function enableWzMenu($rootScope, $location) { const location = $location.path(); $rootScope.hideWzMenu = location.includes('/health-check'); - if(!$rootScope.hideWzMenu){ + if (!$rootScope.hideWzMenu) { AppState.setWzMenu(); } } @@ -120,73 +101,73 @@ function enableWzMenu($rootScope, $location) { //Routes const app = getAngularModule(); -app.config(($routeProvider) => { +app.config($routeProvider => { $routeProvider - .when('/health-check', { - template: healthCheckTemplate, - resolve: { wzConfig, ip }, - outerAngularWrapperRoute: true - }) - .when('/agents/:agent?/:tab?/:tabView?', { - template: agentsTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/agents-preview/', { - template: agentsPrevTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/manager/', { - template: managementTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/manager/:tab?', { - template: managementTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, - outerAngularWrapperRoute: true - }) - .when('/overview/', { - template: overviewTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/settings', { - template: settingsTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - reloadOnSearch: false, - outerAngularWrapperRoute: true - }) - .when('/security', { - template: securityTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - outerAngularWrapperRoute: true - }) - .when('/wazuh-dev', { - template: toolsTemplate, - resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, - outerAngularWrapperRoute: true - }) - .when('/blank-screen', { - template: blankScreenTemplate, - resolve: { enableWzMenu }, - outerAngularWrapperRoute: true - }) - .when('/', { - redirectTo: '/overview/', - outerAngularWrapperRoute: true - }) - .when('', { - redirectTo: '/overview/', - outerAngularWrapperRoute: true - }) - .otherwise({ - redirectTo: '/overview', - outerAngularWrapperRoute: true - }); + .when('/health-check', { + template: healthCheckTemplate, + resolve: { wzConfig, ip }, + outerAngularWrapperRoute: true, + }) + .when('/agents/:agent?/:tab?/:tabView?', { + template: agentsTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/agents-preview/', { + template: agentsPrevTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/manager/', { + template: managementTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/manager/:tab?', { + template: managementTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch, clearRuleId }, + outerAngularWrapperRoute: true, + }) + .when('/overview/', { + template: overviewTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/settings', { + template: settingsTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + reloadOnSearch: false, + outerAngularWrapperRoute: true, + }) + .when('/security', { + template: securityTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + outerAngularWrapperRoute: true, + }) + .when('/wazuh-dev', { + template: toolsTemplate, + resolve: { enableWzMenu, nestedResolve, ip, savedSearch }, + outerAngularWrapperRoute: true, + }) + .when('/blank-screen', { + template: blankScreenTemplate, + resolve: { enableWzMenu }, + outerAngularWrapperRoute: true, + }) + .when('/', { + redirectTo: '/overview/', + outerAngularWrapperRoute: true, + }) + .when('', { + redirectTo: '/overview/', + outerAngularWrapperRoute: true, + }) + .otherwise({ + redirectTo: '/overview', + outerAngularWrapperRoute: true, + }); }); diff --git a/plugins/main/public/styles/theme/dark/index.dark.scss b/plugins/main/public/styles/theme/dark/index.dark.scss index 2e28319801..c7277ec5fb 100644 --- a/plugins/main/public/styles/theme/dark/index.dark.scss +++ b/plugins/main/public/styles/theme/dark/index.dark.scss @@ -6,15 +6,15 @@ body, html.md-default-theme, html { color: #dfe5ef !important; - background-color: #1a1b20!important; + background-color: #1a1b20 !important; } -.application{ +.application { background: #1a1b20; } #kibana-body { - background-color: #1a1b20!important; + background-color: #1a1b20 !important; } .euiHeaderSectionItem__button, @@ -28,7 +28,7 @@ html { } */ .wz-global-breadcrumb .euiToolTipAnchor { - color: #98A2B3!important; + color: #98a2b3 !important; } .app-wrapper-panel { @@ -36,8 +36,9 @@ html { } .wz-md-card:not(.wz-metric-color) { - box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), 0 1px 5px -2px rgba(0, 0, 0, 0.3); - background-color: #1D1E24; + box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), + 0 1px 5px -2px rgba(0, 0, 0, 0.3); + background-color: #1d1e24; border: 1px solid #343741; } @@ -46,22 +47,23 @@ html { border-bottom: 1px solid #343741; } -.wz-card-actions.wz-card-actions-top, .columns-bar-active { - border-bottom: 1px solid #343741!important; +.wz-card-actions.wz-card-actions-top, +.columns-bar-active { + border-bottom: 1px solid #343741 !important; background: #16171c; color: #dfe5ef; - border-top: none!important; + border-top: none !important; } .kuiButton--secondary:enabled:hover { - background: rgba(27, 169, 245, 0.1)!important; - color: #45b9f6!important; - border-color: #1BA9F5!important; + background: rgba(27, 169, 245, 0.1) !important; + color: #45b9f6 !important; + border-color: #1ba9f5 !important; } .kuiButton--secondary { - color: #45b9f6!important; - border-color: #1BA9F5; + color: #45b9f6 !important; + border-color: #1ba9f5; background: transparent; } @@ -76,7 +78,7 @@ html { } .registerAgent { - background: #1a1b20!important; + background: #1a1b20 !important; } .json-beautifier { @@ -89,7 +91,7 @@ html { border-color: #343741; } -.kuiSelect{ +.kuiSelect { filter: invert(1); } @@ -114,10 +116,9 @@ md-content { } .visLegend__toggle { - color: white!important; + color: white !important; } - .euiBreadcrumbs--truncate .euiBreadcrumb:not(.euiBreadcrumb--collapsed).euiBreadcrumb--last, .euiNavDrawerGroup__item .euiListGroupItem__label, @@ -131,11 +132,12 @@ md-content { .wz-nav-item button.md-primary { color: #0079a5 !important; - background-color: #232635!important; - border-bottom: 2px solid #006BB4; + background-color: #232635 !important; + border-bottom: 2px solid #006bb4; } -md-nav-bar.md-default-theme .md-nav-bar, md-nav-bar .md-nav-bar { +md-nav-bar.md-default-theme .md-nav-bar, +md-nav-bar .md-nav-bar { border-color: rgb(52, 55, 65); } @@ -144,8 +146,8 @@ md-nav-bar.md-default-theme .md-nav-bar, md-nav-bar .md-nav-bar { } .sidebar-container .index-pattern { - background-color: #1ba9f5!important; - color: white!important; + background-color: #1ba9f5 !important; + color: white !important; } .wz-menu { @@ -180,7 +182,8 @@ md-nav-bar.md-default-theme .md-nav-bar, md-nav-bar .md-nav-bar { color: #dfe5ef; } -.md-subheader.md-default-theme, .md-subheader { +.md-subheader.md-default-theme, +.md-subheader { color: #dfe5ef; } @@ -205,18 +208,19 @@ table thead > tr { color: #dfe5ef; } -#wz-search-filter-bar-input{ +#wz-search-filter-bar-input { box-shadow: none; } -.kuiLocalSearchInput, .kuiLocalSearchInput:focus { +.kuiLocalSearchInput, +.kuiLocalSearchInput:focus { border: 1px solid #343741 !important; background: #16171c; color: #dfe5ef; } .wzMultipleSelector .panel-primary { - border: 1px solid #343741!important; + border: 1px solid #343741 !important; -webkit-box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important; box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important; border-radius: 2px; @@ -239,11 +243,12 @@ table thead > tr { border-left: 1px dashed #343741; } -md-dialog.md-default-theme.md-content-overflow .md-actions, -md-dialog.md-content-overflow .md-actions, -md-dialog.md-default-theme.md-content-overflow md-dialog-actions, -md-dialog.md-content-overflow md-dialog-actions, -md-divider.md-default-theme, md-divider { +md-dialog.md-default-theme.md-content-overflow .md-actions, +md-dialog.md-content-overflow .md-actions, +md-dialog.md-default-theme.md-content-overflow md-dialog-actions, +md-dialog.md-content-overflow md-dialog-actions, +md-divider.md-default-theme, +md-divider { border-top-color: rgb(52, 55, 65); } @@ -272,18 +277,18 @@ md-divider.md-default-theme, md-divider { background-color: #0b4462; } -.CodeMirror-hints{ +.CodeMirror-hints { background-color: #16171c !important; border-color: #000; - color: #dfe5ef!important; + color: #dfe5ef !important; } -.CodeMirror-hint{ - color: #dfe5ef!important; +.CodeMirror-hint { + color: #dfe5ef !important; } -.CodeMirror-hint:hover{ - background-color: #25262E; +.CodeMirror-hint:hover { + background-color: #25262e; } .wz-input-text { @@ -293,7 +298,7 @@ md-divider.md-default-theme, md-divider { } .wz-menu { - box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3)!important; + box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3) !important; } .wz-menu-select { @@ -309,29 +314,36 @@ md-divider.md-default-theme, md-divider { } .extraHeader { - border-bottom: 1px solid #2e2f34!important; + border-bottom: 1px solid #2e2f34 !important; } -.wzMultipleSelectorAdding{ - background-color: #037200!important; +.wzMultipleSelectorAdding { + background-color: #037200 !important; } -.wzMultipleSelectorRemoving{ - background-color: #990000!important; +.wzMultipleSelectorRemoving { + background-color: #990000 !important; } -.wzMultipleSelectorSelect{ +.wzMultipleSelectorSelect { background-color: #16171c; border: 1px solid rgb(52, 55, 65); } -.wz-button, .wz-button-groups, .refresh-agents-btn { - background-color: #1BA9F5 !important; - border-color: #1BA9F5 !important; +.wz-button, +.wz-button-groups, +.refresh-agents-btn { + background-color: #1ba9f5 !important; + border-color: #1ba9f5 !important; color: #000 !important; } -.wz-button-groups.active, .wz-button-groups:not([disabled]):hover, .wz-button.active, .wz-button:not([disabled]):hover, .wz-button-flat:not([disabled]):hover, .refresh-agents-btn:hover { +.wz-button-groups.active, +.wz-button-groups:not([disabled]):hover, +.wz-button.active, +.wz-button:not([disabled]):hover, +.wz-button-flat:not([disabled]):hover, +.refresh-agents-btn:hover { background-color: #0a9dec !important; border-color: #0a9dec !important; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 2px 2px -1px rgba(0, 0, 0, 0.3) !important; @@ -339,77 +351,78 @@ md-divider.md-default-theme, md-divider { } .kuiButton--hollow:hover { - color: #006E8A !important; + color: #006e8a !important; text-decoration: underline !important; } - .wz-menu-select { - filter: invert(0) !important; + filter: invert(0) !important; } -.logtest{ - border-left: 1px solid #343741!important; - box-shadow: -2px 0px 2px -1px rgba(0, 0, 0, 0.3)!important; +.logtest { + border-left: 1px solid #343741 !important; + box-shadow: -2px 0px 2px -1px rgba(0, 0, 0, 0.3) !important; background: #1a1b20; z-index: 10; } .wz-menu-left-side { - border-right: 1px solid #343741!important; - background: #1d1e24!important; + border-right: 1px solid #343741 !important; + background: #1d1e24 !important; } .wz-menu-sections { background: #1a1b20; } - -.wz-module-header-agent, .wz-module-header-nav { - border-bottom: 1px solid #343741!important; - background: #1d1e24!important; +.wz-module-header-agent, +.wz-module-header-nav { + border-bottom: 1px solid #343741 !important; + background: #1d1e24 !important; } .wz-welcome-page-agent-info { - box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), 0 1px 5px -2px rgba(0, 0, 0, 0.3)!important; - background: #1d1e24!important; + box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), + 0 1px 5px -2px rgba(0, 0, 0, 0.3) !important; + background: #1d1e24 !important; } -.wz-welcome-page-agent-info .wz-welcome-page-agent-info-details{ - background: #1a1b20!important; - border-bottom: 1px solid #343741!important; +.wz-welcome-page-agent-info .wz-welcome-page-agent-info-details { + background: #1a1b20 !important; + border-bottom: 1px solid #343741 !important; } .details-row { - background: #16171c!important; - border-top: 1px solid #343741!important; + background: #16171c !important; + border-top: 1px solid #343741 !important; } -.wz-inventory{ +.wz-inventory { .detail-tooltip { background-color: #16171c; } } .flyout-body .euiAccordion { - border-bottom: 1px solid #343741!important; + border-bottom: 1px solid #343741 !important; } .module-discover-table .euiTableRow.euiTableRow-isExpandedRow .euiTableRowCell { - background: #1d1e24!important; + background: #1d1e24 !important; } .module-discover-table .euiTableRow-isExpandedRow .euiTableCellContent { - background: #1d1e24!important; + background: #1d1e24 !important; } -.wz-search-bar > div > div > div.euiComboBox__inputWrap{ - background: #16171c!important; - box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.2), 0 3px 2px -2px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(255, 255, 255, 0.1)!important; +.wz-search-bar > div > div > div.euiComboBox__inputWrap { + background: #16171c !important; + box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.2), + 0 3px 2px -2px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(255, 255, 255, 0.1) !important; } .euiComboBoxPlaceholder { - color: #DFE5EF!important; + color: #dfe5ef !important; } svg .legend text { @@ -418,7 +431,7 @@ svg .legend text { /* welcome-agent */ -.wz-welcome-page-agent-tabs{ +.wz-welcome-page-agent-tabs { padding: 12px 16px 1px 10px; min-height: 54px; border-bottom: 1px solid #343741; @@ -436,15 +449,16 @@ svg .legend text { .wz-menu-agent-info { background-color: #1a1b20; - border-bottom: 1px solid #343741!important; + border-bottom: 1px solid #343741 !important; } .flyout-row { border: none; } -.application .euiAccordion, .flyout-body .euiAccordion { - border-bottom: 1px solid #343741!important; +.application .euiAccordion, +.flyout-body .euiAccordion { + border-bottom: 1px solid #343741 !important; } .sidepanel-infoBtnStyle { diff --git a/plugins/main/public/templates/agents-prev/agents-prev.html b/plugins/main/public/templates/agents-prev/agents-prev.html index cec8cb3637..573720fea4 100644 --- a/plugins/main/public/templates/agents-prev/agents-prev.html +++ b/plugins/main/public/templates/agents-prev/agents-prev.html @@ -1,6 +1,14 @@ -
      +
      - +
      -
      +
      - Error fetching - agents + + Error fetching agents

      {{ ctrl.errorInit || 'Internal error' }}

      -
      @@ -37,7 +60,10 @@ layout-align="start space-around" >
      - +
      diff --git a/plugins/main/public/templates/visualize/dashboards.html b/plugins/main/public/templates/visualize/dashboards.html index 30eba4a8d5..1dff0934c2 100644 --- a/plugins/main/public/templates/visualize/dashboards.html +++ b/plugins/main/public/templates/visualize/dashboards.html @@ -1,6 +1,9 @@
      -
      - +
      +
      @@ -34,7 +37,9 @@ ng-if="reportBusy && reportStatus && showModuleDashboard" class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--justifyContentSpaceAround euiFlexGroup--directionRow euiFlexGroup--responsive" > -
      +
      @@ -82,7 +87,8 @@ d="M13.6 12.186l-1.357-1.358c-.025-.025-.058-.034-.084-.056.53-.794.84-1.746.84-2.773a4.977 4.977 0 0 0-.84-2.772c.026-.02.059-.03.084-.056L13.6 3.813a6.96 6.96 0 0 1 0 8.373zM8 15A6.956 6.956 0 0 1 3.814 13.6l1.358-1.358c.025-.025.034-.057.055-.084C6.02 12.688 6.974 13 8 13a4.978 4.978 0 0 0 2.773-.84c.02.026.03.058.056.083l1.357 1.358A6.956 6.956 0 0 1 8 15zm-5.601-2.813a6.963 6.963 0 0 1 0-8.373l1.359 1.358c.024.025.057.035.084.056A4.97 4.97 0 0 0 3 8c0 1.027.31 1.98.842 2.773-.027.022-.06.031-.084.056l-1.36 1.358zm5.6-.187A4 4 0 1 1 8 4a4 4 0 0 1 0 8zM8 1c1.573 0 3.019.525 4.187 1.4l-1.357 1.358c-.025.025-.035.057-.056.084A4.979 4.979 0 0 0 8 3a4.979 4.979 0 0 0-2.773.842c-.021-.027-.03-.059-.055-.084L3.814 2.4A6.957 6.957 0 0 1 8 1zm0-1a8.001 8.001 0 1 0 .003 16.002A8.001 8.001 0 0 0 8 0z" > - + + No agents were added to this manager: @@ -90,8 +96,14 @@
      -
      -
      +
      +
      -
      +
      - +
      diff --git a/plugins/main/public/utils/assets.ts b/plugins/main/public/utils/assets.ts index 12d02c6029..771139a85a 100644 --- a/plugins/main/public/utils/assets.ts +++ b/plugins/main/public/utils/assets.ts @@ -1,7 +1,8 @@ import { ASSETS_BASE_URL_PREFIX } from '../../common/constants'; import { getUiSettings } from '../kibana-services'; -export const getAssetURL = (assetURL: string) => `${ASSETS_BASE_URL_PREFIX}${assetURL}`; +export const getAssetURL = (assetURL: string) => + `${ASSETS_BASE_URL_PREFIX}${assetURL}`; export const getThemeAssetURL = (asset: string, theme?: string) => { theme = theme || (getUiSettings()?.get('theme:darkMode') ? 'dark' : 'light'); From 7d42afa911272ce66acc30b3d59b2229a07c37e7 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 2 Aug 2023 10:45:55 -0300 Subject: [PATCH 30/46] Add password scaped singlequote --- .../register-agent/register-agent.tsx | 10 +++---- .../register-agent/containers/steps/steps.tsx | 12 +-------- .../services/wazuh-password-service.test.ts | 15 ++++------- .../services/wazuh-password-service.ts | 27 ++++++++++--------- 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx index 7478f6e06a..43ae9a87ba 100644 --- a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx +++ b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -30,6 +30,7 @@ import { validateServerAddress, validateAgentName, } from '../../utils/validations'; +import { getPasswordWithScapedSpecialCharacters } from '../../services/wazuh-password-service'; interface IRegisterAgentProps { getWazuhVersion: () => Promise; @@ -121,14 +122,13 @@ export const RegisterAgent = withReduxProvider( const authInfo = await getAuthInfo(); // get wazuh password configuration let wazuhPassword = ''; - //const needsPassword = (authInfo.auth || {}).use_password === 'yes'; - const needsPassword = true; + const needsPassword = (authInfo.auth || {}).use_password === 'yes'; if (needsPassword) { - /*wazuhPassword = + wazuhPassword = configuration['enrollment.password'] || authInfo['authd.pass'] || - '';*/ - wazuhPassword = `password"with"singlequote`; + ''; + wazuhPassword = getPasswordWithScapedSpecialCharacters(wazuhPassword); } const groups = await getGroups(); setNeedsPassword(needsPassword); diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx index c96fe059f4..24ec3e74d6 100644 --- a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx @@ -211,17 +211,7 @@ export const Steps = ({ showCommand={showCommandsSections(form.fields)} os={registerAgentFormValues.operatingSystem.name} onCopy={() => setInstallCommandWasCopied(true)} - password={ - registerAgentFormValues.operatingSystem.name === 'macOS' - ? getPasswordForCommand( - registerAgentFormValues.optionalParams.wazuhPassword, - 'doublequote', - ) - : getPasswordForCommand( - registerAgentFormValues.optionalParams.wazuhPassword, - 'singlequote', - ) - } + password={registerAgentFormValues.optionalParams.wazuhPassword} /> ) : null} diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index 63b04b99c9..dadac9a181 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -1,15 +1,10 @@ -import { getPasswordForCommand } from './wazuh-password-service'; +import { getPasswordWithScapedSpecialCharacters } from './wazuh-password-service'; describe('Wazuh Password Service', () => { - it('should return the password wrapped with singlequote and scaped the inner special characters', () => { - const password = `password'with'singlequote`; - const result = getPasswordForCommand(password, 'singlequote'); - expect(result).toBe('password\'with\'singlequote'); + it('should return the password scaped the inner special characters', () => { + const password = "password'with'special'characters"; + const passwordScaped = getPasswordWithScapedSpecialCharacters(password); + expect(passwordScaped).toEqual("password\'with\'special\'characters"); }); - it('should return the password wrapped with doublequote and scaped the inner special characters', () => { - const password = `password"with"doublequote`; - const result = getPasswordForCommand(password, 'doublequote'); - expect(result).toBe("password\"with\"doublequote"); - }); }); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index f66e46e524..eba0dfc1d3 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -1,14 +1,15 @@ -export const getPasswordForCommand = ( - password: string, - wrapperType: 'doublequote' | 'singlequote', +export const getPasswordWithScapedSpecialCharacters = ( + password: string ) => { - - - if(wrapperType === 'doublequote') { - // scape doublequote in password - return password.replace(/"/g, '\"'); - } else { - // scape singlequote in password - return password.replace(/'/g, "\'"); - } -}; + let passwordScaped = password; + // the " characters is scaped by default in the password retrieved from the API + const specialCharsList = ["'"]; + specialCharsList.forEach((specialChar) => { + // scape every special character defined in specialCharList with a backslash + passwordScaped = passwordScaped.replace( + new RegExp(specialChar, 'g'), + `\\${specialChar}` + ); + }); + return passwordScaped; +}; \ No newline at end of file From 4cf09f2e02dc8af32f24b723181f0a8c4b8c4cfc Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Thu, 3 Aug 2023 08:19:29 -0300 Subject: [PATCH 31/46] Add new wazuh password service for scape special characters with unit tests --- .../command-output/command-output.tsx | 39 +++++++++++-------- .../services/wazuh-password-service.test.ts | 31 ++++++++++++--- .../services/wazuh-password-service.ts | 12 ++---- 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index d460951359..a31d34863f 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -18,7 +18,7 @@ interface ICommandSectionProps { } export default function CommandOutput(props: ICommandSectionProps) { - const { commandText, showCommand, onCopy, os, password} = props; + const { commandText, showCommand, onCopy, os, password } = props; const getHighlightCodeLanguage = (os: 'WINDOWS' | string) => { if (os.toLowerCase() === 'windows') { return 'powershell'; @@ -44,32 +44,33 @@ export default function CommandOutput(props: ICommandSectionProps) { setHavePassword(false); setCommandToShow(commandText); } - }, [password, commandText, showPassword]) + }, [password, commandText, showPassword]); const osdfucatePassword = (password: string) => { - if(!password) return; - if(!commandText) return; + if (!password) return; + if (!commandText) return; - if(showPassword){ + if (showPassword) { setCommandToShow(commandText); - }else{ - // search password in commandText and replace with * for every character - const findPassword = commandText.search(password); - if (findPassword > -1) { - let command = commandText; - setCommandToShow(command.replace(/WAZUH_REGISTRATION_PASSWORD='([^']+)'/,`WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`)); - } + } else { + let command = commandText; + setCommandToShow( + command.replace( + `WAZUH_REGISTRATION_PASSWORD='${password}'`, + `WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`, + ), + ); } - } + }; const onChangeShowPassword = (event: EuiSwitchEvent) => { setShowPassword(event.target.checked); - } + }; return ( - { password } + {password}
      - {showCommand && havePassword ? : null} + {showCommand && havePassword ? ( + + ) : null} ); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index dadac9a181..0661d10db2 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -1,10 +1,31 @@ import { getPasswordWithScapedSpecialCharacters } from './wazuh-password-service'; describe('Wazuh Password Service', () => { - it('should return the password scaped the inner special characters', () => { - const password = "password'with'special'characters"; - const passwordScaped = getPasswordWithScapedSpecialCharacters(password); - expect(passwordScaped).toEqual("password\'with\'special\'characters"); - }); + // NOTE: + // The password constant must be written as it comes from the backend + // The expectedPassword variable must be written taking into account how the \ will be escaped + it.each` + passwordFromAPI | expectedScapedPassword + ${"password'with'special'char`acters"} | ${"password\\'with\\'special\\'char\\`acters"} + ${'password"with"doublequ\'sds\\"es'} | ${'password"with"doublequ\\\'sds\\"es'} + ${'password"with"doub``le`qu\'sds\\"es'} | ${'password"with"doub\\`\\`le\\`qu\\\'sds\\"es'} + `( + ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', + ({ passwordFromAPI, expectedScapedPassword }) => { + const passwordScaped = getPasswordWithScapedSpecialCharacters(passwordFromAPI); + /* log to compare passwords */ + console.log( + 'PASSWORD REAL: ', + passwordFromAPI, + '\nPASSWORD BACKEND: ', + JSON.stringify(passwordFromAPI), + '\nRESULT PASSWORD SCAPED REAL IN COMMAND: ', + passwordScaped, + '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', + expectedScapedPassword + ); + expect(passwordScaped).toEqual(expectedScapedPassword); + } + ); }); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index eba0dfc1d3..dbb83c9151 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -3,13 +3,7 @@ export const getPasswordWithScapedSpecialCharacters = ( ) => { let passwordScaped = password; // the " characters is scaped by default in the password retrieved from the API - const specialCharsList = ["'"]; - specialCharsList.forEach((specialChar) => { - // scape every special character defined in specialCharList with a backslash - passwordScaped = passwordScaped.replace( - new RegExp(specialChar, 'g'), - `\\${specialChar}` - ); - }); - return passwordScaped; + const specialCharsList = ["'", "`"]; + const regex = new RegExp(`[${specialCharsList.join('')}]`, 'g'); + return passwordScaped.replace(regex, `\\$&`); }; \ No newline at end of file From 3956dd5ea806608f7380d45566d67fac32489290 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Thu, 3 Aug 2023 08:50:09 -0300 Subject: [PATCH 32/46] Add new unit test case --- .../register-agent/components/command-output/command-output.tsx | 2 +- .../register-agent/services/wazuh-password-service.test.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index a31d34863f..bf6f430394 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -70,7 +70,7 @@ export default function CommandOutput(props: ICommandSectionProps) { return ( - {password} + {JSON.stringify(password)}
      { ${"password'with'special'char`acters"} | ${"password\\'with\\'special\\'char\\`acters"} ${'password"with"doublequ\'sds\\"es'} | ${'password"with"doublequ\\\'sds\\"es'} ${'password"with"doub``le`qu\'sds\\"es'} | ${'password"with"doub\\`\\`le\\`qu\\\'sds\\"es'} + ${"password\"with\"doubleq\\'usds\\\"es"}| ${"password\"with\"doubleq\\\'usds\\\"es"} `( ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', ({ passwordFromAPI, expectedScapedPassword }) => { From d39fd3fc7df904d631a927b5a71d3f154a3a7c2d Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Thu, 3 Aug 2023 18:09:02 -0300 Subject: [PATCH 33/46] Fix service and added unit tests --- .../command-output/command-output.tsx | 36 +++++++----- .../register-agent-os-commands-services.tsx | 58 +++++++++++++------ .../services/wazuh-password-service.test.ts | 6 +- .../services/wazuh-password-service.ts | 10 ++-- 4 files changed, 67 insertions(+), 43 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index bf6f430394..7b79dd33a9 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -19,13 +19,6 @@ interface ICommandSectionProps { export default function CommandOutput(props: ICommandSectionProps) { const { commandText, showCommand, onCopy, os, password } = props; - const getHighlightCodeLanguage = (os: 'WINDOWS' | string) => { - if (os.toLowerCase() === 'windows') { - return 'powershell'; - } else { - return 'bash'; - } - }; const [havePassword, setHavePassword] = useState(false); const [showPassword, setShowPassword] = useState(false); @@ -54,12 +47,26 @@ export default function CommandOutput(props: ICommandSectionProps) { setCommandToShow(commandText); } else { let command = commandText; - setCommandToShow( - command.replace( - `WAZUH_REGISTRATION_PASSWORD='${password}'`, - `WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`, - ), - ); + if (os?.toLocaleLowerCase() === 'macos') { + const regex = /WAZUH_REGISTRATION_PASSWORD='((?:\\'|[^'])*)'/g; + const replacedString = command.replace( + regex, + (match, capturedGroup) => { + return match.replace( + capturedGroup, + '*'.repeat(capturedGroup.length), + ); + }, + ); + setCommandToShow(replacedString); + } else { + setCommandToShow( + command.replace( + `WAZUH_REGISTRATION_PASSWORD='${password}'`, + `WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`, + ), + ); + } } }; @@ -70,14 +77,13 @@ export default function CommandOutput(props: ICommandSectionProps) { return ( - {JSON.stringify(password)}
      {showCommand ? commandToShow : ''} diff --git a/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx b/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx index 818a91945a..87064e8f12 100644 --- a/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx +++ b/plugins/main/public/controllers/register-agent/services/register-agent-os-commands-services.tsx @@ -28,34 +28,34 @@ const getAllOptionals = ( if (osName && osName.toLowerCase() === 'windows' && optionals.serverAddress) { // when os is windows we must to add wazuh registration server with server address paramsText = - paramsText + `WAZUH_REGISTRATION_SERVER=${optionals.serverAddress.replace('WAZUH_MANAGER=','')} `; + paramsText + + `WAZUH_REGISTRATION_SERVER=${optionals.serverAddress.replace( + 'WAZUH_MANAGER=', + '', + )} `; } return paramsText; }; const getAllOptionalsMacos = ( - optionals: IOptionalParameters + optionals: IOptionalParameters, ) => { // create paramNameOrderList, which is an array of the keys of optionals add interface const paramNameOrderList: (keyof IOptionalParameters)[] = - ['serverAddress', 'wazuhPassword', 'agentGroups', 'agentName', 'protocol']; + ['serverAddress', 'agentGroups', 'agentName', 'protocol']; if (!optionals) return ''; - return Object.entries(paramNameOrderList).reduce( - (acc, [key, value]) => { - if (optionals[value]) { - acc += `${optionals[value]}\\n`; - } - return acc; - }, - '', - ); + return Object.entries(paramNameOrderList).reduce((acc, [key, value]) => { + if (optionals[value]) { + acc += `${optionals[value]}\\n`; + } + return acc; + }, ''); }; /******* Linux *******/ - // Install command export const getLinuxRPMInstallCommand = ( props: tOSEntryInstallCommand, @@ -101,20 +101,40 @@ export const getWindowsStartCommand = ( /******** MacOS ********/ +export const transformOptionalsParamatersMacOSCommand = (command: string) => { + return command + .replace(/\' ([a-zA-Z])/g, "' && $1") // Separate environment variables with && + .replace(/\"/g, '\\"') // Escape double quotes + .trim(); +}; + export const getMacOsInstallCommand = ( props: tOSEntryInstallCommand, ) => { const { optionals, urlPackage } = props; // Set macOS installation script with environment variables const optionalsText = optionals && getAllOptionalsMacos(optionals); - const macOSInstallationOptions = `${optionalsText}` - .replace(/\' ([a-zA-Z])/g, "' && $1") // Separate environment variables with && - .replace(/\"/g, '\\"') // Escape double quotes - .trim(); - + const macOSInstallationOptions = transformOptionalsParamatersMacOSCommand( + optionalsText || '', + ); + let wazuhPasswordParamWithValue = ''; + if (optionals?.wazuhPassword) { + /** + * We use the JSON.stringify to prevent that the scaped specials characters will be removed + * and mantain the format of the password + The JSON.stringify mantain the password format but adds " to wrap the characters + */ + const scapedPasswordLength = JSON.stringify( + optionals?.wazuhPassword, + ).length; + // We need to remove the " added by JSON.stringify + wazuhPasswordParamWithValue = `${JSON.stringify( + optionals?.wazuhPassword, + ).substring(1, scapedPasswordLength - 1)}\\n`; + } // If no variables are set, the echo will be empty const macOSInstallationSetEnvVariablesScript = macOSInstallationOptions - ? `echo -e "${macOSInstallationOptions}" > /tmp/wazuh_envs && ` + ? `echo -e "${macOSInstallationOptions}${wazuhPasswordParamWithValue}" > /tmp/wazuh_envs && ` : ``; // Merge environment variables with installation script diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index d40c828752..99b48b4834 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -1,5 +1,4 @@ import { getPasswordWithScapedSpecialCharacters } from './wazuh-password-service'; - describe('Wazuh Password Service', () => { // NOTE: // The password constant must be written as it comes from the backend @@ -9,7 +8,9 @@ describe('Wazuh Password Service', () => { ${"password'with'special'char`acters"} | ${"password\\'with\\'special\\'char\\`acters"} ${'password"with"doublequ\'sds\\"es'} | ${'password"with"doublequ\\\'sds\\"es'} ${'password"with"doub``le`qu\'sds\\"es'} | ${'password"with"doub\\`\\`le\\`qu\\\'sds\\"es'} - ${"password\"with\"doubleq\\'usds\\\"es"}| ${"password\"with\"doubleq\\\'usds\\\"es"} + ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\\'usds\\\"es"} + ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\\'usds\\\"es"} + ${"pas`sw\\`ord\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"pas\\`sw\\\`ord\"with\"doubleq\\\\\'\\usds\\\"\\es"} `( ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', ({ passwordFromAPI, expectedScapedPassword }) => { @@ -25,7 +26,6 @@ describe('Wazuh Password Service', () => { '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', expectedScapedPassword ); - expect(passwordScaped).toEqual(expectedScapedPassword); } ); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index dbb83c9151..cd9151d40b 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -1,9 +1,7 @@ -export const getPasswordWithScapedSpecialCharacters = ( - password: string -) => { +export const getPasswordWithScapedSpecialCharacters = (password: string) => { let passwordScaped = password; // the " characters is scaped by default in the password retrieved from the API - const specialCharsList = ["'", "`"]; - const regex = new RegExp(`[${specialCharsList.join('')}]`, 'g'); + const specialCharsList = ["'", '`', '!']; + const regex = new RegExp(`(? Date: Fri, 4 Aug 2023 14:08:18 -0300 Subject: [PATCH 34/46] Comment logs in service unit test --- .../register-agent/services/wazuh-password-service.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index 99b48b4834..57c35eae75 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -15,7 +15,7 @@ describe('Wazuh Password Service', () => { ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', ({ passwordFromAPI, expectedScapedPassword }) => { const passwordScaped = getPasswordWithScapedSpecialCharacters(passwordFromAPI); - /* log to compare passwords */ + /* log to compare passwords console.log( 'PASSWORD REAL: ', passwordFromAPI, @@ -26,6 +26,7 @@ describe('Wazuh Password Service', () => { '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', expectedScapedPassword ); + */ expect(passwordScaped).toEqual(expectedScapedPassword); } ); From 755942bc50750e780224b34c2230f1e4e32b50b3 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Fri, 4 Aug 2023 14:09:46 -0300 Subject: [PATCH 35/46] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef1f96f0f0..c94181da0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Support for Wazuh 4.7.0 - Added `status detail` column in the agents table. [#5680](https://github.com/wazuh/wazuh-kibana-app/pull/5680) +- Added agent register wizard handle properly special characters in password [5738](https://github.com/wazuh/wazuh-kibana-app/pull/5738) ### Changed From 74fd4ea1eb01225e36287e144810b5c75ad7a5df Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Mon, 7 Aug 2023 10:40:30 -0300 Subject: [PATCH 36/46] Change regex to scape special characters --- .../register-agent/core/config/os-commands-definitions.ts | 2 +- .../register-agent/services/wazuh-password-service.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts index 1391c825a6..c19f4b474f 100644 --- a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts +++ b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -183,7 +183,7 @@ export const optionalParamsDefinitions: tOptionalParams = { property: 'WAZUH_REGISTRATION_PASSWORD', getParamCommand: props => { const { property, value } = props; - return value !== '' ? `${property}='${value}'` : ''; + return value !== '' ? `${property}=$'${value}'` : ''; }, }, }; diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index cd9151d40b..2be3439f87 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -1,7 +1,7 @@ export const getPasswordWithScapedSpecialCharacters = (password: string) => { let passwordScaped = password; // the " characters is scaped by default in the password retrieved from the API - const specialCharsList = ["'", '`', '!']; - const regex = new RegExp(`(? Date: Mon, 7 Aug 2023 14:46:34 -0300 Subject: [PATCH 37/46] Fix unit tests --- .../command-output/command-output.tsx | 4 ++-- .../services/wazuh-password-service.test.ts | 17 ++++++++--------- .../services/wazuh-password-service.ts | 5 +++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index 7bfbdc7f53..7936c44587 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -64,8 +64,8 @@ export default function CommandOutput(props: ICommandSectionProps) { } else { setCommandToShow( command.replace( - `WAZUH_REGISTRATION_PASSWORD='${password}'`, - `WAZUH_REGISTRATION_PASSWORD='${'*'.repeat(password.length)}'`, + `WAZUH_REGISTRATION_PASSWORD=$'${password}'`, + `WAZUH_REGISTRATION_PASSWORD=$'${'*'.repeat(password.length)}'`, ), ); } diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index 57c35eae75..69129d6f03 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -4,18 +4,18 @@ describe('Wazuh Password Service', () => { // The password constant must be written as it comes from the backend // The expectedPassword variable must be written taking into account how the \ will be escaped it.each` - passwordFromAPI | expectedScapedPassword - ${"password'with'special'char`acters"} | ${"password\\'with\\'special\\'char\\`acters"} - ${'password"with"doublequ\'sds\\"es'} | ${'password"with"doublequ\\\'sds\\"es'} - ${'password"with"doub``le`qu\'sds\\"es'} | ${'password"with"doub\\`\\`le\\`qu\\\'sds\\"es'} - ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\\'usds\\\"es"} - ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\\'usds\\\"es"} - ${"pas`sw\\`ord\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"pas\\`sw\\\`ord\"with\"doubleq\\\\\'\\usds\\\"\\es"} + passwordFromAPI | expectedScapedPassword + ${"password'with'special'characters"} | ${"password'\"'\"'with'\"'\"'special'\"'\"'characters"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} + ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\\\usds\\\"\\\\es"} `( ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', ({ passwordFromAPI, expectedScapedPassword }) => { const passwordScaped = getPasswordWithScapedSpecialCharacters(passwordFromAPI); - /* log to compare passwords + /* log to compare passwords */ console.log( 'PASSWORD REAL: ', passwordFromAPI, @@ -26,7 +26,6 @@ describe('Wazuh Password Service', () => { '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', expectedScapedPassword ); - */ expect(passwordScaped).toEqual(expectedScapedPassword); } ); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index 2be3439f87..babaa7bfe7 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -3,5 +3,6 @@ export const getPasswordWithScapedSpecialCharacters = (password: string) => { // the " characters is scaped by default in the password retrieved from the API const specialCharsList = ["'"]; const regex = new RegExp(`([${specialCharsList.join('')}])`, 'g'); - return passwordScaped.replace(regex, `'"$&"'`); -}; + // the single quote is escaped first, and then any unescaped backslashes are escaped + return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? Date: Mon, 7 Aug 2023 17:26:01 -0300 Subject: [PATCH 38/46] Fix osdfucate password with toggle --- .../command-output/command-output.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index 7936c44587..89fa296387 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -50,7 +50,7 @@ export default function CommandOutput(props: ICommandSectionProps) { } else { let command = commandText; if (os?.toLocaleLowerCase() === 'macos') { - const regex = /WAZUH_REGISTRATION_PASSWORD='((?:\\'|[^'])*)'/g; + const regex = /WAZUH_REGISTRATION_PASSWORD=\$'((?:\\'|[^']|[\"'])*)'/g; const replacedString = command.replace( regex, (match, capturedGroup) => { @@ -58,20 +58,21 @@ export default function CommandOutput(props: ICommandSectionProps) { capturedGroup, '*'.repeat(capturedGroup.length), ); - }, + } ); setCommandToShow(replacedString); } else { - setCommandToShow( - command.replace( - `WAZUH_REGISTRATION_PASSWORD=$'${password}'`, - `WAZUH_REGISTRATION_PASSWORD=$'${'*'.repeat(password.length)}'`, - ), + const replacedString = command.replace( + `WAZUH_REGISTRATION_PASSWORD=\$'${password}'`, + () => { + return `WAZUH_REGISTRATION_PASSWORD=\$\'${'*'.repeat(password.length)}\'`; + } ); + setCommandToShow(replacedString); } } }; - + const onChangeShowPassword = (event: EuiSwitchEvent) => { setShowPassword(event.target.checked); }; From cc74845da4d20c1d3794206eec2c1192857c622d Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Tue, 8 Aug 2023 16:42:29 -0300 Subject: [PATCH 39/46] Change scape and osdfucate password depending on os selected --- .../command-output/command-output.tsx | 24 +------- .../register-agent/register-agent.tsx | 2 +- .../register-agent/containers/steps/steps.tsx | 2 +- .../core/config/os-commands-definitions.ts | 29 +++++++-- .../command-generator/command-generator.ts | 4 +- .../optional-parameters-manager.ts | 12 ++-- .../core/register-commands/types.ts | 9 ++- .../hooks/use-register-agent-commands.ts | 8 +-- .../services/wazuh-password-service.test.ts | 4 +- .../services/wazuh-password-service.ts | 59 ++++++++++++++++++- 10 files changed, 106 insertions(+), 47 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx index 89fa296387..a612f347ed 100644 --- a/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx +++ b/plugins/main/public/controllers/register-agent/components/command-output/command-output.tsx @@ -9,6 +9,7 @@ import { } from '@elastic/eui'; import React, { Fragment, useEffect, useState } from 'react'; import { tOperatingSystem } from '../../core/config/os-commands-definitions'; +import { osdfucatePasswordInCommand } from '../../services/wazuh-password-service'; interface ICommandSectionProps { commandText: string; @@ -48,28 +49,7 @@ export default function CommandOutput(props: ICommandSectionProps) { if (showPassword) { setCommandToShow(commandText); } else { - let command = commandText; - if (os?.toLocaleLowerCase() === 'macos') { - const regex = /WAZUH_REGISTRATION_PASSWORD=\$'((?:\\'|[^']|[\"'])*)'/g; - const replacedString = command.replace( - regex, - (match, capturedGroup) => { - return match.replace( - capturedGroup, - '*'.repeat(capturedGroup.length), - ); - } - ); - setCommandToShow(replacedString); - } else { - const replacedString = command.replace( - `WAZUH_REGISTRATION_PASSWORD=\$'${password}'`, - () => { - return `WAZUH_REGISTRATION_PASSWORD=\$\'${'*'.repeat(password.length)}\'`; - } - ); - setCommandToShow(replacedString); - } + setCommandToShow(osdfucatePasswordInCommand(password, commandText, os)); } }; diff --git a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx index 91ee23aba8..e5e1591c78 100644 --- a/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx +++ b/plugins/main/public/controllers/register-agent/containers/register-agent/register-agent.tsx @@ -129,7 +129,7 @@ export const RegisterAgent = withReduxProvider( configuration['enrollment.password'] || authInfo['authd.pass'] || ''; - wazuhPassword = getPasswordWithScapedSpecialCharacters(wazuhPassword); + //wazuhPassword = getPasswordWithScapedSpecialCharacters(wazuhPassword); } const groups = await getGroups(); setNeedsPassword(needsPassword); diff --git a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx index 596e282e86..653bc929d5 100644 --- a/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx +++ b/plugins/main/public/controllers/register-agent/containers/steps/steps.tsx @@ -114,7 +114,7 @@ export const Steps = ({ ) { selectOS(registerAgentFormValues.operatingSystem as tOperatingSystem); } - setOptionalParams({ ...registerAgentFormValues.optionalParams }); + setOptionalParams({ ...registerAgentFormValues.optionalParams }, registerAgentFormValues.operatingSystem as tOperatingSystem); setInstallCommandWasCopied(false); setStartCommandWasCopied(false); }, [registerAgentFormValues]); diff --git a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts index c19f4b474f..7ca4499b08 100644 --- a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts +++ b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -6,6 +6,7 @@ import { getMacosStartCommand, getWindowsInstallCommand, getWindowsStartCommand } from '../../services/register-agent-os-commands-services'; +import { scapeSpecialCharsForLinux, scapeSpecialCharsForMacOS, scapeSpecialCharsForWindows } from '../../services/wazuh-password-service'; import { IOSDefinition, tOptionalParams } from '../register-commands/types'; // Defined OS combinations @@ -149,21 +150,21 @@ export const osCommandsDefinitions = [ export const optionalParamsDefinitions: tOptionalParams = { serverAddress: { property: 'WAZUH_MANAGER', - getParamCommand: props => { + getParamCommand: (props,selectedOS) => { const { property, value } = props; return value !== '' ? `${property}='${value}'` : ''; }, }, agentName: { property: 'WAZUH_AGENT_NAME', - getParamCommand: props => { + getParamCommand: (props,selectedOS) => { const { property, value } = props; return value !== '' ? `${property}='${value}'` : ''; }, }, agentGroups: { property: 'WAZUH_AGENT_GROUP', - getParamCommand: props => { + getParamCommand: (props,selectedOS) => { const { property, value } = props; let parsedValue = value; if (Array.isArray(value)) { @@ -174,15 +175,33 @@ export const optionalParamsDefinitions: tOptionalParams = { }, protocol: { property: 'WAZUH_PROTOCOL', - getParamCommand: props => { + getParamCommand: (props,selectedOS) => { const { property, value } = props; return value !== '' ? `${property}='${value}'` : ''; }, }, wazuhPassword: { property: 'WAZUH_REGISTRATION_PASSWORD', - getParamCommand: props => { + getParamCommand: (props,selectedOS) => { const { property, value } = props; + if(!value){ + return ''; + } + if(selectedOS){ + let osName = selectedOS.name.toLocaleLowerCase(); + switch(osName){ + case "linux": + return `${property}=$'${scapeSpecialCharsForLinux(value)}'`; + case "macos": + return `${property}=$'${scapeSpecialCharsForMacOS(value)}'`; + case "windows": + return `${property}=$'${scapeSpecialCharsForWindows(value)}'`; + default: + return `${property}=$'${value}'`; + } + + } + return value !== '' ? `${property}=$'${value}'` : ''; }, }, diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts b/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts index 6974478eac..c858fc3908 100644 --- a/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts +++ b/plugins/main/public/controllers/register-agent/core/register-commands/command-generator/command-generator.ts @@ -59,9 +59,9 @@ export class CommandGenerator): void { + addOptionalParams(props: IOptionalParameters, selectedOS?: OS): void { // Get all the optional parameters based on the given parameters - this.optionals = this.optionalsManager.getAllOptionalParams(props); + this.optionals = this.optionalsManager.getAllOptionalParams(props, selectedOS); } /** diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts b/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts index 34b943f797..0328a1efd2 100644 --- a/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts +++ b/plugins/main/public/controllers/register-agent/core/register-commands/optional-parameters-manager/optional-parameters-manager.ts @@ -1,5 +1,6 @@ +import { tOperatingSystem } from '../../config/os-commands-definitions'; import { NoOptionalParamFoundException } from '../exceptions'; -import { IOptionalParamInput, IOptionalParameters, IOptionalParametersManager, tOptionalParams } from '../types'; +import { IOperationSystem, IOptionalParamInput, IOptionalParameters, IOptionalParametersManager, tOptionalParams } from '../types'; export class OptionalParametersManager implements IOptionalParametersManager { constructor(private optionalParamsConfig: tOptionalParams) {} @@ -10,7 +11,7 @@ export class OptionalParametersManager implements IOption * @returns The command string for the given optional parameter. * @throws NoOptionalParamFoundException if the given optional parameter name is not found in the configuration. */ - getOptionalParam(props: IOptionalParamInput) { + getOptionalParam(props: IOptionalParamInput, selectedOS?: IOperationSystem) { const { value, name } = props; if (!this.optionalParamsConfig[name]) { throw new NoOptionalParamFoundException(name); @@ -19,7 +20,8 @@ export class OptionalParametersManager implements IOption value, property: this.optionalParamsConfig[name].property, name - }); + }, + selectedOS); } /** @@ -28,7 +30,7 @@ export class OptionalParametersManager implements IOption * @returns An object containing the command strings for all optional parameters with non-empty values. * @throws NoOptionalParamFoundException if any of the given optional parameter names is not found in the configuration. */ - getAllOptionalParams(paramsValues: IOptionalParameters){ + getAllOptionalParams(paramsValues: IOptionalParameters, selectedOS: IOperationSystem){ // get keys for only the optional params with values !== '' const optionalParams = Object.keys(paramsValues).filter(key => paramsValues[key as keyof typeof paramsValues] !== '') as Array; const resolvedOptionalParams: any = {}; @@ -42,7 +44,7 @@ export class OptionalParametersManager implements IOption name: param as Params, value: paramsValues[param] as string, property: paramDef.property - }) as string; + }, selectedOS) as string; } return resolvedOptionalParams; } diff --git a/plugins/main/public/controllers/register-agent/core/register-commands/types.ts b/plugins/main/public/controllers/register-agent/core/register-commands/types.ts index 243477a999..fc1a0a5638 100644 --- a/plugins/main/public/controllers/register-agent/core/register-commands/types.ts +++ b/plugins/main/public/controllers/register-agent/core/register-commands/types.ts @@ -1,5 +1,8 @@ ///////////////////////////////////////////////////////// /// Domain + +import { tOperatingSystem } from "../../hooks/use-register-agent-commands.test"; + ///////////////////////////////////////////////////////// export interface IOperationSystem { name: string; @@ -51,7 +54,7 @@ export type tOptionalParamsCommandProps = IOptionalParamProps }; export interface IOptionsParamConfig { property: string; - getParamCommand: (props: tOptionalParamsCommandProps) => string; + getParamCommand: (props: tOptionalParamsCommandProps, selectedOS?: IOperationSystem) => string; } export type tOptionalParams = { @@ -64,7 +67,7 @@ export interface IOptionalParamInput { } export interface IOptionalParametersManager { getOptionalParam(props: IOptionalParamInput): string; - getAllOptionalParams(paramsValues: IOptionalParameters): object; + getAllOptionalParams(paramsValues: IOptionalParameters, selectedOs?: IOperationSystem): object; } /////////////////////////////////////////////////////////////////// @@ -79,7 +82,7 @@ export interface ICommandGenerator { selectOS(params: IOperationSystem): void; - addOptionalParams(props: IOptionalParameters): void; + addOptionalParams(props: IOptionalParameters, osSelected?: IOperationSystem): void; getInstallCommand(): string; getStartCommand(): string; getUrlPackage(): string; diff --git a/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts b/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts index 800c198039..fb157c15cd 100644 --- a/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts +++ b/plugins/main/public/controllers/register-agent/hooks/use-register-agent-commands.ts @@ -15,7 +15,7 @@ interface IUseRegisterCommandsProps { selectOS: (params: OS) => void; - setOptionalParams: (params: IOptionalParameters) => void; + setOptionalParams: (params: IOptionalParameters, selectedOS?: OS) => void; installCommand: string; startCommand: string; optionalParamsParsed: IOptionalParameters | {}; @@ -62,7 +62,7 @@ export function useRegisterAgentCommands, + optionalParamsValues as IOptionalParameters, osSelected ); } const installCommand = commandGenerator.getInstallCommand(); @@ -93,8 +93,8 @@ export function useRegisterAgentCommands): void => { - commandGenerator.addOptionalParams(params); + const setOptionalParams = (params: IOptionalParameters, selectedOS?: OS): void => { + commandGenerator.addOptionalParams(params,selectedOS); setOptionalParamsValues(params); setOptionalParamsParsed(commandGenerator.getOptionalParamsCommands()); }; diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index 69129d6f03..2c07c3cef4 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -1,4 +1,4 @@ -import { getPasswordWithScapedSpecialCharacters } from './wazuh-password-service'; +import { scapeSpecialCharsForLinux } from './wazuh-password-service'; describe('Wazuh Password Service', () => { // NOTE: // The password constant must be written as it comes from the backend @@ -14,7 +14,7 @@ describe('Wazuh Password Service', () => { `( ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', ({ passwordFromAPI, expectedScapedPassword }) => { - const passwordScaped = getPasswordWithScapedSpecialCharacters(passwordFromAPI); + const passwordScaped = scapeSpecialCharsForLinux(passwordFromAPI); /* log to compare passwords */ console.log( 'PASSWORD REAL: ', diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index babaa7bfe7..b55720ad0f 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -1,8 +1,63 @@ -export const getPasswordWithScapedSpecialCharacters = (password: string) => { +import { tOperatingSystem } from "../hooks/use-register-agent-commands.test"; + +export const scapeSpecialCharsForLinux = (password: string) => { let passwordScaped = password; // the " characters is scaped by default in the password retrieved from the API const specialCharsList = ["'"]; const regex = new RegExp(`([${specialCharsList.join('')}])`, 'g'); // the single quote is escaped first, and then any unescaped backslashes are escaped return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? { + let passwordScaped = password; + // the " characters is scaped by default in the password retrieved from the API + const specialCharsList = ["'"]; + const regex = new RegExp(`([${specialCharsList.join('')}])`, 'g'); + // the single quote is escaped first, and then any unescaped backslashes are escaped + return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? { + let passwordScaped = password; + // the " characters is scaped by default in the password retrieved from the API + const specialCharsList = ["'"]; + const regex = new RegExp(`([${specialCharsList.join('')}])`, 'g'); + // the single quote is escaped first, and then any unescaped backslashes are escaped + return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? { + let command = commandText; + const osName = os?.toLocaleLowerCase(); + switch (osName){ + case 'macos': + { + const regex = /WAZUH_REGISTRATION_PASSWORD=\$'((?:\\'|[^']|[\"'])*)'/g; + const replacedString = command.replace( + regex, + (match, capturedGroup) => { + return match.replace( + capturedGroup, + '*'.repeat(capturedGroup.length), + ); + } + ); + return replacedString; + } + case 'linux': + case 'windows': + { + const replacedString = command.replace( + `WAZUH_REGISTRATION_PASSWORD=\$'${scapeSpecialCharsForLinux(password)}'`, + () => { + return `WAZUH_REGISTRATION_PASSWORD=\$\'${'*'.repeat(scapeSpecialCharsForLinux(password).length)}\'`; + } + ); + return replacedString; + } + default: + return commandText; + } +} \ No newline at end of file From 12968ee3568d2b4219bc3330802301ef320019ce Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Tue, 8 Aug 2023 16:51:28 -0300 Subject: [PATCH 40/46] Add osdfucate and scape password for windows --- .../core/config/os-commands-definitions.ts | 2 +- .../services/wazuh-password-service.ts | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts index 7ca4499b08..f10b9c597f 100644 --- a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts +++ b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -195,7 +195,7 @@ export const optionalParamsDefinitions: tOptionalParams = { case "macos": return `${property}=$'${scapeSpecialCharsForMacOS(value)}'`; case "windows": - return `${property}=$'${scapeSpecialCharsForWindows(value)}'`; + return `${property}='${scapeSpecialCharsForWindows(value)}'`; default: return `${property}=$'${value}'`; } diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index b55720ad0f..3c4b41f602 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -25,8 +25,8 @@ export const scapeSpecialCharsForWindows = (password: string) => { const specialCharsList = ["'"]; const regex = new RegExp(`([${specialCharsList.join('')}])`, 'g'); // the single quote is escaped first, and then any unescaped backslashes are escaped - return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? { let command = commandText; @@ -46,8 +46,17 @@ export const osdfucatePasswordInCommand = (password: string, commandText: string ); return replacedString; } - case 'linux': case 'windows': + { + const replacedString = command.replace( + `WAZUH_REGISTRATION_PASSWORD=\'${scapeSpecialCharsForWindows(password)}'`, + () => { + return `WAZUH_REGISTRATION_PASSWORD=\'${'*'.repeat(scapeSpecialCharsForWindows(password).length)}\'`; + } + ); + return replacedString; + } + case 'linux': { const replacedString = command.replace( `WAZUH_REGISTRATION_PASSWORD=\$'${scapeSpecialCharsForLinux(password)}'`, From da215c604f1651caa34fed5f6638527383e86538 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 9 Aug 2023 11:13:40 -0300 Subject: [PATCH 41/46] Add windows scape password unit tests --- .../services/wazuh-password-service.test.ts | 85 +++++++++++++------ .../services/wazuh-password-service.ts | 2 +- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index 2c07c3cef4..54b65196f4 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -1,32 +1,63 @@ -import { scapeSpecialCharsForLinux } from './wazuh-password-service'; +import { scapeSpecialCharsForLinux, scapeSpecialCharsForWindows } from './wazuh-password-service'; describe('Wazuh Password Service', () => { // NOTE: // The password constant must be written as it comes from the backend // The expectedPassword variable must be written taking into account how the \ will be escaped - it.each` - passwordFromAPI | expectedScapedPassword - ${"password'with'special'characters"} | ${"password'\"'\"'with'\"'\"'special'\"'\"'characters"} - ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} - ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} - ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} - ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} - ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\\\usds\\\"\\\\es"} - `( - ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', - ({ passwordFromAPI, expectedScapedPassword }) => { - const passwordScaped = scapeSpecialCharsForLinux(passwordFromAPI); - /* log to compare passwords */ - console.log( - 'PASSWORD REAL: ', - passwordFromAPI, - '\nPASSWORD BACKEND: ', - JSON.stringify(passwordFromAPI), - '\nRESULT PASSWORD SCAPED REAL IN COMMAND: ', - passwordScaped, - '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', - expectedScapedPassword - ); - expect(passwordScaped).toEqual(expectedScapedPassword); - } - ); + describe('For Linux shell' , () => { + it.each` + passwordFromAPI | expectedScapedPassword + ${"password'with'special'characters"} | ${"password'\"'\"'with'\"'\"'special'\"'\"'characters"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} + ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\\\usds\\\"\\\\es"} + `( + ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', + ({ passwordFromAPI, expectedScapedPassword }) => { + const passwordScaped = scapeSpecialCharsForLinux(passwordFromAPI); + /* log to compare passwords + console.log( + 'PASSWORD REAL: ', + passwordFromAPI, + '\nPASSWORD BACKEND: ', + JSON.stringify(passwordFromAPI), + '\nRESULT PASSWORD SCAPED REAL IN COMMAND: ', + passwordScaped, + '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', + expectedScapedPassword + );*/ + expect(passwordScaped).toEqual(expectedScapedPassword); + } + ); + }) + + describe('For Windows shell' , () => { + it.each` + passwordFromAPI | expectedScapedPassword + ${"password'with'special'characters"} | ${"password'\"'\"'with'\"'\"'special'\"'\"'characters"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} + ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\usds\\\"\\es"} + `( + ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', + ({ passwordFromAPI, expectedScapedPassword }) => { + const passwordScaped = scapeSpecialCharsForWindows(passwordFromAPI); + /* log to compare passwords + console.log( + 'PASSWORD REAL: ', + passwordFromAPI, + '\nPASSWORD BACKEND: ', + JSON.stringify(passwordFromAPI), + '\nRESULT PASSWORD SCAPED REAL IN COMMAND: ', + passwordScaped, + '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', + expectedScapedPassword + );*/ + expect(passwordScaped).toEqual(expectedScapedPassword); + } + ); + }); }); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index 3c4b41f602..e988f5bcd3 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -34,7 +34,7 @@ export const osdfucatePasswordInCommand = (password: string, commandText: string switch (osName){ case 'macos': { - const regex = /WAZUH_REGISTRATION_PASSWORD=\$'((?:\\'|[^']|[\"'])*)'/g; + const regex = /WAZUH_REGISTRATION_PASSWORD=\'((?:\\'|[^']|[\"'])*)'/g; const replacedString = command.replace( regex, (match, capturedGroup) => { From 67249a5dce5b54ef56932f8d20634f35277f7f37 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 9 Aug 2023 11:14:05 -0300 Subject: [PATCH 42/46] Remove $ in wazuh password command param --- .../register-agent/core/config/os-commands-definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts index f10b9c597f..55189d3956 100644 --- a/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts +++ b/plugins/main/public/controllers/register-agent/core/config/os-commands-definitions.ts @@ -193,7 +193,7 @@ export const optionalParamsDefinitions: tOptionalParams = { case "linux": return `${property}=$'${scapeSpecialCharsForLinux(value)}'`; case "macos": - return `${property}=$'${scapeSpecialCharsForMacOS(value)}'`; + return `${property}='${scapeSpecialCharsForMacOS(value)}'`; case "windows": return `${property}='${scapeSpecialCharsForWindows(value)}'`; default: From 791be711959c7ba29edfc000f4998de9ae472f82 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 9 Aug 2023 11:14:26 -0300 Subject: [PATCH 43/46] Fix overlay height to fix horizontal scroll --- .../public/controllers/agent/components/agents-preview.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/main/public/controllers/agent/components/agents-preview.scss b/plugins/main/public/controllers/agent/components/agents-preview.scss index e420ca4e33..04f94f4f6f 100644 --- a/plugins/main/public/controllers/agent/components/agents-preview.scss +++ b/plugins/main/public/controllers/agent/components/agents-preview.scss @@ -57,7 +57,7 @@ position: absolute; top: 0; width: 100%; - height: 100%; + height: 90%; display: flex; flex-direction: column; justify-content: center; From 91f2d8bdcca2739c60a3790f068ce08518fc9c46 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 9 Aug 2023 13:52:30 -0300 Subject: [PATCH 44/46] Change macos scape characters method and unit tests --- .../services/wazuh-password-service.test.ts | 32 +++++++++++++++++-- .../services/wazuh-password-service.ts | 8 ++--- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index 54b65196f4..f6d903ab63 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -1,4 +1,4 @@ -import { scapeSpecialCharsForLinux, scapeSpecialCharsForWindows } from './wazuh-password-service'; +import { scapeSpecialCharsForLinux, scapeSpecialCharsForMacOS, scapeSpecialCharsForWindows } from './wazuh-password-service'; describe('Wazuh Password Service', () => { // NOTE: // The password constant must be written as it comes from the backend @@ -8,7 +8,6 @@ describe('Wazuh Password Service', () => { passwordFromAPI | expectedScapedPassword ${"password'with'special'characters"} | ${"password'\"'\"'with'\"'\"'special'\"'\"'characters"} ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} - ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\\\usds\\\"\\\\es"} @@ -37,7 +36,6 @@ describe('Wazuh Password Service', () => { passwordFromAPI | expectedScapedPassword ${"password'with'special'characters"} | ${"password'\"'\"'with'\"'\"'special'\"'\"'characters"} ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} - ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'\"'\"'sds\\\"es"} ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\'\"'\"'usds\\\"es"} ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\usds\\\"\\es"} @@ -60,4 +58,32 @@ describe('Wazuh Password Service', () => { } ); }); + + describe('For macOS shell' , () => { + it.each` + passwordFromAPI | expectedScapedPassword + ${"password'with'special'characters"} | ${"password'with'special'characters"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'sds\\\\\\\\\\\"es"} + ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\'\"'\"'usds\\\\\\\\\\\"es"} + ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\\\\\\\\\"es"} + ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\usds\\\\\\\\\\\"\\es"} + `( + ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', + ({ passwordFromAPI, expectedScapedPassword }) => { + const passwordScaped = scapeSpecialCharsForMacOS(passwordFromAPI); + /* log to compare passwords + console.log( + 'PASSWORD REAL: ', + passwordFromAPI, + '\nPASSWORD BACKEND: ', + JSON.stringify(passwordFromAPI), + '\nRESULT PASSWORD SCAPED REAL IN COMMAND: ', + passwordScaped, + '\nPASSWORD SCAPED REAL IN COMMAND EXPECTED: ', + expectedScapedPassword + );*/ + expect(passwordScaped).toEqual(expectedScapedPassword); + } + ); +}) }); diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index e988f5bcd3..6f11faa35e 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -9,14 +9,10 @@ export const scapeSpecialCharsForLinux = (password: string) => { return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? { let passwordScaped = password; - // the " characters is scaped by default in the password retrieved from the API - const specialCharsList = ["'"]; - const regex = new RegExp(`([${specialCharsList.join('')}])`, 'g'); - // the single quote is escaped first, and then any unescaped backslashes are escaped - return passwordScaped.replace(regex, `\'\"$&\"\'`).replace(/(? { From 6f98e928d8cd22a77acf74e09d59fd9b4b21fa0c Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 9 Aug 2023 15:02:09 -0300 Subject: [PATCH 45/46] Change macos scape method --- .../register-agent/services/wazuh-password-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts index 6f11faa35e..a42e35afb9 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.ts @@ -12,7 +12,7 @@ export const scapeSpecialCharsForLinux = (password: string) => { export const scapeSpecialCharsForMacOS = (password: string) => { let passwordScaped = password; // The double quote is escaped first and then the backslash followed by a single quote - return passwordScaped.replace(/\\"/g, '\\\\\\\\\\\"').replace(/\\'/g, `\\'\"'\"'`); + return passwordScaped.replace(/\\"/g, '\\\"').replace(/\\'/g, `\\'\"'\"'`); } export const scapeSpecialCharsForWindows = (password: string) => { From 9cc42b764ccc79f84ed04cc3180d708757ec9d60 Mon Sep 17 00:00:00 2001 From: Maximiliano Ibarra Date: Wed, 9 Aug 2023 15:26:40 -0300 Subject: [PATCH 46/46] Fix mac os scape service unit test --- .../services/wazuh-password-service.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts index f6d903ab63..f1be9e62c3 100644 --- a/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts +++ b/plugins/main/public/controllers/register-agent/services/wazuh-password-service.test.ts @@ -63,10 +63,10 @@ describe('Wazuh Password Service', () => { it.each` passwordFromAPI | expectedScapedPassword ${"password'with'special'characters"} | ${"password'with'special'characters"} - ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'sds\\\\\\\\\\\"es"} - ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\'\"'\"'usds\\\\\\\\\\\"es"} - ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\\\\\\\\\"es"} - ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\usds\\\\\\\\\\\"\\es"} + ${'password"with"doublequ\'sds\\"es'} | ${"password\"with\"doublequ'sds\\\"es"} + ${"password\"with\"doubleq\\'usds\\\"es"} | ${"password\"with\"doubleq\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'usds\\\"es"} | ${"password\"with\"doubleq\\\\'\"'\"'usds\\\"es"} + ${"password\"with\"doubleq\\\\'\\usds\\\"\\es"} | ${"password\"with\"doubleq\\\\'\"'\"'\\usds\\\"\\es"} `( ' should return password received with scaped characters: $passwordFromAPI | $scapedPassword | $expectedScapedPassword', ({ passwordFromAPI, expectedScapedPassword }) => {