xternalsoft/laravel-patrowl
Laravel Patrowl integrates the Patrowl security scanning orchestrator with your Laravel application, offering a simple wrapper to configure connections, launch scans, and manage findings so teams can bring automated security monitoring into their existing Laravel workflows.
This is a v2 major release, so treat the upgrade as a migration rather than a routine dependency bump.
Read UPGRADE.md, the README, and the new examples first. The examples (controls_assets_list.php, controls_warning_by_organization.php, assets_move.php) are the fastest way to see the intended v2 call shapes.
Grep your codebase for the removed/breaking APIs before upgrading:
grep -rn "organizations()->default\|assets()->replace\|assetTags()->all\|assetTags()->get" app/
grep -rn "organizations()->all" app/
grep -rn "relatedTechnologies\|criticality" app/
Update the package and reconcile configuration. The DTO layer no longer reads Laravel's config(). If you previously relied on a config value to supply the default organization, set it explicitly on the connector/client instance instead. This also means the package is safer to use outside Laravel, but it changes where the default organization comes from.
First meaningful use case: pull the organization list and feed it into an asset inventory or control-impact report.
$organizations = $client->organizations()->all(); // OrganizationLiteData[]
foreach ($organizations as $organization) {
// OrganizationLiteData objects, not raw arrays
$organization->id;
}
Use controls()->assets($controlId) for impact reporting. The control detail endpoint in API 2.36–2.38 returns empty assets_impacted and assets_warning; this method is the supported way to retrieve them, with auto-pagination.
organizations()->all() now returns OrganizationLiteData[]. Update sync jobs to map DTO properties into your models rather than treating the result as raw arrays:
foreach ($client->organizations()->all() as $organization) {
Organization::updateOrCreate(
['patrowl_id' => $organization->id],
[/* mapped fields */]
);
}
This is a good place to cache or queue the organization list, since it is typically stable and used to resolve IDs for other calls.
assets()->create() now sends organization and teams as the API requires, and throws AssetAlreadyExistsException on HTTP 208. In day-to-day synchronization jobs, 208 should usually be treated as success:
use YourPackage\Exceptions\AssetAlreadyExistsException;
try {
$client->assets()->create($createAssetData);
} catch (AssetAlreadyExistsException $e) {
// Asset already exists; look it up or continue.
}
Do not swallow this exception in interactive code unless you have a clear reconciliation path—it hides genuine duplicate-creation mistakes.
assets()->move($assetId, $targetOrganizationId, deleteSource: true) recreates an asset in another organization, matching or creating tags, then deletes the source. Use it for tenant migrations or organization restructuring.
Important operational details:
deleteSource defaults to true. For production migrations, consider calling with deleteSource: false first, verifying the recreated asset and its tags, then deleting the source or re-running with deletion enabled.Use controls()->assets($controlId) instead of trusting the control detail payload. The method auto-paginates, so it is safe to use directly in report generation:
foreach ($client->controls()->assets($controlId) as $asset) {
// impacted or potentially impacted asset
}
For organization-level reporting, see controls_warning_by_organization.php in the examples directory.
The new read-only EASM surface covers domains, IPv4s, resolutions, ASNs, services, web servers, certificates, vendors, products, technologies, technology detections, and CVEs. Typical patterns:
Because this is read-only, do not build write-back workflows around these endpoints.
assets()->addTag() and assets()->removeTag() now use the current API endpoints. Keep tag changes in a dedicated service/job rather than scattering them through controllers. Tag DTOs map assets_count, which is useful for tag-usage reporting.
organizations()->default(), assets()->replace(), and assetTags()->all() / get() are gone. The tag methods were removed because the API no longer serves GET /assets/tags/ and /assets/tags/{id}/, so there is no drop-in replacement for listing tags.organizations()->all() changed return shape. Code that expected a different structure will break. Iterate OrganizationLiteData objects and use their properties.AssetAlreadyExistsException explicitly; do not let it fail bulk synchronization jobs.CreateAssetGroupData::$criticality is no longer nullable. Always supply a value when creating asset groups; nullable-criticality code paths need updating.AssetInListData::$relatedTechnologies is now an array. Any code doing string concatenation, str_contains, or implicit string conversion on this property will break or emit warnings.config(). This fixes crashes outside Laravel, but it also means the connector's default organization is no longer overridden by config. Set the default organization deliberately on the connector and test jobs that previously depended on ambient config.assets_impacted and assets_warning are empty from the detail endpoint. Always use controls()->assets() for impacted/potentially impacted assets.assets()->move() does not preserve ownership. Owners, teams, and groups are returned in the result but not copied. Build a follow-up step to restore them.How can I help you explore Laravel packages today?