Skip to content

Commit

Permalink
Merge pull request #6 from BSd3v/dev
Browse files Browse the repository at this point in the history
More additional functions and security measures:
  • Loading branch information
alexcjohnson authored Jan 24, 2023
2 parents 413f5c8 + f20f974 commit c344e84
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 19 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@
All notable changes to `dash-ag-grid` will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased] - 2023-01-23
_More additional functions and security measures_:
- allowing for strings of functions to be passed as parameters to `valueGetterFunction`, `valueFormatterFunction`
- this allows for functions to be parsed even when the app is completely locked down, (meta tags, etc)
- added row conditional formatting via `getRowStyle` acts similar to `cellStyles`
- added ability for custom parsing functions to be passed via the namespace `window.dashAgGridFunctions`
- allowed for `null` to be passed to `columnSize`, to prevent the fit to width or autosize being the only options
- fixed props issue for `enableAddRows`


## [Unreleased] - 2023-01-20
_Major overhaul of dash-ag-grid_:
- bringing ag-grid from version v27.x to v29.x+
- added secondary `agGridEnterprise.react.js` as additional importing `ag-grid-enterprise` due to all-modules no longer supported
- updating props for breaking changes due to version update
- adding props for easier user / dash manipulation (enable... props ) for creating buttons
- removing `agGridColumns` due to deprecation and removal due to v29
- added `className` support for css customization native to ag-grid (removed hardcoded styling as well)
- added overarching `dangerously_allow_html` to grid props only provided at render, to keep `columnDefs` from receiving possible updates to show unsafe html
- added `data_previous` and `data_previous_timestamp` to allow for use with user change logs
- added `dashGridOptions` to allow for arbitrary use of props not explicitly listed
- added `setRowId` for allowing `rowData` change detection to work
- added prop `columnState` to allow for pulling the current state of the columns after user interaction, necessary for saving layouts outside of snapshots
- fixed issue where conditional formatting was not applied to nested columns
- fixed issue where columns would not take edits or adjustments due to becoming static
- updated `markdownRenderer.js` to use github markdown, and also have the ability to be passed a target for links, to avoid `dangerously_allow_html`
- updated `requirements.txt` to pull the latest packages

## [1.3.2] - 2023-01-13

### Updated
Expand Down
23 changes: 19 additions & 4 deletions src/lib/components/AgGrid.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ DashAgGrid.propTypes = {
* If true, the internal method addRows() will be called
*/
enableAddRows: PropTypes.oneOfType([
PropTypes.bool, PropTypes.Object
PropTypes.bool, PropTypes.arrayOf(PropTypes.object)
]),

/**
Expand Down Expand Up @@ -248,9 +248,11 @@ DashAgGrid.propTypes = {
}),

/**
* Size the columns automatically or to fit their contents
* Size the columns autoSizeAll changes the column sizes to fit the column's content,
* sizeToFit changes the column sizes to fit the width of the table
* and null bypasses the altering of the column widths
*/
columnSize: PropTypes.oneOf(['sizeToFit', 'autoSizeAll']),
columnSize: PropTypes.oneOf(['sizeToFit', 'autoSizeAll', null]),

/**
* Use this with Dash Enterprise only. Sets the ag-grid theme. Use ddk for dark themes.
Expand All @@ -270,6 +272,19 @@ DashAgGrid.propTypes = {
defaultStyle: PropTypes.object,
}),

/**
* Object used to perform the row styling. See AG-Grid Row Style.
*/
getRowStyle: PropTypes.shape({
styleConditions: PropTypes.arrayOf(
PropTypes.shape({
condition: PropTypes.string.isRequired,
style: PropTypes.object.isRequired,
})
),
defaultStyle: PropTypes.object,
}),

/**
* Infinite Scroll, Datasource interface
* See https://www.ag-grid.com/react-grid/infinite-scrolling/#datasource-interface
Expand Down Expand Up @@ -348,7 +363,7 @@ DashAgGrid.propTypes = {
/**
* Data retreived from the server
*/
rowData: PropTypes.arrayOf(PropTypes.any),
rowData: PropTypes.arrayOf(PropTypes.object),

/**
* Current row count, if known
Expand Down
97 changes: 82 additions & 15 deletions src/lib/fragments/AgGrid.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {propTypes, defaultProps} from '../components/AgGrid.react';

import MarkdownRenderer from '../renderers/markdownRenderer';
import RowMenuRenderer from '../renderers/rowMenuRenderer';
import * as customFunctions from '../renderers/customFunctions';

import 'ag-grid-community';
import { AgGridReact } from 'ag-grid-react';
Expand Down Expand Up @@ -57,10 +58,12 @@ export default class DashAgGrid extends Component {
this.onGridSizeChanged = this.onGridSizeChanged.bind(this);
this.updateColumnWidths = this.updateColumnWidths.bind(this);
this.handleDynamicCellStyle = this.handleDynamicCellStyle.bind(this);
this.handleDynamicRowStyle = this.handleDynamicRowStyle.bind(this);
this.generateRenderer = this.generateRenderer.bind(this);
this.resetColumnState = this.resetColumnState.bind(this);
this.exportDataAsCsv = this.exportDataAsCsv.bind(this);
this.setSelection = this.setSelection.bind(this);
this.parseParamFunction = this.parseParamFunction.bind(this);

//Additional Exposure
this.setUpCols = this.setUpCols.bind(this);
Expand All @@ -72,6 +75,7 @@ export default class DashAgGrid extends Component {
this.deleteSelectedRows = this.deleteSelectedRows.bind(this);
this.addRows = this.addRows.bind(this);
this.getRowData = this.getRowData.bind(this);
this.fixCols = this.fixCols.bind(this);

this.selectionEventFired = false;

Expand All @@ -93,20 +97,45 @@ export default class DashAgGrid extends Component {
}
}

fixCols(columnDef, templateMessage) {
const test = (base, target) => {
if (target in columnDef) {
if (!(columnDef['dangerously_allow_html']
&& this.state.dangerously_allow_html)) {
if (typeof columnDef[target] !== 'function') {
console.error({field: columnDef['field'], message: templateMessage})
columnDef[target] = ''
}
}
}
if (base in columnDef) {
const newFunc = (params) => this.parseParamFunction({params}, columnDef[base])
columnDef[target] = newFunc
}
}
if ("headerComponentParams" in columnDef) {
if ('template' in columnDef['headerComponentParams'] && !(columnDef['dangerously_allow_html']
&& this.state.dangerously_allow_html)) {
columnDef['headerComponentParams']['template'] = '<div></div>'
console.error({field: columnDef['field'], message: templateMessage})
}
}

test('valueGetterFunction','valueGetter')
test('valueFormatterFunction','valueFormatter')

return columnDef
}

setUpCols(cellStyle) {
const templateMessage = 'you are trying to use a dangerous element that could lead to XSS'
if (this.props.columnDefs) {
this.props.setProps(
{columnDefs: this.props.columnDefs.map((columnDef) => {
if ('children' in columnDef) {
columnDef['children'] = columnDef['children'].map((child) => {
if ("headerComponentParams" in child) {
if ('template' in child['headerComponentParams'] && !(child['dangerously_allow_html']
&& this.state.dangerously_allow_html)) {
child['headerComponentParams']['template'] = '<div></div>'
console.error({field: child['field'], message: templateMessage})
}
}
child = this.fixCols(child, templateMessage)

if ('cellStyle' in child) {
return child
}
Expand All @@ -117,13 +146,9 @@ export default class DashAgGrid extends Component {
}
})
}
if ("headerComponentParams" in columnDef) {
if ('template' in columnDef['headerComponentParams'] && !(columnDef['dangerously_allow_html']
&& this.state.dangerously_allow_html)) {
columnDef['headerComponentParams']['template'] = '<div></div>'
console.error({field: columnDef['field'], message: templateMessage})
}
}

columnDef = this.fixCols(columnDef, templateMessage)

if ('cellStyle' in columnDef) {
return columnDef
}
Expand Down Expand Up @@ -361,7 +386,7 @@ export default class DashAgGrid extends Component {
}

/**
* @params AG-Grid Cell Style rules attribute.
* @params AG-Grid Styles rules attribute.
* See: https://www.ag-grid.com/react-grid/cell-styles/#cell-style-cell-class--cell-class-rules-params
*/
handleDynamicCellStyle({params, cellStyle = {}}) {
Expand All @@ -382,6 +407,41 @@ export default class DashAgGrid extends Component {
return defaultStyle ? defaultStyle : null;
}

/**
* @params AG-Grid Styles rules attribute.
* See: https://www.ag-grid.com/react-grid/row-styles/#row-style-row-class--row-class-rules-params
*/
handleDynamicRowStyle({params, getRowStyle = {}}) {
const {styleConditions, defaultStyle} = getRowStyle;

if (styleConditions && styleConditions.length > 0) {
for (const styleCondition of styleConditions) {
const {condition, style} = styleCondition;
const parsedCondition = esprima.parse(condition).body[0]
.expression;

if (evaluate(parsedCondition, {...params})) {
return style;
}
}
}

return defaultStyle ? defaultStyle : null;
}

parseParamFunction({params}, tempFunction) {
try {
const parsedCondition = esprima.parse(tempFunction).body[0]
.expression;
const value = evaluate(parsedCondition, {...params, ...customFunctions, ...window.dashAgGridFunctions})
return value
} catch (err) {
console.log(err)
}
//const value = evaluate(parsedCondition, {...params})
return ''
}

generateRenderer(Renderer) {
const {setProps} = this.props;

Expand Down Expand Up @@ -478,6 +538,7 @@ export default class DashAgGrid extends Component {
const {
id,
cellStyle,
getRowStyle,
style,
theme,
className,
Expand Down Expand Up @@ -505,6 +566,11 @@ export default class DashAgGrid extends Component {
}

this.setUpCols(cellStyle)

let newRowStyle;
if (getRowStyle) {
newRowStyle = (params) => this.handleDynamicRowStyle({params, getRowStyle})
}

const cols = [];

Expand Down Expand Up @@ -585,6 +651,7 @@ export default class DashAgGrid extends Component {
>
<AgGridReact
getRowId={getRowId}
getRowStyle={newRowStyle}
onGridReady={this.onGridReady}
onSelectionChanged={this.onSelectionChanged}
onCellClicked={this.onCellClicked}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/renderers/customFunctions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function Round(v, a=2) {
return Math.round(v * (10**a)) / (10**a)
}

0 comments on commit c344e84

Please sign in to comment.