Skip to content

Saved filters 2#1301

Merged
lyubov-voloshko merged 32 commits intomainfrom
saved-filters-2
Aug 13, 2025
Merged

Saved filters 2#1301
lyubov-voloshko merged 32 commits intomainfrom
saved-filters-2

Conversation

@lyubov-voloshko
Copy link
Copy Markdown
Collaborator

No description provided.

- add saved filters panel;
- add save filters set dialog and request.
- show filters menu;
- display filters in edit popup;
- remove filters.
- remove filter selection through index;
- clear filters on selecting already active filter
- add update saved filters;
- update saved filters layout.
@lyubov-voloshko lyubov-voloshko merged commit a0c7051 into main Aug 13, 2025
12 checks passed
@lyubov-voloshko lyubov-voloshko deleted the saved-filters-2 branch August 13, 2025 19:11
if (dynamicColumn && this.savedFilterMap[this.selectedFilterSetId]) {
const filters = params['filters'] ? JsonURL.parse(params['filters']) : {};

this.savedFilterMap[this.selectedFilterSetId].dynamicColumn = {

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.
This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.

Copilot Autofix

AI 9 months ago

To fix the prototype pollution vulnerability, we should prevent user-controlled keys from being used to modify object properties in a way that could affect the prototype chain. The best way to do this is to use a Map object instead of a plain object for savedFilterMap, as Map is resilient to prototype pollution. This requires:

  • Changing the type and initialization of savedFilterMap from { [key: string]: any } = {} to Map<string, any> = new Map().
  • Updating all code that reads or writes to savedFilterMap to use Map methods (set, get, has, etc.) instead of object property access.
  • Specifically, in loadSavedFilters, update the assignment to use set instead of object spread, and update all subsequent accesses to use get instead of bracket notation.

All changes are to be made in frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts.


Suggested changeset 1
frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts b/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts
--- a/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts
+++ b/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.ts
@@ -60,7 +60,7 @@
   private dynamicColumnValueDebounceTimer: any = null;
 
   public savedFilterData: any[] = [];
-  public savedFilterMap: { [key: string]: any } = {};
+  public savedFilterMap: Map<string, any> = new Map();
 
   public selectedFilterSetId: string | null = null;
   public selectedFilter: any = null;
@@ -196,23 +196,27 @@
           this.savedFilterData = data.sort((a, b) =>
             new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
           );
-          this.savedFilterMap = Object.assign({}, ...data.map((filter) => {
+          this.savedFilterMap = new Map();
+          data.forEach((filter) => {
             const transformedFilter = this.processFiltersData(filter);
-            return { [filter.id]: transformedFilter };
-          }));
+            this.savedFilterMap.set(filter.id, transformedFilter);
+          });
 
           const params = this.route.snapshot.queryParams;
           const dynamicColumn = params['dynamic_column'] ? JsonURL.parse(params['dynamic_column']) : null;
 
           if (this.selectedFilterSetId && this.savedFilterData.length > 0) {
-            if (dynamicColumn && this.savedFilterMap[this.selectedFilterSetId]) {
+            if (dynamicColumn && this.savedFilterMap.has(this.selectedFilterSetId)) {
               const filters = params['filters'] ? JsonURL.parse(params['filters']) : {};
 
-              this.savedFilterMap[this.selectedFilterSetId].dynamicColumn = {
-                column: dynamicColumn.column_name,
-                operator: dynamicColumn.comparator,
-                value: filters[dynamicColumn.column_name]?.[dynamicColumn.comparator] || null
-              };
+              const selectedFilter = this.savedFilterMap.get(this.selectedFilterSetId);
+              if (selectedFilter) {
+                selectedFilter.dynamicColumn = {
+                  column: dynamicColumn.column_name,
+                  operator: dynamicColumn.comparator,
+                  value: filters[dynamicColumn.column_name]?.[dynamicColumn.comparator] || null
+                };
+              }
             }
           }
         }
EOF
@@ -60,7 +60,7 @@
private dynamicColumnValueDebounceTimer: any = null;

public savedFilterData: any[] = [];
public savedFilterMap: { [key: string]: any } = {};
public savedFilterMap: Map<string, any> = new Map();

public selectedFilterSetId: string | null = null;
public selectedFilter: any = null;
@@ -196,23 +196,27 @@
this.savedFilterData = data.sort((a, b) =>
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
);
this.savedFilterMap = Object.assign({}, ...data.map((filter) => {
this.savedFilterMap = new Map();
data.forEach((filter) => {
const transformedFilter = this.processFiltersData(filter);
return { [filter.id]: transformedFilter };
}));
this.savedFilterMap.set(filter.id, transformedFilter);
});

const params = this.route.snapshot.queryParams;
const dynamicColumn = params['dynamic_column'] ? JsonURL.parse(params['dynamic_column']) : null;

if (this.selectedFilterSetId && this.savedFilterData.length > 0) {
if (dynamicColumn && this.savedFilterMap[this.selectedFilterSetId]) {
if (dynamicColumn && this.savedFilterMap.has(this.selectedFilterSetId)) {
const filters = params['filters'] ? JsonURL.parse(params['filters']) : {};

this.savedFilterMap[this.selectedFilterSetId].dynamicColumn = {
column: dynamicColumn.column_name,
operator: dynamicColumn.comparator,
value: filters[dynamicColumn.column_name]?.[dynamicColumn.comparator] || null
};
const selectedFilter = this.savedFilterMap.get(this.selectedFilterSetId);
if (selectedFilter) {
selectedFilter.dynamicColumn = {
column: dynamicColumn.column_name,
operator: dynamicColumn.comparator,
value: filters[dynamicColumn.column_name]?.[dynamicColumn.comparator] || null
};
}
}
}
}
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants