Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devtron-labs/devtron-fe-common-lib",
"version": "0.6.0-patch-1",
"version": "0.6.0-patch-1-beta-3",
"description": "Supporting common component library",
"type": "module",
"main": "dist/index.js",
Expand Down Expand Up @@ -40,6 +40,7 @@
"@testing-library/react": "^12.1.4",
"@tippyjs/react": "^4.2.0",
"@typeform/embed-react": "2.20.0",
"@types/dompurify": "^3.0.5",
"@types/react": "17.0.39",
"@types/react-dom": "17.0.13",
"@types/react-router-dom": "^5.3.3",
Expand Down
1 change: 1 addition & 0 deletions src/Common/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const URLS = {
GLOBAL_CONFIG_SCOPED_VARIABLES: '/global-config/scoped-variables',
GLOBAL_CONFIG_DEPLOYMENT_CHARTS_LIST: '/global-config/deployment-charts',
NETWORK_STATUS_INTERFACE: '/network-status-interface',
RESOURCE_BROWSER: '/resource-browser',
}

export const ROUTES = {
Expand Down
46 changes: 45 additions & 1 deletion src/Common/Helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import React, { SyntheticEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import DOMPurify from 'dompurify'
import { JSONPath, JSONPathOptions } from 'jsonpath-plus'
import { compare as compareJSON, applyPatch } from 'fast-json-patch'
import { components } from 'react-select'
Expand Down Expand Up @@ -642,7 +643,11 @@ export const powerSetOfSubstringsFromStart = (strings: string[], regex: RegExp)
return _keys
})

export const convertJSONPointerToJSONPath = (pointer: string) => pointer.replace(/\/([\*0-9]+)\//g, '[$1].').replace(/\//g, '.').replace(/\./, '$.')
export const convertJSONPointerToJSONPath = (pointer: string) =>
pointer
.replace(/\/([\*0-9]+)\//g, '[$1].')
.replace(/\//g, '.')
.replace(/\./, '$.')

export const flatMapOfJSONPaths = (
paths: string[],
Expand Down Expand Up @@ -953,3 +958,42 @@ export const throttle = <T extends (...args: unknown[]) => unknown>(
}
}
}

// TODO: Might need to expose sandbox and referrer policy
export const getSanitizedIframe = (iframeString: string) =>
DOMPurify.sanitize(iframeString, {
ADD_TAGS: ['iframe'],
ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling'],
})

/**
* This method adds default attributes to iframe - title, loading ="lazy", width="100%", height="100%"
*/
export const getIframeWithDefaultAttributes = (iframeString: string, defaultName?: string): string => {
const parentDiv = document.createElement('div')
parentDiv.innerHTML = getSanitizedIframe(iframeString)


const iframe = parentDiv.querySelector('iframe')
if (iframe) {
if (!iframe.hasAttribute('title') && !!defaultName) {
iframe.setAttribute('title', defaultName)
}

if (!iframe.hasAttribute('loading')) {
iframe.setAttribute('loading', 'lazy')
}

if (!iframe.hasAttribute('width')) {
iframe.setAttribute('width', '100%')
}

if (!iframe.hasAttribute('height')) {
iframe.setAttribute('height', '100%')
}

return parentDiv.innerHTML
}

return iframeString
}
31 changes: 31 additions & 0 deletions src/Shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { Dayjs } from 'dayjs'
import {
OptionType,
CommonNodeAttr,
Expand Down Expand Up @@ -118,6 +119,7 @@ export enum Nodes {
Event = 'Event',
Namespace = 'Namespace',
Overview = 'Overview',
MonitoringDashboard = 'MonitoringDashboard',
}

// FIXME: This should be `typeof Nodes[keyof typeof Nodes]` instead since the key and values are not the same. Same to be removed from duplications in dashboard
Expand Down Expand Up @@ -680,6 +682,7 @@ export enum ConfigurationType {
export interface BaseURLParams {
appId: string
envId: string
clusterId: string
}

export interface ConfigKeysWithLockType {
Expand Down Expand Up @@ -738,3 +741,31 @@ export interface CustomRoleAndMeta {
possibleRolesMetaForCluster: MetaPossibleRoles
possibleRolesMetaForJob: MetaPossibleRoles
}

interface CommonTabArgsType {
name: string
kind?: string
url: string
isSelected: boolean
title?: string
isDeleted?: boolean
position: number
iconPath?: string
dynamicTitle?: string
showNameOnSelect?: boolean
/**
* @default false
*/
hideName?: boolean
isAlive?: boolean
lastSyncMoment?: Dayjs
componentKey?: string
}

export interface InitTabType extends CommonTabArgsType {
idPrefix: string
}

export interface DynamicTabType extends CommonTabArgsType {
id: string
}
27 changes: 27 additions & 0 deletions src/Shared/validations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { getSanitizedIframe } from '@Common/Helper'
import { URLProtocolType } from './types'

export interface ValidationResponseType {
Expand Down Expand Up @@ -342,3 +343,29 @@ export const validateJSON = (json: string): ValidationResponseType => {
}
}
}

export const validateIframe = (input: string): ValidationResponseType => {
const sanitizedInput = getSanitizedIframe(input)
const parentDiv = document.createElement('div')
parentDiv.innerHTML = sanitizedInput

const iframe = parentDiv.querySelector('iframe')

// TODO: Can also check for accessability and security tags like sandbox, title, lazy, etc
if (!iframe || parentDiv.children.length !== 1) {
return { isValid: false, message: 'Input must contain a single iframe tag.' }
}

const src = iframe.getAttribute('src')
if (!src) {
return { isValid: false, message: 'Iframe must have a valid src attribute.' }
}

const urlValidationResponse = validateURL(src)

if (!urlValidationResponse.isValid) {
return urlValidationResponse
}

return { isValid: true }
}