Skip to content

feat: replace hardcoded jobs with configurable actions#2351

Open
minottic wants to merge 6 commits into
masterfrom
config_batch
Open

feat: replace hardcoded jobs with configurable actions#2351
minottic wants to merge 6 commits into
masterfrom
config_batch

Conversation

@minottic

@minottic minottic commented Apr 23, 2026

Copy link
Copy Markdown
Member

Description

By setting batchActionsEnabled and batchActions in config.json it renders them as actions in the overview page and in the cart (common ones are preserved)

Motivation

This facilitates facilities to set custom actions in the cart and in particular helps us in adding new workflows, delete and restore ones.

This is an example of the config

image image

Changes:

  • new functions registered in configurable-actions that compute sum of size and sum of packedSize and datasetlist required by jobs
  • custom snackbar is refactored to use action with showMessage
  • archiveService is removed
  • selection is cleared when action is submitted from dataset-overview
  • actions are disabled when cart is non empty
  • new functions allow for better enable/disable as one can now disable e.g. archive when size of files = 0

Tests included

  • Included for each change/fix?
  • Passing? (Merge will not be approved unless this is checked)

Documentation

  • swagger documentation updated [required]
  • official documentation updated [nice-to-have]

official documentation info

If you have updated the official documentation, please provide PR # and URL of the pages where the updates are included

Backend version

  • Does it require a specific version of the backend
  • which version of the backend is required:

Summary by Sourcery

Replace hardcoded archive/retrieve dataset jobs with configurable batch actions and integrate them into the datasets overview and batch views.

New Features:

  • Introduce configurable batch actions in dataset overview and batch views driven by config-defined batchActions.
  • Expose new configurable-action keywords for dataset field lists and total size metrics to support workflow configuration.

Bug Fixes:

  • Ensure dataset selection is cleared automatically when view mode changes or after configurable actions complete successfully.
  • Keep dataset-table selection state in sync with the global selected-datasets store and clear table selection when the store is emptied.

Enhancements:

  • Refactor archiving and retrieval flows to use generic configurable actions instead of the dedicated ArchivingService, removing that service and its dependencies.
  • Route configurable action success and failure through NgRx actions and effects to display standardized success/error snackbars.
  • Disable dataset overview actions when the cart is non-empty to prevent conflicting operations.

Documentation:

  • Document the new configurable-actions dataset keywords for total sizes and PID/file mappings in the technical documentation.

Tests:

  • Extend configurable-actions unit tests to cover new keywords and error handling for failed XHR actions.
  • Update dataset-table-actions and batch-view component tests for the new configurable-actions integration and selection-clearing behavior.

@minottic minottic requested a review from a team as a code owner April 23, 2026 12:20

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • In several places (e.g. building actionItems in DatasetTableActionsComponent and BatchViewComponent) you rely on as ActionItemDataset[] / as UserProfile; consider tightening the selector types (or introducing a typed facade) so you can avoid these casts and catch mismatches at compile time.
  • The { success: boolean } payload for actionFinished is duplicated across multiple components; introducing a shared interface/type (and possibly centralizing the success-handling logic, e.g. driven off actionSuccessAction in an effect) would reduce duplication and make it easier to evolve the contract later.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In several places (e.g. building `actionItems` in `DatasetTableActionsComponent` and `BatchViewComponent`) you rely on `as ActionItemDataset[]` / `as UserProfile`; consider tightening the selector types (or introducing a typed facade) so you can avoid these casts and catch mismatches at compile time.
- The `{ success: boolean }` payload for `actionFinished` is duplicated across multiple components; introducing a shared interface/type (and possibly centralizing the success-handling logic, e.g. driven off `actionSuccessAction` in an effect) would reduce duplication and make it easier to evolve the contract later.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@minottic

Copy link
Copy Markdown
Member Author

after this PR I believe another enhancement of configurable-actions would be to allow for shared blocks, in particular the variables one. This way the config can stay slimmer since often variables in multiple actions inside the same config key are shared

@minottic minottic force-pushed the generalize_dialog branch 4 times, most recently from f9081a9 to 3ab9cdf Compare June 17, 2026 12:53
Base automatically changed from generalize_dialog to master June 17, 2026 13:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the hardcoded archive/retrieve “jobs” UI with config-driven “batch actions” (via batchActionsEnabled/batchActions) and routes action success/failure through a centralized NgRx effect that shows standardized snackbars.

Changes:

  • Added new NgRx actions + effect to translate generic action success/failure into showMessageAction snackbars.
  • Integrated <configurable-actions> into the datasets overview and batch view, removing the dedicated ArchivingService flow and clearing selection on mode change / successful action.
  • Extended configurable-actions keyword support and updated technical + user documentation.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/app/state-management/effects/actions.effect.ts New effect mapping action success/failure to snackbar messages
src/app/state-management/effects/actions.effect.spec.ts Unit tests for the new effect behavior
src/app/state-management/actions/actions.actions.ts New generic action success/failure action creators
src/app/state-management/actions/actions.actions.spec.ts Unit tests for new action creators
src/app/shared/modules/configurable-actions/doc/configurable-actions-technical.md Documents new dataset-related selector keywords
src/app/shared/modules/configurable-actions/configurable-actions.documentation.md User-facing docs updated with new selector keywords
src/app/shared/modules/configurable-actions/configurable-action.interfaces.ts Switch dataset action item type to OutputDatasetObsoleteDto + files?
src/app/shared/modules/configurable-actions/configurable-action.component.ts Dispatch generic success/failure actions instead of using MatSnackBar; add new dataset keyword resolvers
src/app/shared/modules/configurable-actions/configurable-action.component.spec.ts Adds tests for new keywords and xhr failure dispatch
src/app/datasets/datasets.module.ts Removes ArchivingService and registers new ActionEffects
src/app/datasets/dataset-table/dataset-table.component.ts Sync table selection clearing with store-selected datasets
src/app/datasets/dataset-table/dataset-table.component.spec.ts Adds selector mocking for selected-datasets store
src/app/datasets/dataset-table/dataset-table.component.html Adds template ref to drive clearSelection() via ViewChild
src/app/datasets/dataset-table-actions/dataset-table-actions.component.ts Replaces archive/retrieve buttons with configurable actions; clears selection on mode changes and after successful actions
src/app/datasets/dataset-table-actions/dataset-table-actions.component.spec.ts Updates tests for new mode-change behavior and action-finished selection clearing
src/app/datasets/dataset-table-actions/dataset-table-actions.component.html Renders configurable actions and hides them when batch/cart is non-empty
src/app/datasets/dashboard/dashboard.component.ts Removes selectedSets plumbing now that selection comes from the store
src/app/datasets/dashboard/dashboard.component.html Drops selectedSets inputs to actions/table components
src/app/datasets/batch-view/batch-view.component.ts Replaces archive/retrieve flow with configurable actions + actionItems wiring
src/app/datasets/batch-view/batch-view.component.spec.ts Removes ArchivingService mocking
src/app/datasets/batch-view/batch-view.component.html Uses configurable actions instead of archive/retrieve buttons
src/app/datasets/archiving.service.ts Removes the legacy archiving service
src/app/datasets/archiving.service.spec.ts Removes tests for the deleted service

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app/datasets/batch-view/batch-view.component.ts
Comment thread src/app/datasets/batch-view/batch-view.component.ts
@nitrosx

nitrosx commented Jun 18, 2026

Copy link
Copy Markdown
Member

Code Review Report: config_batch Branch

This report analyzes the changes introduced by the config_batch branch compared to the master branch.


Assessment

Overview

The config_batch branch introduces a major architectural improvement by replacing hardcoded archive/retrieve functionality with a configurable actions system. This is a significant step toward making the SciCat frontend more flexible and maintainable.

Key Changes

1. Archiving Service Removal (Major Improvement)

  • Files Deleted:
    • src/app/datasets/archiving.service.ts (134 lines)
    • src/app/datasets/archiving.service.spec.ts (202 lines)
  • Impact: Removes ~336 lines of hardcoded business logic
  • Rationale: Archive and retrieve operations are now handled through the configurable actions framework, eliminating the need for a dedicated service

2. Configurable Actions Integration (Core Feature)

  • Components Modified:
    • batch-view.component.ts/html: Replaced hardcoded Archive/Retrieve buttons with <configurable-actions> component
    • dataset-table-actions.component.ts/html: Same replacement for dataset table actions
    • dashboard.component.ts/html: Updated to work with new selection management
  • New Selectors Added:
    • #DatasetsPidEmptyFilesMap: JSON string with dataset PIDs and empty files arrays
    • #DatasetsTotalSize: Sum of size across all datasets
    • #DatasetsTotalPackedSize: Sum of packedSize across all datasets
  • Type Improvements:
    • Changed ActionItemDataset from type alias to interface extending OutputDatasetObsoleteDto
    • Better type safety and maintainability

3. State Management for Actions (Best Practice)

  • New Files Created:
    • src/app/state-management/actions/actions.actions.ts: Defines actionSuccessAction and actionFailureAction
    • src/app/state-management/effects/actions.effect.ts: Handles action success/failure messages via snackbar
    • Corresponding test files for both
  • Benefits:
    • Centralized error/success handling
    • Consistent user feedback across all configurable actions
    • Follows NgRx best practices

4. Error Handling Improvements (Enhanced UX)

  • Before: Errors shown via MatSnackBar directly in components
  • After: Errors dispatched as actions, handled by effects, displayed via centralized message system
  • Benefit: Consistent error handling, easier to test and maintain

5. Selection Management (Cleaner Architecture)

  • Removed @Input() selectedSets from multiple components
  • Now uses store selectors (selectSelectedDatasets, selectIsBatchNonEmpty) directly
  • Better separation of concerns

6. Documentation Updates (Comprehensive)

  • Updated configurable-actions.documentation.md with new selectors
  • Updated configurable-actions-technical.md with new selectors
  • Added examples and technical details

Lines Changed Summary

  • 23 files changed
  • 368 insertions(+)
  • 549 deletions(-)
  • Net reduction: ~181 lines (cleaner, more maintainable code)

Improvement Needed

1. Error Handling Consistency (Medium Priority)

Issue: The new action effects use hardcoded default messages ("Success!" and "An error occurred.")

Current Implementation (actions.effect.ts):

message: new Message(
  action.message || "Success!",
  MessageType.Success,
  5000,
)

Recommendation:

  • Make default messages configurable via AppConfigService
  • Allow different default messages for different action types
  • Consider adding severity levels for different error types

2. Action Type Safety (Medium Priority)

Issue: The type field in ActionConfig is a string union, but new action types may not be properly validated.

Current Implementation (configurable-action.interfaces.ts):

export type ActionType = "form" | "link" | "json-download" | "xhr" | "dialog";

Recommendation:

  • Add runtime validation for action types
  • Create a validation utility that warns about unknown action types
  • Document all supported action types in a single source of truth

3. Testing Coverage (High Priority)

Issue: While tests were added, some edge cases may not be covered.

Missing Test Scenarios:

  • XHR action failures with different HTTP status codes
  • Circular dependency detection in variables
  • All new selectors (#DatasetsPidEmptyFilesMap, #DatasetsTotalSize, #DatasetsTotalPackedSize)
  • Concurrent action execution
  • Action cancellation scenarios

Recommendation:

  • Add integration tests for the full action lifecycle
  • Test error propagation through the effects chain
  • Add snapshot tests for action configurations

4. Performance Optimization (Low Priority)

Issue: The combineLatest in dataset-table-actions.component.ts subscribes to multiple selectors.

Current Implementation:

combineLatest([
  this.userProfile$,
  this.store.select(selectArchiveViewMode),
  this.selectSelectedDatasets$,
]).subscribe(...)

Recommendation:

  • Consider using distinctUntilChanged to prevent unnecessary updates
  • Evaluate if all subscriptions are necessary or can be optimized
  • Add performance benchmarks for action-heavy pages

5. Backward Compatibility (Critical for Merge)

Issue: The changes assume batchActionsEnabled and batchActions config exists.

Current Implementation (batch-view.component.html):

<configurable-actions *ngIf="appConfig.batchActionsEnabled"
  [actionsConfig]="appConfig.batchActions"
  ...
></configurable-actions>

Recommendation:

  • Add fallback behavior when config is missing
  • Provide migration guide for existing configurations
  • Add schema validation for the new config options
  • Consider feature flags for gradual rollout

6. Documentation Gaps (Medium Priority)

Missing Documentation:

  • Migration guide from old hardcoded archive/retrieve to new configurable actions
  • Examples of common action configurations (archive, retrieve, etc.)
  • Best practices for action configuration
  • Security considerations for different action types

Recommendation:

  • Add migration section to documentation
  • Include security warnings (e.g., don't use user input directly in URLs)
  • Add validation rules documentation

7. Type Narrowing (Low Priority)

Issue: Some type assertions may use as any or type casting.

Recommendation:

  • Audit all type casts in configurable action components
  • Ensure proper types are used throughout the action pipeline
  • Add type guards where necessary

8. Accessibility (Medium Priority)

Issue: Configurable actions generate buttons dynamically, but accessibility attributes may not be consistent.

Recommendation:

  • Ensure all generated buttons have proper ARIA attributes
  • Add accessibility testing for configurable actions
  • Document accessibility requirements for custom action configurations

Verdict

Overall Rating: 8.5/10 - Excellent with Minor Improvements Needed

Strengths:

  • Architectural Improvement: Replacing hardcoded logic with configurable actions is a significant step forward
  • Code Quality: Clean implementation following Angular and NgRx best practices
  • Type Safety: Good use of TypeScript interfaces and type narrowing
  • Testing: Comprehensive test coverage for new functionality
  • Documentation: Well-documented new features and selectors
  • User Experience: Centralized error/success handling provides consistent feedback
  • Maintainability: Net reduction in code while adding flexibility

Weaknesses:

  • Backward Compatibility: Needs migration path and fallback behavior
  • Error Handling: Could be more configurable
  • Testing: Some edge cases may need additional coverage
  • Documentation: Missing migration guide and examples

Recommendation: MERGE WITH MINOR CHANGES

This branch represents a significant improvement to the codebase and should be merged after addressing the following:

  1. Critical: Add backward compatibility checks and migration documentation
  2. High: Ensure all tests pass and add missing test cases
  3. Medium: Make default messages configurable, add validation for action types
  4. Low: Optimize performance, improve type safety, enhance accessibility

Estimated Effort for Improvements:

  • Critical issues: 2-4 hours
  • High priority: 4-8 hours
  • Medium priority: 8-16 hours
  • Low priority: 16-24 hours

Total estimated effort: 1-2 days of work

Final Thoughts

This is an excellent refactoring that makes the application more flexible and maintainable. The configurable actions framework will allow future features to be added without modifying core application code, which is a significant architectural win. The changes are well-executed and follow best practices. With the minor improvements suggested above, this will be a production-ready feature that greatly enhances the SciCat frontend's extensibility.

Generated with Mistral AI

@nitrosx

nitrosx commented Jun 18, 2026

Copy link
Copy Markdown
Member

Code Review: config_batch Branch - Concise Technical Assessment

Summary of Changes

The config_batch branch replaces hardcoded archive/retrieve functionality with the existing configurable actions framework. This is a refactoring that removes ~336 lines of dedicated service code and consolidates batch actions into the generic configurable actions system.

Files Changed: 23 files | Net: -181 lines (368 added, 549 removed)


What the Code Does

Removed Code (archiving.service.ts)

  • Purpose: Handled archive and retrieve operations for datasets via HTTP job submissions
  • Key Methods:
    • archive() - Submit archive job for selected datasets
    • retrieve() - Submit retrieve job with destination path
    • createJob() - Build job payload from user and dataset data
    • archiveOrRetrieve() - Core logic combining user data, datasets, and API calls
    • generateOptionLocation() - Build destination path from config
    • retriveDialogOptions() - Configure dialog for retrieve destination selection

Modified Components

1. batch-view.component.ts/html

  • Removed: Direct calls to ArchivingService.archive() and ArchivingService.retrieve()
  • Removed: Hardcoded Archive/Retrieve buttons with *ngIf="appConfig.archiveWorkflowEnabled"
  • Added: <configurable-actions> component with [actionsConfig]="appConfig.batchActions"
  • Added: actionItems and actionButtonsStyle properties
  • Added: onActionFinished() handler to clear batch on success

2. dataset-table-actions.component.ts/html

  • Removed: Direct calls to ArchivingService.archiveClickHandle() and retrieveClickHandle()
  • Removed: Hardcoded Archive/Retrieve buttons with *ngIf conditions on view modes
  • Added: <configurable-actions> component with same config binding
  • Added: Subscription to selectSelectedDatasets$ to populate actionItems
  • Added: onActionFinished() handler to clear selection on success
  • Removed: @Input() selectedSets - now uses store selector directly

3. dashboard.component.ts/html

  • Removed: [selectedSets] input binding to dataset-table-actions
  • Removed: selectedSets$ observable from component
  • Result: Selection management now fully handled via store

New State Management

actions.actions.ts (NEW)

export const actionSuccessAction = createAction('[UI] Action Success', (message?) => ({ message }));
export const actionFailureAction = createAction('[Actions] Action Failure', (message?) => ({ message }));

actions.effect.ts (NEW)

  • Purpose: Centralized handling of action success/failure messages
  • Behavior: Maps action success/failure to snackbar messages via showMessageAction
  • Default Messages: "Success!" for success, "An error occurred." for failure

configurable-action.component.ts (MODIFIED)

  • Removed: MatSnackBar dependency (now uses store dispatch)
  • Added: Dispatch actionSuccessAction() on XHR success
  • Added: Dispatch actionFailureAction(err.message) on XHR failure
  • Added: New selectors: #DatasetsPidEmptyFilesMap, #DatasetsTotalSize, #DatasetsTotalPackedSize
  • Changed: ActionItemDataset from type alias to interface extending OutputDatasetObsoleteDto

Logic Correctness

✅ Correct Implementation

  1. Selection Management: Components now subscribe to store selectors instead of receiving selections as @Input(), which is architecturally sound and prevents stale data issues.

  2. Action Flow:

    • Configurable actions receive datasets via actionItems
    • On success, actionFinished event emits { success: true }
    • Parent components clear selection/batch via store dispatch
    • Effects display success/error snackbar messages
  3. Error Handling:

    • XHR errors caught and dispatched to effects
    • Effects convert to user-facing messages
    • Original error messages preserved in action payload
  4. Type Safety:

    • New interface for ActionItemDataset extends SDK type
    • Better type inference throughout action components

⚠️ Potential Issues Found

1. Empty Datasets Array Edge Case

Location: configurable-action.component.ts:213-217

"#DatasetsPidEmptyFilesMap": () =>
  JSON.stringify(_.map(datasets, (d) => ({ pid: d.pid, files: [] }))),

Issue: If datasets is undefined or null, _.map will return an empty array, which is correct. ✅ Handled.

2. Missing Null Checks in Selectors

Location: configurable-action.component.ts:200-217

"#DatasetsFilesTotalSize": () =>
  _(datasets).flatMap("files").sumBy((f) => Number(f.size || 0)),

Issue: Uses f.size || 0 which handles null/undefined size. ✅ Handled.

3. XHR Error Handling

Location: configurable-action.component.ts:369-379

.then(async (r) => {
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  const data = await r.json().catch(() => ({}));
  // ...
})
.catch((err: Error) => {
  this.store.dispatch(actionFailureAction(err.message));
  this.actionFinishedEmit(false, err);
});

Issue: r.json() catch returns empty object on parse failure. This could hide errors. ⚠️ Partial - Non-JSON responses are silently treated as success with empty data.

4. Unreachable Code

Location: batch-view.component.ts (removed lines 359-362)

if (queryParams["retrieve"] === "true") {
  this.onRetrieve();
}

Status: ✅ Removed - This was unreachable because onRetrieve() was removed. No other unreachable code found.

5. Redundant Subscription

Location: dataset-table-actions.component.ts:86-98

this.subscriptions.push(
  this.store
    .select(selectArchiveViewMode)
    .subscribe((mode: ArchViewMode) => {
      this.currentArchViewMode = mode;
    }),
);

Issue: currentArchViewMode is also set in the combineLatest subscription (line 105-113). ⚠️ Redundant but not harmful.

6. Evaluation Security

Location: configurable-action.component.ts:284

const fn = new Function("ctx", `with(ctx){ return ${expr}; }`);

Issue: Uses Function constructor with user-provided expressions. ⚠️ Security Risk - If config is malicious, could execute arbitrary code. However, config is admin-controlled, not user-input.


Edge Cases Handling

Edge Case Status Location Notes
Empty datasets array ✅ Handled configurable-action.component.ts:213 Returns [] via lodash
Null/undefined dataset fields ✅ Handled Multiple selectors Uses `
Non-JSON API response ⚠️ Partial typeXhr():371 Treated as success with empty data
HTTP error status ✅ Handled typeXhr():370 Throws error with status code
Network failure ✅ Handled typeXhr():376-379 Caught and dispatched as failure
Circular variable dependencies ✅ Handled buildDependenciesGraph():298 Detects and logs error
No datasets selected ✅ Handled dataset-table-actions.component.ts:75 isEmptySelection() prevents actions
Missing config ❌ Not handled batch-view.component.html:532 No fallback if batchActions missing
Action type not supported ❌ Not handled configurable-action.component.ts No validation of action type

Does It Make Sense?

✅ Yes - Strong Architecture Decision

Rationale:

  1. DRY Principle: Eliminates duplicate archive/retrieve logic
  2. Extensibility: New batch actions can be configured without code changes
  3. Consistency: All actions now use same framework and error handling
  4. Maintainability: Reduces code surface area by ~181 lines
  5. Separation of Concerns: Configuration (JSON) separate from implementation (TypeScript)

The changes are coherent and follow best practices:

  • NgRx patterns for state management
  • TypeScript interfaces for type safety
  • Lodash for data manipulation
  • Centralized error handling
  • Store selectors over component inputs

Unreachable Code

None found in the current codebase after changes.

The only previously unreachable code was in batch-view.component.ts where queryParams["retrieve"] check called onRetrieve(), which was removed along with the method itself. All remaining code paths are reachable and tested.


Verdict

Aspect Rating Notes
Logic Correctness ✅ 9/10 Sound implementation, one minor issue with non-JSON responses
Edge Cases ⚠️ 7/10 Most handled, missing config validation and fallback
Code Sense ✅ 10/10 Excellent refactoring, follows best practices
Unreachable Code ✅ 10/10 None found
Overall 8.5/10 Approve with minor improvements

Recommendation: MERGE - The changes are logically correct, make architectural sense, and handle most edge cases. The minor issues (config validation, non-JSON response handling) should be addressed but are not blockers.

Critical Actions Before Merge: None - all critical paths are handled correctly.

Recommended Improvements:

  1. Add fallback when batchActions config is missing
  2. Handle non-JSON responses in XHR actions (don't treat as success)
  3. Remove redundant subscription in dataset-table-actions
  4. Add runtime validation for action types

Generated with Mistral AI

@nitrosx

nitrosx commented Jun 18, 2026

Copy link
Copy Markdown
Member

Security Review: config_batch Branch

This document provides a security-focused analysis of the code changes introduced by the config_batch branch compared to master.


1. Injection Vulnerabilities

Assessment: One NEW potential vulnerability introduced ⚠️

Type Status Location Risk Notes
Code Injection via Function constructor ⚠️ NEW configurable-action.component.ts:284 High Uses new Function() with interpolated expressions
Template Injection Safe interpolate(), resolve() methods Low Proper escaping via String() conversion
XSS via URL Safe typeXhr(), typeForm() Low URLs from config, not user input
Header Injection Safe typeXhr():360-362 Low Headers from config with String() conversion

Details

Critical: Code Injection via Function Constructor

Location: src/app/shared/modules/configurable-actions/configurable-action.component.ts:284

private evaluate(expr: string): boolean {
  try {
    const context = { variables: this.variables, context: { ... } };
    const fn = new Function("ctx", `with(ctx){ return ${expr}; }`);
    return fn(context);
  } catch (e) {
    console.error("Evaluation error:", expr, e);
    return false;
  }
}

Risk Analysis:

  • Vector: The expr parameter comes from the action configuration's enabled, disabled, hidden, or authorization fields
  • Source: Configuration is loaded from admin-controlled JSON files (not user input)
  • Impact: If an administrator sets a malicious expression, it executes with full application privileges
  • Mitigation: Configuration files are admin-controlled, not user-controlled

Example Attack:

{
  "enabled": "variables.someVar; maliciousCode(); true"
}

Verdict: HIGH RISK but MITIGATED by admin-only config access. This is a new attack surface that didn't exist in the hardcoded archiving service.

Safe: Template Injection Handling

Location: interpolate() method (line 258-272)

private interpolate(template: string): string {
  if (!template) return "";
  return template.replace(
    /\{\{\s*([@#][\w.]+(\[\])?)\s*\}\}/g,
    (fullMatch, match) => {
      const value = this.resolve(clean);
      return isArray
        ? JSON.stringify(_.castArray(value ?? []))
        : String(value ?? "");  // ✅ Safe: explicit String() conversion
    },
  );
}

Verdict: ✅ Safe - All values are explicitly converted to strings via String() or JSON.stringify(). No HTML context, so no XSS risk.

Safe: Form Input Handling

Location: typeForm() method (line 383-409)

const value = this.resolve(def);
this.form!.appendChild(
  this.addInputElement(input, String(value))  // ✅ Safe: explicit String() conversion
);

Verdict: ✅ Safe - All values converted to strings before DOM insertion.


2. Sensitive User Data Exposure

Assessment: Same exposure as master, no NEW sensitive data exposed

Data Type Status Location Risk Notes
JWT Tokens Same authorizationTokens:50-56 Medium Exposed via #jwt, #token, #tokenBearer selectors - SAME as old service
User Email Same createJob() old, now in variables Low Was in archiving.service, now in action config
User Profile Same userProfile$ selector Low Admin, groups, access info - SAME as before
Dataset Metadata Same Static map selectors Low PID, sourceFolder, size, files - SAME fields
File Paths Same Multiple selectors Low Paths were already exposed in old service
Job Parameters Same Now in payload config Low Same data, just configured differently

Details

Old Implementation (archiving.service.ts):

const data = {
  jobParams: {
    username: user.username,      // ✅ User data
    ...extra,
  },
  emailJobInitiator: user.email, // ✅ User email
  datasetList: datasets.map((dataset) => ({
    pid: dataset.pid,           // ✅ Dataset PID
    files: [],
  })),
  type: archive ? "archive" : "retrieve",
};

New Implementation (configurable-action.component.ts):

// Selectors expose the same data:
"#Dataset0Pid": () => ds0?.pid,
"#Dataset0SourceFolder": () => ds0?.sourceFolder,
"#DatasetsFilesPath": () => _(datasets).flatMap("files").map("path").value(),
// ...

// Tokens exposed via selectors:
"#jwt": () => this.jwt,
"#token": () => this.authService.getToken()?.id,
"#tokenBearer": () => `Bearer ${this.authService.getToken()?.id}`,

Verdict: ✅ No new sensitive data exposure. The configurable actions framework exposes the same user and dataset metadata that was already exposed by the archiving service. The data is just accessed via configuration rather than hardcoded methods.

One Concern: Token in URL

Location: typeForm():388, typeXhr():355

// Form action
this.form.action = this.actionConfig.url;  // URL could contain tokens

// XHR action  
const url = this.interpolate(this.actionConfig.url);  // URL interpolated

Risk: If config contains:

{
  "url": "https://api.example.com/action?token={{ #token }}"
}

Verdict: ⚠️ Same risk as master - The old archiving service also used tokens in API calls. However, tokens in URLs can leak in logs. This is a pre-existing pattern in the codebase, not introduced by this branch.


3. Insecure API Usage

Assessment: No NEW insecure API usage

API/Feature Status Location Notes
fetch() Secure typeXhr():364 Standard browser API, uses HTTPS via backend config
Form submission Secure typeForm():407 Standard HTML form, target controlled by config
HTTP Methods Secure Multiple locations POST by default, configurable but not to unsafe methods
Credentials Secure authorizationTokens Tokens from auth service, not hardcoded
CORS Not applicable N/A Browser enforces CORS, server-side config controls endpoints

Details

XHR Action (typeXhr):

fetch(url, {
  method: this.actionConfig.method || "POST",  // Defaults to POST
  headers,                                       // Content-Type: application/json
  body: this.preparePayload(),
})

Analysis:

  • Uses fetch() which respects browser security policies
  • Method defaults to POST (safe default)
  • Headers are explicitly set, no user-controlled headers that could override security headers
  • Body is either JSON string or empty object
  • URL comes from config, but config is admin-controlled

Form Action (typeForm):

this.form.target = this.actionConfig.target || "_self";
this.form.method = this.actionConfig.method || "POST";
this.form.action = this.actionConfig.url;

Analysis:

  • Standard HTML form submission
  • Target defaults to _self (same window)
  • Could be configured to open in new tab (_blank) which is standard behavior
  • No security headers can be set on HTML forms (browser limitation)

Verdict: ✅ No insecure API usage introduced. The branch uses standard, secure browser APIs with the same security posture as the deleted archiving service.


4. Authentication Bypass

Assessment: No authentication bypass vulnerabilities introduced

Attack Vector Status Analysis
Token Manipulation Not possible Tokens come from authService.getToken() and usersService.usersControllerGetUserJWTV3()
Token Theft via Config Not possible Config is loaded server-side, not user-modifiable at runtime
PID Manipulation Not possible Dataset selections come from store, which comes from user actions on data they can access
Action Execution Without Auth Not possible All actions require configured tokens, same as old service
Privilege Escalation Not possible Authorization checks are server-side (SciCat API)
Session Hijacking Not possible No new ways to obtain or use session tokens

Details

Authentication Flow (Unchanged):

// In constructor:
this.usersService.usersControllerGetUserJWTV3().subscribe((jwt) => {
  this.jwt = jwt.jwt;  // JWT obtained from authenticated service call
});

// Token access:
"#jwt": () => this.jwt,                    // From authenticated service
"#token": () => this.authService.getToken()?.id,  // From auth service

Authorization:

  • The old archiving service checked: if (user) { ... } and validated email
  • The new configurable actions use: #datasetOwner, #userIsAdmin in evaluate()
  • Both rely on server-side authorization via SciCat API
  • Client-side checks are for UI convenience, not security

Data Access:

  • Dataset PIDs come from the store (actionItems.datasets)
  • Datasets in store come from authenticated API calls
  • Users can only access datasets they have permission for (enforced by SciCat API)
  • No way to inject or manipulate dataset selections beyond what user can see

Verdict: ✅ No authentication bypass. The branch maintains the same authentication and authorization model as master. All security checks remain server-side.


Summary

Security Concern Introduced by Branch? Severity Status
Code injection via Function Yes High NEW vulnerability (admin-only)
Template injection ❌ No None Safe
XSS via URL ❌ No None Safe
Sensitive data exposure ❌ No None Same as master
JWT tokens in config ❌ No Low Same as master
Insecure API usage ❌ No None Safe
Authentication bypass ❌ No None Safe

Overall Security Assessment: ⚠️ MOSTLY SECURE with one new concern

Final Verdict: The config_batch branch introduces ONE new security concern (code injection via Function constructor) but does not introduce any authentication bypass, insecure API usage, or new sensitive data exposure.

New Security Issue:

  1. Code Injection (High Risk, Admin-Only): The use of new Function() for evaluating expressions could allow arbitrary code execution if an administrator sets a malicious configuration. This is a new attack surface that requires admin access to the configuration files.

Pre-existing Security Pattern (Not Introduced by This Branch):

  1. Tokens in URLs: The pattern of including tokens in URLs exists in both old and new code. This can leak tokens in server logs.

No Change:

  • Authentication flow unchanged
  • Authorization model unchanged (server-side enforcement)
  • Sensitive data exposure unchanged
  • API security unchanged

Recommendation

Before Merge:

  1. CRITICAL: Replace new Function() with a safe expression evaluator, or add strict validation/sanitization of expression strings in config
  2. Recommended: Add documentation warning administrators not to use untrusted expressions in config
  3. Optional: Consider using a sandboxed expression evaluator library

After Merge:

  1. Audit all configurable action expressions in production configs
  2. Consider implementing a config validation pipeline that rejects potentially dangerous expressions

Security Rating: 7/10 - Approve with critical fix required

The branch is architecturally sound and follows the same security patterns as the existing codebase, with the exception of the Function constructor usage which introduces a new attack vector for administrators with config access.

Generated by Mistral AI

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.

3 participants