Skip to content

Commit 922c5e4

Browse files
Merge pull request #13 from shashank-jfrog/feature/RTDEV-52485
Enhanced worker samples
2 parents f1942a3 + 629316b commit 922c5e4

File tree

17 files changed

+804
-69
lines changed

17 files changed

+804
-69
lines changed
Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,72 @@
1-
# Log download activity to an external endpoint
1+
# Log Download activity to an external endpoint
22

3-
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.
3+
## Overview
4+
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.
5+
6+
## Functionality
7+
8+
1. **Trigger:** Activated after a download event occurs in Artifactory.
9+
2. **Notification:** Sends a log containing details about the downloaded artifact to a specified external endpoint.
10+
3. **Authentication:** Uses a bearer token stored in a secret to authenticate with the external endpoint.
11+
4. **Error Handling:** Logs and updates the response message if the notification fails.
12+
13+
## Configuration
14+
15+
To customize the worker, you need to update two constants in the source code:
16+
17+
- `URL`: The endpoint where the download log will be sent.
18+
Example:
19+
```typescript
20+
const URL = 'https://<external_endpoint>';
21+
```
22+
23+
- `SECRET_NAME`: The name of the secret that contains the bearer token for authentication.
24+
Example:
25+
```typescript
26+
const SECRET_NAME = 'myBearerToken';
27+
```
28+
29+
## How It Works
30+
31+
1. **Download Event:** The worker is triggered when an artifact is downloaded.
32+
2. **Log Creation:** The worker creates a log message with the following details:
33+
- Artifact path
34+
- Repository key
35+
- User ID or token used for the download
36+
3. **Notification:** Sends the log message as a POST request to the external endpoint.
37+
4. **Response Handling:**
38+
- Logs success if the notification is successfully sent.
39+
- Logs a warning or error if the notification fails and updates the response message accordingly.
40+
41+
## Example Log Message
42+
43+
```plaintext
44+
The artifact '<artifact_path>' has been downloaded by the <userid/token>: <user_id> from the repository '<repo_key>'.
45+
```
46+
47+
## Response Messages
48+
49+
- **Successful Notification:**
50+
- Message: `Download activity successfully logged`
51+
52+
- **Failed Notification:**
53+
- Message: `Failed to log download activity`
54+
55+
## Dependencies
56+
57+
This worker relies on the following JFrog Workers APIs:
58+
- `PlatformContext` for accessing platform resources (e.g., secrets, HTTP clients).
59+
- `AfterDownloadRequest` and `AfterDownloadResponse` for handling download event payloads and responses.
60+
61+
## Recommendations
62+
63+
1. **Endpoint Configuration:** Ensure the external endpoint is correctly set and accessible.
64+
2. **Bearer Token Secret:** Store the bearer token securely as a secret in the JFrog platform.
65+
3. **Monitoring:** Regularly monitor logs for failed notifications to identify issues.
66+
4. **Testing:** Test the worker in a staging environment before deploying it in production.
67+
68+
## Error Scenarios
69+
70+
- **Network Errors:** Occur if the external endpoint is unreachable.
71+
- **Authentication Failures:** Occur if the bearer token is invalid or missing.
72+
- **Invalid URL:** Occurs if the provided endpoint URL is incorrect.

samples/artifactory/AFTER_DOWNLOAD/report-download-to-external-endpoint/worker.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { PlatformContext, AfterDownloadRequest, AfterDownloadResponse } from 'jfrog-workers';
22

3-
43
export default async (context: PlatformContext, data: AfterDownloadRequest): Promise<AfterDownloadResponse> => {
54
// MODIFY THOSE TWO CONSTANTS TO FIT YOUR NEEDS
65
const URL = 'https://<external_endpoint>';
Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,71 @@
1-
# Block download if last Xray scan is older than 30 seconds
1+
# Worker for Restricting Downloads Based on Xray Scan Recency
22

3-
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.
3+
## Overview
44

5+
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.
6+
7+
## Functionality
8+
9+
### Key Features
10+
- **Xray Scan Check:** Verifies the most recent Xray scan date of an artifact.
11+
- **Threshold Enforcement:** Restricts download if the last scan exceeds the specified threshold days or date.
12+
- **Error Handling:** Issues a warning and allows download if Xray is unavailable or errors occur during the check.
13+
14+
### Worker Logic
15+
1. **Xray Availability Check:** Verifies if Xray is operational by pinging the Xray system.
16+
2. **Artifact Scan Details Fetching:** Retrieves artifact scan details, including the last scan date, using the artifact name and repository key.
17+
3. **Decision Making:**
18+
- Stops the download if the last scan is older than the allowed threshold.
19+
- Allows download with a warning if scan details cannot be retrieved or Xray is unavailable.
20+
- Proceeds with the download if the scan is recent.
21+
22+
## Configuration
23+
24+
### Threshold Configuration
25+
- **Threshold Days:** Defines the maximum allowed age (in days) of the last Xray scan. Default: 2 days.
26+
- **Threshold Date:** Defines the latest allowable date for the last scan. Default: 7 days prior to the current date.
27+
28+
## Response Types
29+
30+
### Download Proceed
31+
```json
32+
{
33+
"status": "DOWNLOAD_PROCEED",
34+
"message": "Proceeding with download"
35+
}
36+
```
37+
- **Explanation:** Indicates the artifact complies with the scan recency requirements.
38+
39+
### Download Stopped
40+
```json
41+
{
42+
"status": "DOWNLOAD_STOP",
43+
"message": "Stopping Download, because last scan date is older than allowed threshold duration of <threshold_days> days."
44+
}
45+
```
46+
- **Explanation:** Indicates the artifact's last scan is outdated, and the download is blocked.
47+
48+
### Warning Response
49+
```json
50+
{
51+
"status": "DOWNLOAD_WARN",
52+
"message": "Could not check for Xray scans because Xray is not available. Proceeding download with warning."
53+
}
54+
```
55+
- **Explanation:** Indicates that the worker encountered an error (e.g., Xray unavailability), but the download is allowed with a warning.
56+
57+
## Error Handling
58+
59+
- **Xray Unavailability:** Issues a warning and allows download if Xray is not operational.
60+
- **Scan Check Failure:** Logs the error and proceeds with a warning.
61+
62+
## Recommendations
63+
64+
1. **Threshold Adjustment:**
65+
- Update the threshold days or date as per organizational requirements.
66+
67+
2. **Monitoring and Logging:**
68+
- Regularly review logs for `DOWNLOAD_WARN` responses to address recurring issues.
69+
70+
3. **Testing:**
71+
- Validate the worker functionality in a staging environment before deploying to production.
Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,124 @@
11
import { PlatformContext, BeforeDownloadRequest, BeforeDownloadResponse, DownloadStatus } from 'jfrog-workers';
22

33
export default async (context: PlatformContext, data: BeforeDownloadRequest): Promise<BeforeDownloadResponse> => {
4+
const thresholdDate = new Date(new Date().getUTCDate() - 7);
5+
const thresholdDays = 2;
6+
const isXrayAvailable = await checkIfXrayAvailable();
7+
if (!isXrayAvailable) {
8+
return {
9+
status: DownloadStatus.DOWNLOAD_WARN,
10+
message: "Could not check for xray scans because xray is not available. Proceeding download with warning.",
11+
headers: {} // This can be populated if response headers are required to be added/overriden.
12+
}
13+
}
414

515
let status: DownloadStatus = DownloadStatus.DOWNLOAD_UNSPECIFIED;
16+
let message = '';
17+
18+
let responseData: {
19+
"data": [
20+
{
21+
"name": string,
22+
"repo_path": string,
23+
"package_id": string,
24+
"version": string,
25+
"sec_issues": {
26+
"critical": number,
27+
"high": number,
28+
"low": number,
29+
"medium": number,
30+
"total": number
31+
},
32+
"scans_status": {
33+
"overall": {
34+
"status": string,
35+
"time": string
36+
}
37+
}
38+
"size": string,
39+
"violations": number,
40+
"created": string,
41+
"deployed_by": string,
42+
"repo_full_path": string
43+
}
44+
],
45+
"offset": number
46+
} = null;
647

748
try {
8-
// The in-browser HTTP client facilitates making calls to the JFrog REST APIs
9-
//To call an external endpoint, use 'await context.clients.axios.get("https://foo.com")'
10-
const res = await context.clients.platformHttp.get('/artifactory/api/v1/system/readiness');
49+
const artifactName = data.metadata.repoPath.path.substr(data.metadata.repoPath.path.lastIndexOf('/') + 1);
50+
const repoKey = data.metadata.repoPath.key;
51+
52+
const res = await context.clients.platformHttp.get(`/xray/api/v1/artifacts?repo=${repoKey}&search=${artifactName}&num_of_rows=1`);
53+
responseData = res.data;
1154

12-
// You should reach this part if the HTTP request status is successful (HTTP Status 399 or lower)
1355
if (res.status === 200) {
14-
status = DownloadStatus.DOWNLOAD_PROCEED;
15-
console.log("Artifactory ping success");
56+
// console.log(responseData);
57+
console.log(responseData.data);
58+
const lastScanDetails = responseData.data[0].scans_status.overall;
59+
if (lastScanDetails.status !== "DONE") {
60+
return {
61+
status: DownloadStatus.DOWNLOAD_WARN,
62+
message: "Scan is on-going, proceeding with download with a warning.",
63+
headers: {} // This can be populated if response headers are required to be added/overriden.
64+
}
65+
} else {
66+
const lastScanDate = new Date(lastScanDetails.time);
67+
const currentDate = new Date();
68+
console.log(differenceInDays(lastScanDate, currentDate));
69+
if (thresholdDays < differenceInDays(lastScanDate, currentDate)) {
70+
return {
71+
status: DownloadStatus.DOWNLOAD_STOP,
72+
message: `Stopping Download, because last scan date is older than allowed threshold duration of ${thresholdDays} days.`,
73+
headers: {}
74+
}
75+
} else if(lastScanDate<thresholdDate) {
76+
return {
77+
status: DownloadStatus.DOWNLOAD_STOP,
78+
message: `Stopping Download, because last scan date is older than allowed threshold last date of ${thresholdDate.toUTCString()}.`,
79+
headers: {}
80+
}
81+
}
82+
return {
83+
status: DownloadStatus.DOWNLOAD_PROCEED,
84+
message: `Proceeding with download`,
85+
headers: {}
86+
}
87+
}
1688
} else {
1789
status = DownloadStatus.DOWNLOAD_WARN;
18-
console.warn(`Request is successful but returned status other than 200. Status code : ${ res.status }`);
90+
message = 'Request returned unexpected result. Download will proceed with warning.'
1991
}
20-
} catch(error) {
21-
// The platformHttp client throws PlatformHttpClientError if the HTTP request status is 400 or higher
22-
status = DownloadStatus.DOWNLOAD_STOP;
23-
console.error(`Request failed with status code ${ error.status || '<none>' } caused by : ${ error.message }`)
92+
} catch (error) {
93+
message = `Encountered error: ${error.message} during scan check. Download will proceed with warning.`;
94+
status = DownloadStatus.DOWNLOAD_WARN;
95+
console.error(`Request failed: ${error.message}`);
2496
}
2597

2698
return {
2799
status,
28-
message: 'Overwritten by worker-service if an error occurs.',
100+
message,
101+
headers: {} // This can be populated if response headers are required to be added/overriden.
102+
}
103+
104+
105+
async function checkIfXrayAvailable(): Promise<boolean> {
106+
let response;
107+
try {
108+
response = await context.clients.platformHttp.get('/xray/api/v1/system/ping');
109+
if (response.data.status !== "pong") {
110+
throw new Error("Xray not available");
111+
}
112+
return true;
113+
} catch (error) {
114+
console.log(`Encountered error ${error.message} while checking for xray readiness. Allowing download with a warning`);
115+
return false;
116+
}
117+
118+
}
119+
120+
function differenceInDays(lastScanDate: Date, currentDate: Date): number {
121+
const differenceInTime = currentDate.getTime() - lastScanDate.getTime();
122+
return Math.round(differenceInTime / (1000 * 3600 * 24));
29123
}
30-
}
124+
}
Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,88 @@
1-
# Restrict Download By Property Value
1+
Restrict Download by Property Value
2+
=====================================
23

3-
This worker will block the download on artifacts on which the property EXPECTED_PROPERTY_KEY is set with the value EXPECTED_PROPERTY_VALUE.
4+
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.
45

6+
Functionality
7+
-------------
8+
- **Forbidden Properties Check:** Blocks download if an artifact contains a forbidden property with restricted values.
9+
- **Error Handling:** Issues a warning and allows download if the properties cannot be verified due to an error.
10+
- **Headers:** Optionally modifies response headers if required.
11+
12+
Worker Logic
13+
------------
14+
1. **Property Fetching**: Queries Artifactory for artifact properties using the repository key and artifact path.
15+
2. **Decision Making**:
16+
- Blocks the download if a forbidden property with restricted values is present.
17+
- Allows the download if no forbidden properties are found.
18+
- Issues a warning and allows download for errors or unexpected results.
19+
20+
Payload
21+
-------
22+
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.
23+
24+
Configuration
25+
-------------
26+
- **Forbidden Properties**: Define the list of forbidden properties and their restricted values in the worker script. Example:
27+
```typescript
28+
let forbiddenProperties: ForbiddenProperty[] = [];
29+
forbiddenProperties.push({
30+
key: "FORBIDDEN",
31+
values: ["true"]
32+
});
33+
```
34+
35+
Possible Responses
36+
------------------
37+
38+
### Download Proceed
39+
- **Structure:**
40+
```json
41+
{
42+
"status": "DOWNLOAD_PROCEED",
43+
"message": "Allowing Download"
44+
}
45+
```
46+
- **Explanation:**
47+
- Indicates the artifact does not contain any forbidden properties, and the download is allowed.
48+
49+
### Download Stopped
50+
- **Structure:**
51+
```json
52+
{
53+
"status": "DOWNLOAD_STOP",
54+
"message": "Stopping Download because forbiddenProperty <key> is present with forbidden values"
55+
}
56+
```
57+
- **Explanation:**
58+
- Indicates the artifact contains a forbidden property, and the download is blocked.
59+
60+
### Warning Response
61+
- **Structure:**
62+
```json
63+
{
64+
"status": "DOWNLOAD_WARN",
65+
"message": "Download proceed with a warning. Could not check if artifact is forbidden or not."
66+
}
67+
```
68+
- **Explanation:**
69+
- Indicates that the worker encountered an error (e.g., API failure), but the download is allowed with a warning.
70+
71+
### Headers
72+
- The worker response can optionally include modified headers. The `headers` field in the response is currently empty but can be populated if required.
73+
74+
Error Handling
75+
--------------
76+
- **Property Fetch Failure:** Allows download with a warning message.
77+
- **Unexpected Errors:** Logs errors and proceeds with a warning.
78+
79+
Recommendations
80+
---------------
81+
1. **Forbidden Properties Definition**:
82+
- Update the list of forbidden properties and their restricted values as per organizational policies.
83+
84+
2. **Monitoring and Logging**:
85+
- Review logs for `DOWNLOAD_WARN` responses to identify and address recurring issues.
86+
87+
3. **Testing**:
88+
- Validate the worker functionality in a staging environment before deploying to production.

0 commit comments

Comments
 (0)