From 629316b9633c6d0ed60676bf0cd473b7cfcf4ac2 Mon Sep 17 00:00:00 2001 From: Shashank Taliwal Date: Thu, 26 Dec 2024 17:45:35 +0530 Subject: [PATCH] RTDEV-52485 - Enhanced restrict-download-by-property-value worker to handle multiple forbidden key and values --- .../README.md | 73 +++++++++- .../worker.ts | 1 - .../README.md | 71 +++++++++- .../worker.ts | 120 ++++++++++++++-- .../README.md | 88 +++++++++++- .../package.json | 6 +- .../worker.ts | 55 +++++-- .../README.md | 85 ++++++++++- .../worker.ts | 33 ++++- .../property-creation-permission/README.md | 66 ++++++++- .../allow-upload-by-pattern/worker.ts | 1 - .../BEFORE_UPLOAD/repo-quota/worker.ts | 1 - .../restrict-overwrite/worker.ts | 1 - samples/artifactory/GENERIC_EVENT/README.md | 51 +++++++ .../GENERIC_EVENT/remote-backup/README.md | 134 +++++++++++++++--- .../GENERIC_EVENT/remote-backup/worker.ts | 1 - samples/artifactory/README.md | 86 +++++++++++ 17 files changed, 804 insertions(+), 69 deletions(-) create mode 100644 samples/artifactory/GENERIC_EVENT/README.md create mode 100644 samples/artifactory/README.md diff --git a/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/README.md b/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/README.md index f505d16..4419a81 100644 --- a/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/README.md +++ b/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/README.md @@ -1,3 +1,72 @@ -# Log download activity to an external endpoint +# Log Download activity to an external endpoint -This simple worker will send a log containing the name of the artifact that has been downloaded, the repository and the token/userid that requested the download. \ No newline at end of file +## Overview +This worker listens to the `AFTER_DOWNLOAD` event in JFrog Artifactory and notifies an external endpoint when a file is downloaded. The worker logs the artifact's details, the repository, and the user responsible for the download. + +## Functionality + +1. **Trigger:** Activated after a download event occurs in Artifactory. +2. **Notification:** Sends a log containing details about the downloaded artifact to a specified external endpoint. +3. **Authentication:** Uses a bearer token stored in a secret to authenticate with the external endpoint. +4. **Error Handling:** Logs and updates the response message if the notification fails. + +## Configuration + +To customize the worker, you need to update two constants in the source code: + +- `URL`: The endpoint where the download log will be sent. + Example: + ```typescript + const URL = 'https://'; + ``` + +- `SECRET_NAME`: The name of the secret that contains the bearer token for authentication. + Example: + ```typescript + const SECRET_NAME = 'myBearerToken'; + ``` + +## How It Works + +1. **Download Event:** The worker is triggered when an artifact is downloaded. +2. **Log Creation:** The worker creates a log message with the following details: + - Artifact path + - Repository key + - User ID or token used for the download +3. **Notification:** Sends the log message as a POST request to the external endpoint. +4. **Response Handling:** + - Logs success if the notification is successfully sent. + - Logs a warning or error if the notification fails and updates the response message accordingly. + +## Example Log Message + +```plaintext +The artifact '' has been downloaded by the : from the repository ''. +``` + +## Response Messages + +- **Successful Notification:** + - Message: `Download activity successfully logged` + +- **Failed Notification:** + - Message: `Failed to log download activity` + +## Dependencies + +This worker relies on the following JFrog Workers APIs: +- `PlatformContext` for accessing platform resources (e.g., secrets, HTTP clients). +- `AfterDownloadRequest` and `AfterDownloadResponse` for handling download event payloads and responses. + +## Recommendations + +1. **Endpoint Configuration:** Ensure the external endpoint is correctly set and accessible. +2. **Bearer Token Secret:** Store the bearer token securely as a secret in the JFrog platform. +3. **Monitoring:** Regularly monitor logs for failed notifications to identify issues. +4. **Testing:** Test the worker in a staging environment before deploying it in production. + +## Error Scenarios + +- **Network Errors:** Occur if the external endpoint is unreachable. +- **Authentication Failures:** Occur if the bearer token is invalid or missing. +- **Invalid URL:** Occurs if the provided endpoint URL is incorrect. diff --git a/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/worker.ts b/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/worker.ts index 33d0854..ff71d19 100755 --- a/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/worker.ts +++ b/samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/worker.ts @@ -1,6 +1,5 @@ import { PlatformContext, AfterDownloadRequest, AfterDownloadResponse } from 'jfrog-workers'; - export default async (context: PlatformContext, data: AfterDownloadRequest): Promise => { // MODIFY THOSE TWO CONSTANTS TO FIT YOUR NEEDS const URL = 'https://'; diff --git a/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/README.md b/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/README.md index a165adb..1b62935 100644 --- a/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/README.md +++ b/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/README.md @@ -1,4 +1,71 @@ -# Block download if last Xray scan is older than 30 seconds +# Worker for Restricting Downloads Based on Xray Scan Recency -This worker will block the download if the artifact has not been scanned by Xray within the last 30 seconds and send a report to an external endpoint. For the example the expiry is quite short, but you may edit the variables EXPIRY and SECONDS to change the expiry duration. +## Overview +This worker is triggered by the `BEFORE_DOWNLOAD` event of Artifactory. Its primary purpose is to restrict downloads of artifacts that have not undergone an Xray scan within a specified threshold duration. This ensures artifacts comply with security policies by enforcing timely scans. + +## Functionality + +### Key Features +- **Xray Scan Check:** Verifies the most recent Xray scan date of an artifact. +- **Threshold Enforcement:** Restricts download if the last scan exceeds the specified threshold days or date. +- **Error Handling:** Issues a warning and allows download if Xray is unavailable or errors occur during the check. + +### Worker Logic +1. **Xray Availability Check:** Verifies if Xray is operational by pinging the Xray system. +2. **Artifact Scan Details Fetching:** Retrieves artifact scan details, including the last scan date, using the artifact name and repository key. +3. **Decision Making:** + - Stops the download if the last scan is older than the allowed threshold. + - Allows download with a warning if scan details cannot be retrieved or Xray is unavailable. + - Proceeds with the download if the scan is recent. + +## Configuration + +### Threshold Configuration +- **Threshold Days:** Defines the maximum allowed age (in days) of the last Xray scan. Default: 2 days. +- **Threshold Date:** Defines the latest allowable date for the last scan. Default: 7 days prior to the current date. + +## Response Types + +### Download Proceed +```json +{ + "status": "DOWNLOAD_PROCEED", + "message": "Proceeding with download" +} +``` +- **Explanation:** Indicates the artifact complies with the scan recency requirements. + +### Download Stopped +```json +{ + "status": "DOWNLOAD_STOP", + "message": "Stopping Download, because last scan date is older than allowed threshold duration of days." +} +``` +- **Explanation:** Indicates the artifact's last scan is outdated, and the download is blocked. + +### Warning Response +```json +{ + "status": "DOWNLOAD_WARN", + "message": "Could not check for Xray scans because Xray is not available. Proceeding download with warning." +} +``` +- **Explanation:** Indicates that the worker encountered an error (e.g., Xray unavailability), but the download is allowed with a warning. + +## Error Handling + +- **Xray Unavailability:** Issues a warning and allows download if Xray is not operational. +- **Scan Check Failure:** Logs the error and proceeds with a warning. + +## Recommendations + +1. **Threshold Adjustment:** + - Update the threshold days or date as per organizational requirements. + +2. **Monitoring and Logging:** + - Regularly review logs for `DOWNLOAD_WARN` responses to address recurring issues. + +3. **Testing:** + - Validate the worker functionality in a staging environment before deploying to production. \ No newline at end of file diff --git a/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/worker.ts b/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/worker.ts index 15d237a..f735127 100755 --- a/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/worker.ts +++ b/samples/artifactory/BEFORE_DOWNLOAD/resctrict-download-if-no-xray-scan-since/worker.ts @@ -1,30 +1,124 @@ import { PlatformContext, BeforeDownloadRequest, BeforeDownloadResponse, DownloadStatus } from 'jfrog-workers'; export default async (context: PlatformContext, data: BeforeDownloadRequest): Promise => { + const thresholdDate = new Date(new Date().getUTCDate() - 7); + const thresholdDays = 2; + const isXrayAvailable = await checkIfXrayAvailable(); + if (!isXrayAvailable) { + return { + status: DownloadStatus.DOWNLOAD_WARN, + message: "Could not check for xray scans because xray is not available. Proceeding download with warning.", + headers: {} // This can be populated if response headers are required to be added/overriden. + } + } let status: DownloadStatus = DownloadStatus.DOWNLOAD_UNSPECIFIED; + let message = ''; + + let responseData: { + "data": [ + { + "name": string, + "repo_path": string, + "package_id": string, + "version": string, + "sec_issues": { + "critical": number, + "high": number, + "low": number, + "medium": number, + "total": number + }, + "scans_status": { + "overall": { + "status": string, + "time": string + } + } + "size": string, + "violations": number, + "created": string, + "deployed_by": string, + "repo_full_path": string + } + ], + "offset": number + } = null; try { - // The in-browser HTTP client facilitates making calls to the JFrog REST APIs - //To call an external endpoint, use 'await context.clients.axios.get("https://foo.com")' - const res = await context.clients.platformHttp.get('/artifactory/api/v1/system/readiness'); + const artifactName = data.metadata.repoPath.path.substr(data.metadata.repoPath.path.lastIndexOf('/') + 1); + const repoKey = data.metadata.repoPath.key; + + const res = await context.clients.platformHttp.get(`/xray/api/v1/artifacts?repo=${repoKey}&search=${artifactName}&num_of_rows=1`); + responseData = res.data; - // You should reach this part if the HTTP request status is successful (HTTP Status 399 or lower) if (res.status === 200) { - status = DownloadStatus.DOWNLOAD_PROCEED; - console.log("Artifactory ping success"); + // console.log(responseData); + console.log(responseData.data); + const lastScanDetails = responseData.data[0].scans_status.overall; + if (lastScanDetails.status !== "DONE") { + return { + status: DownloadStatus.DOWNLOAD_WARN, + message: "Scan is on-going, proceeding with download with a warning.", + headers: {} // This can be populated if response headers are required to be added/overriden. + } + } else { + const lastScanDate = new Date(lastScanDetails.time); + const currentDate = new Date(); + console.log(differenceInDays(lastScanDate, currentDate)); + if (thresholdDays < differenceInDays(lastScanDate, currentDate)) { + return { + status: DownloadStatus.DOWNLOAD_STOP, + message: `Stopping Download, because last scan date is older than allowed threshold duration of ${thresholdDays} days.`, + headers: {} + } + } else if(lastScanDate' } caused by : ${ error.message }`) + } catch (error) { + message = `Encountered error: ${error.message} during scan check. Download will proceed with warning.`; + status = DownloadStatus.DOWNLOAD_WARN; + console.error(`Request failed: ${error.message}`); } return { status, - message: 'Overwritten by worker-service if an error occurs.', + message, + headers: {} // This can be populated if response headers are required to be added/overriden. + } + + + async function checkIfXrayAvailable(): Promise { + let response; + try { + response = await context.clients.platformHttp.get('/xray/api/v1/system/ping'); + if (response.data.status !== "pong") { + throw new Error("Xray not available"); + } + return true; + } catch (error) { + console.log(`Encountered error ${error.message} while checking for xray readiness. Allowing download with a warning`); + return false; + } + + } + + function differenceInDays(lastScanDate: Date, currentDate: Date): number { + const differenceInTime = currentDate.getTime() - lastScanDate.getTime(); + return Math.round(differenceInTime / (1000 * 3600 * 24)); } -} +} \ No newline at end of file diff --git a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/README.md b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/README.md index 6932141..1757641 100644 --- a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/README.md +++ b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/README.md @@ -1,4 +1,88 @@ -# Restrict Download By Property Value +Restrict Download by Property Value +===================================== -This worker will block the download on artifacts on which the property EXPECTED_PROPERTY_KEY is set with the value EXPECTED_PROPERTY_VALUE. +This worker is triggered by the `BEFORE_DOWNLOAD` event of Artifactory. Its primary purpose is to block artifact downloads if the artifact contains certain forbidden properties with restricted values. This ensures compliance with organizational policies by preventing downloads of restricted artifacts. +Functionality +------------- +- **Forbidden Properties Check:** Blocks download if an artifact contains a forbidden property with restricted values. +- **Error Handling:** Issues a warning and allows download if the properties cannot be verified due to an error. +- **Headers:** Optionally modifies response headers if required. + +Worker Logic +------------ +1. **Property Fetching**: Queries Artifactory for artifact properties using the repository key and artifact path. +2. **Decision Making**: + - Blocks the download if a forbidden property with restricted values is present. + - Allows the download if no forbidden properties are found. + - Issues a warning and allows download for errors or unexpected results. + +Payload +------- +The worker operates on the `BEFORE_DOWNLOAD` event payload provided by Artifactory. It uses metadata such as the artifact's repository path and key to fetch properties. + +Configuration +------------- +- **Forbidden Properties**: Define the list of forbidden properties and their restricted values in the worker script. Example: + ```typescript + let forbiddenProperties: ForbiddenProperty[] = []; + forbiddenProperties.push({ + key: "FORBIDDEN", + values: ["true"] + }); + ``` + +Possible Responses +------------------ + +### Download Proceed +- **Structure:** + ```json + { + "status": "DOWNLOAD_PROCEED", + "message": "Allowing Download" + } + ``` +- **Explanation:** + - Indicates the artifact does not contain any forbidden properties, and the download is allowed. + +### Download Stopped +- **Structure:** + ```json + { + "status": "DOWNLOAD_STOP", + "message": "Stopping Download because forbiddenProperty is present with forbidden values" + } + ``` +- **Explanation:** + - Indicates the artifact contains a forbidden property, and the download is blocked. + +### Warning Response +- **Structure:** + ```json + { + "status": "DOWNLOAD_WARN", + "message": "Download proceed with a warning. Could not check if artifact is forbidden or not." + } + ``` +- **Explanation:** + - Indicates that the worker encountered an error (e.g., API failure), but the download is allowed with a warning. + +### Headers +- The worker response can optionally include modified headers. The `headers` field in the response is currently empty but can be populated if required. + +Error Handling +-------------- +- **Property Fetch Failure:** Allows download with a warning message. +- **Unexpected Errors:** Logs errors and proceeds with a warning. + +Recommendations +--------------- +1. **Forbidden Properties Definition**: + - Update the list of forbidden properties and their restricted values as per organizational policies. + +2. **Monitoring and Logging**: + - Review logs for `DOWNLOAD_WARN` responses to identify and address recurring issues. + +3. **Testing**: + - Validate the worker functionality in a staging environment before deploying to production. diff --git a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/package.json b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/package.json index 97aebe6..4bf51da 100755 --- a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/package.json +++ b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/package.json @@ -1,7 +1,7 @@ { "name": "restrict-download-by-property-value", "description": "Run a script on BEFORE_DOWNLOAD", - "version": "1.0.0", + "version": "1.1.0", "scripts": { "deploy": "jf worker deploy", "undeploy": "jf worker rm \"restrict-download-by-property-value\"", @@ -26,7 +26,9 @@ "clearMocks": true, "maxConcurrency": 1, "testRegex": "\\.spec\\.ts$", - "moduleDirectories": ["node_modules"], + "moduleDirectories": [ + "node_modules" + ], "collectCoverageFrom": [ "**/*.ts" ], diff --git a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/worker.ts b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/worker.ts index 53ddfb7..b19f904 100755 --- a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/worker.ts +++ b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-by-property-value/worker.ts @@ -1,30 +1,33 @@ import { PlatformContext, BeforeDownloadRequest, BeforeDownloadResponse, DownloadStatus } from 'jfrog-workers'; - export default async (context: PlatformContext, data: BeforeDownloadRequest): Promise => { - const EXPECTED_PROPERTY_KEY = 'FORBIDDEN'; - const EXPECTED_PROPERTY_VALUE = 'true'; + let forbiddenProperties: ForbiddenProperty[] = []; + forbiddenProperties.push({ + key: "FORBIDDEN", + values: ["true"] + }); - let status: DownloadStatus = DownloadStatus.DOWNLOAD_UNSPECIFIED; - let message = ''; + let status: DownloadStatus = DownloadStatus.DOWNLOAD_PROCEED; + let message = 'Allowing Download'; try { const res = await context.clients.platformHttp.get(`/artifactory/api/storage/${data.repoPath.key}/${data.repoPath.path}?properties`); - const artifactData: { // Data structure from Artifactory endpoint: https://jfrog.com/help/r/jfrog-rest-apis/item-properties + const artifactData: { "properties": { [key: string]: string[] }, "uri": string } = res.data; - - if (artifactData.properties[EXPECTED_PROPERTY_KEY] && artifactData.properties[EXPECTED_PROPERTY_KEY][0] === EXPECTED_PROPERTY_VALUE) { - message = `Download is forbidden. The artifact ${data.repoPath.key}/${data.repoPath.path} has the property ${EXPECTED_PROPERTY_KEY} = ${EXPECTED_PROPERTY_VALUE}`; - status = DownloadStatus.DOWNLOAD_STOP; - } else { - message = "Download can proceed"; - status = DownloadStatus.DOWNLOAD_PROCEED; + const properties = artifactData.properties; + for(const forbiddenProperty of forbiddenProperties) { + if(isPropertyPresent(forbiddenProperty, properties)) { + status = DownloadStatus.DOWNLOAD_STOP; + message = `Stopping Download because forbiddenProperty ${forbiddenProperty.key} is present with forbidden values`; + break; + } } - } catch(error) { + } catch (error) { + console.log(`Got error: ${JSON.stringify(error)}`); message = `Download proceed with a warning. Could not check if artifact is forbidden or not.`; status = DownloadStatus.DOWNLOAD_WARN; } @@ -32,5 +35,27 @@ export default async (context: PlatformContext, data: BeforeDownloadRequest): Pr return { status, message, + headers: {} + } + + type ForbiddenProperty = { + key: string, + values: string[] + }; + type ArtifactProperties = { + [key: string]: string[] + } + + function isPropertyPresent(forbiddenProperty: ForbiddenProperty, artifactProperties: ArtifactProperties): boolean { + const artifactPropertyKeys = new Set(Object.keys(artifactProperties)); + if(artifactPropertyKeys.has(forbiddenProperty.key)) { + const artifactPropertyValues = new Set(artifactProperties[forbiddenProperty.key]); + for(const forbiddenValue of forbiddenProperty.values) { + if(artifactPropertyValues.has(forbiddenValue)){ + return true; + } + } + } + return false; } -} +} \ No newline at end of file diff --git a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/README.md b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/README.md index 330aac2..2d00846 100644 --- a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/README.md +++ b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/README.md @@ -1,3 +1,84 @@ -# Block download if xray scan shows more than 2 critical security issues +Restrict Download on Xray Critical Issues +===================================== -This worker will block download if the Artifact has more than 2 critical security issues. If the artifact has not yet been scanned, the download will proceed with a warning. The accepted threshold may be changed by using the MAX_CRITICAL_SEC_ISSUES_ACCEPTED variable. +This worker is triggered by the `BEFORE_DOWNLOAD` event of Artifactory. Its primary purpose is to block artifact downloads if the number of critical security issues in JFrog Xray exceeds a defined threshold. The worker ensures compliance with security policies by preventing downloads of artifacts with excessive vulnerabilities. + +Functionality +------------- +- **Threshold Check:** Blocks download if the number of critical security issues is greater than `MAX_CRITICAL_SEC_ISSUES_ACCEPTED` (default: 2). +- **Xray Availability:** Issues a warning and allows download if JFrog Xray is unavailable. +- **Error Handling:** Provides appropriate messages for unexpected errors or timeouts. +- **Headers:** Optionally modifies response headers if required. + +Worker Logic +------------ +1. **Xray Availability Check**: Before proceeding, the worker pings the Xray service to ensure it is available. If unavailable, the download proceeds with a warning. +2. **Artifact Scan**: Queries Xray for the artifact's security issues using the repository key and artifact name. +3. **Decision Making**: + - Allows the download if the number of critical issues is below the threshold. + - Blocks the download if critical issues exceed the threshold. + - Issues a warning and allows download for errors or unexpected results. + +Payload +------- +The worker operates on the `BEFORE_DOWNLOAD` event payload provided by Artifactory. It leverages metadata such as the artifact's repository path and key to query Xray. + +Configuration +------------- +- **MAX_CRITICAL_SEC_ISSUES_ACCEPTED**: Maximum allowed critical security issues for an artifact. Default is 2. This can be modified in the worker script as per organizational requirements. + +Possible Responses +------------------ + +### Download Proceed +- **Structure:** + ```json + { + "status": "DOWNLOAD_PROCEED", + "message": "Artifact has less than 2 security issues: proceed with the download." + } + ``` +- **Explanation:** + - Indicates the artifact meets security requirements, and the download is allowed. + +### Download Stopped +- **Structure:** + ```json + { + "status": "DOWNLOAD_STOP", + "message": "DOWNLOAD STOPPED : artifact scan shows critical security issues." + } + ``` +- **Explanation:** + - Indicates the artifact has too many critical security issues, and the download is blocked. + +### Warning Response +- **Structure:** + ```json + { + "status": "DOWNLOAD_WARN", + "message": "Error during scan check. Download will proceed with warning." + } + ``` +- **Explanation:** + - Indicates that the worker encountered an error (e.g., Xray unavailability or unexpected results), but the download is allowed with a warning. + +### Headers +- The worker response can optionally include modified headers. The `headers` field in the response is currently empty but can be populated if required. + +Error Handling +-------------- +- **Xray Unavailable:** Allows download with a warning message. +- **Query Failure:** Issues a warning and allows download for unexpected results from Xray. +- **Unexpected Errors:** Logs errors and proceeds with a warning. + +Recommendations +--------------- +1. **Threshold Adjustment**: + - Update `MAX_CRITICAL_SEC_ISSUES_ACCEPTED` as per security requirements. + +2. **Monitoring and Logging**: + - Review logs for `DOWNLOAD_WARN` responses to identify and address recurring issues. + +3. **Testing**: + - Validate the worker functionality in a staging environment before deploying to production. \ No newline at end of file diff --git a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/worker.ts b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/worker.ts index 17f256e..fc99528 100755 --- a/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/worker.ts +++ b/samples/artifactory/BEFORE_DOWNLOAD/restrict-download-on-xray-critical-issues/worker.ts @@ -1,13 +1,19 @@ import { PlatformContext, BeforeDownloadRequest, BeforeDownloadResponse, DownloadStatus } from 'jfrog-workers'; - export default async (context: PlatformContext, data: BeforeDownloadRequest): Promise => { const MAX_CRITICAL_SEC_ISSUES_ACCEPTED = 2; + const isXrayAvailable = await checkIfXrayAvailable(); + if (!isXrayAvailable) { + return { + status: DownloadStatus.DOWNLOAD_WARN, + message: "Could not check for xray scans because xray is not available. Proceeding download with warning.", + headers: {} // This can be populated if response headers are required to be added/overriden. + } + } let status: DownloadStatus = DownloadStatus.DOWNLOAD_UNSPECIFIED; let message = ''; - // Reponse strucure from the XRay endpoint: https://jfrog.com/help/r/jfrog-rest-apis/scans-list-get-artifacts let responseData: { "data": [ { @@ -52,14 +58,31 @@ export default async (context: PlatformContext, data: BeforeDownloadRequest): Pr status = DownloadStatus.DOWNLOAD_WARN; message = 'Request returned unexpected result. Download will proceed with warning.' } - } catch(error) { + } catch (error) { message = "Error during scan check. Download will proceed with warning."; status = DownloadStatus.DOWNLOAD_WARN; - console.error(`Request failed: ${ error.message }`); + console.error(`Request failed: ${error.message}`); } return { status, message, + headers: {} // This can be populated if response headers are required to be added/overriden. + } + + + async function checkIfXrayAvailable(): Promise { + let response; + try { + response = await context.clients.platformHttp.get('/xray/api/v1/system/ping'); + if (response.data.status !== "pong") { + throw new Error("Xray not available"); + } + return true; + } catch (error) { + console.log(`Encountered error ${error.message} while checking for xray readiness. Allowing download with a warning`); + return false; + } + } -} +} \ No newline at end of file diff --git a/samples/artifactory/BEFORE_PROPERTY_CREATE/property-creation-permission/README.md b/samples/artifactory/BEFORE_PROPERTY_CREATE/property-creation-permission/README.md index 5337fae..77d210d 100644 --- a/samples/artifactory/BEFORE_PROPERTY_CREATE/property-creation-permission/README.md +++ b/samples/artifactory/BEFORE_PROPERTY_CREATE/property-creation-permission/README.md @@ -1,3 +1,65 @@ -# Before Property Create +# Admin-Only Property Creation -In this use case we want to grant to only admins the permission to create properties on artifacts. +## Overview +This worker is triggered by the `BEFORE_PROPERTY_CREATE` event in JFrog Artifactory. Its primary purpose is to enforce a policy where only users with admin privileges can create artifact properties. + + +## Functionality + +- **Admin Check:** Ensures that only users with admin privileges can create artifact properties. +- **Access Restriction:** Non-admin users attempting to create properties will have their request denied. +- **Event Interception:** Intercepts the `BEFORE_PROPERTY_CREATE` event to implement this logic. + +## Key Features + +1. **Permission Validation:** + - Checks the user context to determine if the user is an admin. + - Grants or denies property creation based on user privileges. + +2. **Custom Responses:** + - Returns appropriate messages for both allowed and denied actions. + - Prevents unauthorized users from modifying artifact properties. + +## Worker Logic + +### Admin Validation +The worker determines if the user is an admin by checking their user ID. It assumes that admin user IDs end with `/users/admin`. + +### Decision Making +- **Admin Users:** + - Permission is granted for property creation. + - Responds with `BEFORE_PROPERTY_CREATE_PROCEED`. + +- **Non-Admin Users:** + - Permission is denied for property creation. + - Responds with `BEFORE_PROPERTY_CREATE_STOP`. + +### Responses +The worker sends back one of the following responses: + +#### Property Creation Allowed +```json +{ + "message": "Permission granted to admin", + "status": "BEFORE_PROPERTY_CREATE_PROCEED" +} +``` + +#### Property Creation Not Allowed +``` json +{ + "message": "Only admins are allowed to create properties", + "status": "BEFORE_PROPERTY_CREATE_STOP" +} +``` + +## Recommendations + +1. **User Context Validation:** Ensure that admin user IDs are configured to end with /users/admin. +2. **Auditing:** Periodically review logs for denied requests to ensure policy compliance. +3. **Testing:** Test the worker in a staging environment before deploying it in production. + +## Error Scenarios + +- **Invalid User IDs:** If the user ID format does not conform to the expected convention, the worker will deny property creation. +- **Unexpected Failures:** The worker logs any errors encountered during execution for further debugging. diff --git a/samples/artifactory/BEFORE_UPLOAD/allow-upload-by-pattern/worker.ts b/samples/artifactory/BEFORE_UPLOAD/allow-upload-by-pattern/worker.ts index f2d47c0..26b8aca 100755 --- a/samples/artifactory/BEFORE_UPLOAD/allow-upload-by-pattern/worker.ts +++ b/samples/artifactory/BEFORE_UPLOAD/allow-upload-by-pattern/worker.ts @@ -1,6 +1,5 @@ import { PlatformContext, BeforeUploadRequest, BeforeUploadResponse, UploadStatus } from 'jfrog-workers'; - export default async (context: PlatformContext, data: BeforeUploadRequest): Promise => { // This RegExp will match all repopaths that start with 'org/company/' and end with the extension .jar OR .war // For instance those paths will match the regex : diff --git a/samples/artifactory/BEFORE_UPLOAD/repo-quota/worker.ts b/samples/artifactory/BEFORE_UPLOAD/repo-quota/worker.ts index 125d161..a2addc0 100755 --- a/samples/artifactory/BEFORE_UPLOAD/repo-quota/worker.ts +++ b/samples/artifactory/BEFORE_UPLOAD/repo-quota/worker.ts @@ -1,6 +1,5 @@ import { PlatformContext, BeforeUploadRequest, BeforeUploadResponse, UploadStatus } from 'jfrog-workers'; - const REPO_QUOTA_PROPERTY = "repository.path.quota"; export default async function (context: PlatformContext, data: BeforeUploadRequest): Promise> { diff --git a/samples/artifactory/BEFORE_UPLOAD/restrict-overwrite/worker.ts b/samples/artifactory/BEFORE_UPLOAD/restrict-overwrite/worker.ts index c704687..18b4a95 100755 --- a/samples/artifactory/BEFORE_UPLOAD/restrict-overwrite/worker.ts +++ b/samples/artifactory/BEFORE_UPLOAD/restrict-overwrite/worker.ts @@ -1,6 +1,5 @@ import { PlatformContext, BeforeUploadRequest, BeforeUploadResponse, UploadStatus } from 'jfrog-workers'; - export default async function (context: PlatformContext, data: BeforeUploadRequest): Promise> { try { return restrictOverwrite(context, data); diff --git a/samples/artifactory/GENERIC_EVENT/README.md b/samples/artifactory/GENERIC_EVENT/README.md new file mode 100644 index 0000000..1ff0cbd --- /dev/null +++ b/samples/artifactory/GENERIC_EVENT/README.md @@ -0,0 +1,51 @@ +# Generic Event Workers + +## Overview + +Generic Event Workers are specialized JFrog workers that are not tied to specific Artifactory events. Instead, they are executed on demand via API calls or manual invocation. These workers are highly customizable, allowing you to perform tasks that are not bound to predefined triggers. + +## Key Features + +- **Manual Execution:** Can be triggered manually or through a specific API call. +- **Custom Logic:** Supports flexible implementation for a variety of use cases. +- **Independent Operation:** Operates independently of Artifactory's standard event-driven workflow. + +## Use Cases + +1. **Custom Maintenance Tasks:** Perform one-off operations such as cleaning up repositories or updating artifact metadata. +2. **Scheduled Jobs:** Execute pre-scheduled tasks using external job schedulers. +3. **Custom API Integration:** Enable workflows that require external API calls or integrations. + +## Example Worker Logic + +Here’s a simple example of a Generic Event Worker: + +```typescript +import { PlatformContext, GenericEventRequest, GenericEventResponse } from 'jfrog-workers'; + +export default async (context: PlatformContext, data: GenericEventRequest): Promise => { + try { + console.log("Worker triggered manually with data:", data); + return { + message: "Worker executed successfully", + status: "SUCCESS" + }; + } catch (error) { + console.error("Worker execution failed:", error.message); + return { + message: "Worker execution failed", + status: "FAILURE" + }; + } +}; +``` + +## Recommendations + +- **Authentication:** Ensure only authorized users or systems can trigger the worker. +- **Input Validation:** Validate the input payload to prevent errors or misuse. +- **Logging and Monitoring:** Log all executions for auditing and debugging purposes. + +## License + +This worker script and documentation are provided under the MIT License. diff --git a/samples/artifactory/GENERIC_EVENT/remote-backup/README.md b/samples/artifactory/GENERIC_EVENT/remote-backup/README.md index 3bd28b4..f2b074d 100644 --- a/samples/artifactory/GENERIC_EVENT/remote-backup/README.md +++ b/samples/artifactory/GENERIC_EVENT/remote-backup/README.md @@ -1,24 +1,16 @@ -Artifactory Remote Backup User Plugin +Artifactory Remote Backup ===================================== -This worker copies files from a remote cache to a local 'backup' -repository. This ensures that cached artifacts are still available, even after -they're removed from the cache. This worker can also be used to copy from a -local repository to a different local repository. +This worker copies files from a remote cache to a local 'backup' repository. This ensures that cached artifacts are still available, even after they're removed from the cache. This worker can also be used to copy from a local repository to a different local repository. -Note that this workers will not copy properties on folders, including Docker -image folders. Properties on artifacts are copied as expected. +Note that this worker will not copy properties on folders, including Docker image folders. Properties on artifacts are copied as expected. -Targets repositories should exist or the copies will fail. +Target repositories should exist, or the copies will fail. Payload ------- -The worker expects a JSON object of repository pairs. For example, if you'd like to -backup `repo-foo-remote` to `repo-foo-backup-local`, and also backup -`repo-bar-remote` to `repo-bar-backup-local`. -You can also specify `maxDepth` the maximum path depth to copy, as well as `maxFiles` the max number of item to copy. -Your configuration would be: +The worker expects a JSON object of repository pairs. For example, if you'd like to backup `repo-foo-remote` to `repo-foo-backup-local`, and also backup `repo-bar-remote` to `repo-bar-backup-local`, you can specify `maxDepth` (the maximum path depth to copy) and `maxFiles` (the max number of items to copy). Your configuration would be: ```json { @@ -32,29 +24,133 @@ Your configuration would be: } ``` - Usage ----- -The worker can be executed using as a Generic event. +The worker can be executed using a Generic event. Execute with the payload from standard input: ```shell -jf worker exec my worker - < to : Permission denied` +- **Common Causes:** + - Missing repository. + - Insufficient permissions. + - Timeout due to exceeding execution time. + +### Invalid Payload Response +- **Structure:** + ```json + { + "error": "Invalid payload structure" + } + ``` +- **Explanation:** + - This error occurs when the payload JSON is missing required fields like `backups` or contains incorrect data types for fields (e.g., `backups` is not a dictionary). + +### Dry Run Response +- **Structure:** + ```json + { + "complete": 0, + "total": 10 + } + ``` +- **Explanation:** + - Indicates that no files were copied because the worker was executed with the `dryRun` flag set to `true`. +- Use this response to verify which files **would** be backed up without actually performing the backup. + +### Repository Path Error +- **Structure:** + ```json + { + "error": "Invalid repository path: repo-foo-remote-cache" + } + ``` +- **Explanation:** + - This error occurs if the repository path is incorrectly specified in the payload. + +Recommendations for Handling Responses +-------------------------------------- + +1. **Success Response** + - Monitor the `complete` and `total` fields to ensure all files were backed up. + - If `complete < total`, investigate potential errors (e.g., timeouts or skipped files). + +2. **Error Response** + - Check the `error` message for specific guidance. + - Review repository paths, payload structure, and permissions. + +3. **Timeout** + - For large repositories, schedule multiple executions of the worker using a cron job. + - Use strategies like skipping already processed artifacts to optimize subsequent runs. + +4. **Dry Run** + - Use the `dryRun` mode to identify issues before performing actual backups. + diff --git a/samples/artifactory/GENERIC_EVENT/remote-backup/worker.ts b/samples/artifactory/GENERIC_EVENT/remote-backup/worker.ts index 5d05c41..4fafdc9 100755 --- a/samples/artifactory/GENERIC_EVENT/remote-backup/worker.ts +++ b/samples/artifactory/GENERIC_EVENT/remote-backup/worker.ts @@ -1,6 +1,5 @@ import { PlatformContext } from 'jfrog-workers'; - export default async function(context: PlatformContext, data: RemoteBackupPayload) { try { const [complete, total] = await runBackup(context, data.backups, data.dryRun, data.checksums, data.maxDepth ?? 10, data.maxFiles ?? 1000); diff --git a/samples/artifactory/README.md b/samples/artifactory/README.md new file mode 100644 index 0000000..2f9cdf3 --- /dev/null +++ b/samples/artifactory/README.md @@ -0,0 +1,86 @@ +# Artifactory Event-Driven Workers + +Artifactory workers allow you to automate tasks, enforce policies, and integrate with external systems based on specific events such as uploading, downloading, or managing artifacts and properties. + +### Available Workers + +This repository includes sample workers for the following Artifactory events: + +- **Before Download** + - **Triggers**: Before an artifact is downloaded. + - **Use Cases**: Enforce download policies, log access. + +- **After Download** + - **Triggers**: After an artifact is downloaded. + - **Use Cases**: Audit and log download activities. + +- **Before Upload** + - **Triggers**: Before an artifact is uploaded. + - **Use Cases**: Validate artifacts, enforce naming conventions. +- **After Create** + - **Triggers**: After an artifact or folder is created. + - **Use Cases**: Automate post-creation tasks like indexing. + +- **Before Property Create** + - **Triggers**: Before a property is added to an artifact. + - **Use Cases**: Enforce property-related policies. + + +### Generic Events + +In addition to Artifactory-specific events, workers can respond to **Generic Events**. These are custom-defined events that don't directly map to Artifactory actions, offering flexibility to trigger and manage custom workflows or integrations within your JFrog Platform. + +#### Use Cases for Generic Events: +- **Custom Automation**: Integrate with third-party systems or automate unique workflows. +- **Observability**: Log or monitor specific behaviors in your application. +- **Multi-Step Processes**: Orchestrate tasks across Artifactory or other parts of the JFrog Platform. + +Each worker includes TypeScript code and detailed instructions for deployment and configuration. + +--- + +## Getting Started + +To use or customize the sample workers, follow these steps: + +### 1. Clone the Repository +``` +git clone https://github.com/jfrog/workers-sample.git +``` + +### 2. Navigate to the Artifactory Samples Directory +``` +cd workers-sample/samples/artifactory +``` + +### 3. Install Dependencies +Go to the specific worker directory and install required dependencies: +``` +cd specific-worker-directory +npm install +``` + +### 4. Modify the Worker Logic +> **Note** +> +> **Use Localized Types**: If creating new types, define them in the same file to avoid dependency issues. + +Update the worker code as needed. + + +### 5. Test the Worker +Trigger the relevant event (for example, upload an artifact for a **Before Upload** worker) to ensure it works correctly. + +### 6. Deploy the Worker +> **Note** +> +> **Remove All Imports**: Before deployment, remove all imports from the script to prevent compilation errors. + +Deploy the worker using the JFrog CLI or the Workers UI. + + +### 7. Validate the Deployment +- Ensure the worker is associated with the correct event. +- Test in a staging environment before deploying to production. + +--- \ No newline at end of file