Skip to content
Closed
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
63 changes: 63 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# PM4 package: Laravel is provided by the host app; composer.json is only for this package's own PHP code/deps.
# JS build: Vue 2 + Laravel Mix (manifest: package.json). Built assets under public/js/ are not scanned by Dependabot.
#
# Policy: NO routine version-update PRs (open-pull-requests-limit: 0).
# Security/CVE PRs are handled by Dependabot security updates (org Settings → Code security).
# Security PRs are batched into one PR per ecosystem (patch/minor).
# Major security PRs will still open if no patch/minor fix exists — treat as manual review.
#
# Vue 2 pin: security fixes requiring Vue 3+ will be suppressed — accepted risk,
# migration not planned. Same applies to vue-loader, vue-template-compiler, @vue/cli.
#
# Webpack pin: develop lockfile pins 5.91.0; Dependabot security PRs may bump to 5.107+.
# 5.106.0 is the last release that still ships SizeFormatHelpers (Laravel Mix compat).
# Block webpack >= 5.107 so batched security PRs keep other bumps without breaking the build.
#
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 0
ignore:
# If you ever raise `open-pull-requests-limit`, this skips routine major bumps.
# Note: update-types has no effect on security updates.
- dependency-name: "*"
update-types: ["version-update:semver-major"]
- dependency-name: "vue"
versions: [">=3.0.0"] # stay on Vue 2.x — suppresses security PRs requiring v3+ too
- dependency-name: "@vue/cli*"
versions: [">=5.0.0"] # CLI v5+ is Vue 3 era
- dependency-name: "vue-loader"
versions: [">=17.0.0"] # vue-loader v17+ drops Vue 2 support
- dependency-name: "vue-template-compiler"
versions: [">=3.0.0"] # must stay in sync with Vue 2.x
- dependency-name: "webpack"
versions: [">=5.107.0"] # 5.106.0 last with SizeFormatHelpers; block 5.107+ security bumps
groups:
npm-security:
applies-to: security-updates # batches all JS security PRs into one
patterns: # note: update-types has no effect here for security
- "*"
# version suppressions (vue, webpack, etc.) live in top-level `ignore` above — groups do not support `ignore`

- package-ecosystem: composer
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 0
ignore:
# If you ever raise `open-pull-requests-limit`, this skips routine major bumps.
# Note: update-types has no effect on security updates.
- dependency-name: "*"
update-types: ["version-update:semver-major"]
groups:
composer-security:
applies-to: security-updates # batches all PHP security PRs into one
patterns:
- "*"

11 changes: 11 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Nightly Vulnerability Scan

on:
schedule:
- cron: '0 0 * * *' # midnight UTC
workflow_dispatch:

jobs:
nightly:
uses: processmaker/.github/.github/workflows/nightly.yml@main
secrets: inherit
31 changes: 22 additions & 9 deletions ProcessMaker/Http/Controllers/Api/PermissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use ProcessMaker\Events\PermissionChanged;
use Illuminate\Validation\ValidationException;
use ProcessMaker\Events\PermissionUpdated;
use ProcessMaker\Http\Controllers\Controller;
use ProcessMaker\Http\Resources\ApiCollection;
use ProcessMaker\Models\Group;
use ProcessMaker\Models\Permission;
use ProcessMaker\Models\User;
Expand All @@ -30,7 +28,7 @@ class PermissionController extends Controller
*
* @param Request $request
*
* @return Response
* @return \Illuminate\Support\Collection
*/
public function index(Request $request)
{
Expand All @@ -44,7 +42,7 @@ public function index(Request $request)
*
* @param Request $request
*
* @return Response
* @return \Illuminate\Http\Response
*
* @OA\Put(
* path="/permissions",
Expand Down Expand Up @@ -82,8 +80,22 @@ public function index(Request $request)
*/
public function update(Request $request)
{
$request->validate([
'user_id' => 'required_without:group_id|integer',
'group_id' => 'required_without:user_id|integer',
'permission_names' => 'nullable|array',
]);

if ($request->filled('user_id') && $request->filled('group_id')) {
throw ValidationException::withMessages([
'user_id' => [__('The user_id field cannot be present when group_id is present.')],
'group_id' => [__('The group_id field cannot be present when user_id is present.')],
]);
}

//Obtain the requested user or group
if ($request->input('user_id')) {
if ($request->filled('user_id')) {
$this->authorize('edit-users');
$entity = User::findOrFail($request->input('user_id'));
// Obtain user old Permissions before save
$originalPermissionNames = $entity->permissions()->pluck('name')->toArray();
Expand All @@ -98,14 +110,15 @@ public function update(Request $request)
$entity->is_administrator = $isSettingToAdmin;
$entity->save();
}
} elseif ($request->input('group_id')) {
} elseif ($request->filled('group_id')) {
$this->authorize('edit-groups');
$entity = Group::findOrFail($request->input('group_id'));
// Obtain group old Permissions before save
$originalPermissionNames = $entity->permissions()->pluck('name')->toArray();
}

// Obtain the requested permission names for that entity
$requestPermissions = $request->input('permission_names');
$requestPermissions = $request->input('permission_names') ?? [];

// Convert permission names into a collection of Permission models
$permissions = Permission::whereIn('name', $requestPermissions)->get();
Expand All @@ -114,7 +127,7 @@ public function update(Request $request)
PermissionUpdated::dispatch(
$requestPermissions,
$originalPermissionNames,
$entity->is_administrator ?: false,
$entity instanceof User ? $entity->is_administrator : false,
$request->input('user_id'),
$request->input('group_id')
);
Expand Down
29 changes: 17 additions & 12 deletions ProcessMaker/Http/Controllers/Api/ProcessRequestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use ProcessMaker\Models\Comment;
use ProcessMaker\Models\ProcessRequest;
use ProcessMaker\Models\ProcessRequestToken;
use ProcessMaker\Models\Screen;
use ProcessMaker\Models\User;
use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CatchEventInterface;
Expand Down Expand Up @@ -793,8 +794,8 @@ public function screenRequested(Request $httpRequest, ProcessRequest $request)
$query = ProcessRequestToken::query();
$query->select('id', 'element_id', 'process_id', 'process_request_id', 'data')
->where('process_request_id', $request->id)
->whereNotIn('element_type', ['startEvent', 'end_event', 'scriptTask'])
->where('status', 'CLOSED')
->whereNotIn('element_type', ['end_event', 'scriptTask'])
->whereIn('status', ['CLOSED', 'TRIGGERED'])
->orderBy('completed_at');

$response =
Expand All @@ -806,19 +807,23 @@ public function screenRequested(Request $httpRequest, ProcessRequest $request)
$collection = $response->getCollection()
->transform(function ($token): ?object {
$definition = $token->getDefinition();
if (array_key_exists('screenRef', $definition)) {
$screen = $token->getScreenVersion();
if ($screen) {
$dataManager = new DataManager();
$screen->data = $dataManager->getData($token, true);
$screen->screen_id = $screen->id;
$screen->id = $token->id;

return $screen;
if (!array_key_exists('screenRef', $definition)) {
$config = isset($definition['config']) ? json_decode($definition['config']) : null;
$screenRef = (isset($config) && isset($config->web_entry)) ? $config->web_entry->screen_id ?? null : null;
if (!$screenRef) {
return null;
}
}
$screen = $token->getScreenVersion() ?? null;

return null;
if ($screen) {
$dataManager = new DataManager();
$screen->data = $dataManager->getData($token, true);
$screen->screen_id = $screen->id;
$screen->id = $token->id;
}

return $screen;
})
->reject(fn ($item) => $item === null)
->values();
Expand Down
20 changes: 19 additions & 1 deletion ProcessMaker/Http/Controllers/Api/TaskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,27 @@ public function getTasksByCase(Request $request, User $user = null)
}

// Validate the inputs, including optional ones
$allowedStatuses = ['ACTIVE', 'CLOSED', 'TRIGGERED'];
$request->validate([
'case_number' => 'required|integer',
'status' => 'nullable|string|in:ACTIVE,CLOSED',
'status' => [
'nullable',
'string',
function ($attribute, $value, $fail) use ($allowedStatuses) {
$statuses = array_map(fn ($v) => mb_strtoupper(trim($v)), explode(',', $value));
$statuses = array_filter($statuses);
foreach ($statuses as $status) {
if (!in_array($status, $allowedStatuses)) {
$fail(__('The :attribute must contain only valid statuses: :values. Multiple statuses can be comma-separated.', [
'attribute' => $attribute,
'values' => implode(', ', $allowedStatuses),
]));

return;
}
}
},
],
'order_by' => 'nullable|string|in:id,element_name,due_at,user.lastname,process.name',
'order_direction' => 'nullable|string|in:asc,desc',
'page' => 'nullable|integer|min:1',
Expand Down
10 changes: 10 additions & 0 deletions ProcessMaker/Http/Controllers/HomeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ public function redirectToIntended()
return redirect($url)->withCookie(\Cookie::forget('processmaker_intended'));
}

// No intended URL. Honor the tenant's package-dynamic-ui home page
// if it's installed, so admins'configured landing pages are still
// respected, before falling back to /requests.
if (Auth::check() && class_exists(\ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::class)) {
$homePage = \ProcessMaker\Package\PackageDynamicUI\Models\DynamicUI::getHomePage(Auth::user());
if (!empty($homePage)) {
return redirect($homePage);
}
}

return redirect()->route('requests.index');
}
}
2 changes: 1 addition & 1 deletion ProcessMaker/Http/Resources/ScreenVersion.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function toArray($request)
*/
private function setDefaultScreenForNestedScreens(array &$screenVersion): void
{
$configArray = $screenVersion['config'];
$configArray = $screenVersion['config'] ?? [];
foreach ($configArray as $key => $config) {
foreach ($config['items'] as $itemKey => $item) {
if (isset($item['component']) && $item['component'] === 'FormNestedScreen') {
Expand Down
Loading
Loading