├── attest
├── README.md
├── dist
│ └── package.json
├── package.json
├── action.yml
└── index.js
├── .gitignore
├── dist
├── main
│ └── package.json
└── post
│ └── package.json
├── pics
├── cimon-cover.png
├── detect-report.png
└── prevent-report.png
├── .prettierrc
├── package.json
├── action.yml
├── src
├── post
│ └── index.js
└── main
│ └── index.js
├── .github
└── workflows
│ └── verify-pr.yaml
├── README.md
├── LICENSE.md
└── assets
└── images
├── Cygives-lightmode.svg
└── Cygives-darkmode.svg
/attest/README.md:
--------------------------------------------------------------------------------
1 | # cimon-attest
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
3 | .idea/
4 |
--------------------------------------------------------------------------------
/dist/main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/dist/post/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/attest/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/pics/cimon-cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CycodeLabs/cimon-action/HEAD/pics/cimon-cover.png
--------------------------------------------------------------------------------
/pics/detect-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CycodeLabs/cimon-action/HEAD/pics/detect-report.png
--------------------------------------------------------------------------------
/pics/prevent-report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CycodeLabs/cimon-action/HEAD/pics/prevent-report.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "singleQuote": false,
4 | "useTabs": false,
5 | "overrides": [
6 | {
7 | "files": ["*.yml", "*.yaml"],
8 | "options": {
9 | "tabWidth": 2,
10 | "singleQuote": true
11 | }
12 | },
13 | {
14 | "files": ["*.ts", "*.js"],
15 | "options": {
16 | "tabWidth": 4,
17 | "singleQuote": true
18 | }
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/attest/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cimon-attest",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "all": "npm run clean && npm run dist/index.js",
8 | "dist/index.js": "ncc build --out dist/ index.js",
9 | "clean": "rm -rf dist"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "type": "module",
15 | "dependencies": {
16 | "@actions/artifact": "^2.2.1",
17 | "@actions/core": "^1.11.1",
18 | "@actions/exec": "^1.1.1",
19 | "@vercel/ncc": "^0.34.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agent-javascript-action",
3 | "version": "0.0.1",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "all": "npm run clean && npm run dist/main/index.js && npm run dist/post/index.js",
8 | "dist/main/index.js": "ncc build --out dist/main src/main/index.js",
9 | "dist/post/index.js": "ncc build --out dist/post src/post/index.js",
10 | "clean": "rm -rf dist"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "type": "module",
16 | "devDependencies": {
17 | "@actions/core": "^1.11.1",
18 | "@actions/exec": "^1.1.1",
19 | "@vercel/ncc": "^0.34.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: Cimon by Cycode
2 | description: Runtime Security Solution for your CI/CD Pipeline
3 | branding:
4 | icon: shield
5 | color: green
6 | author: CycodeLabs
7 | inputs:
8 | client-id:
9 | description: Cimon client ID for authentication
10 | required: false
11 | secret:
12 | description: Cimon secret for authentication
13 | required: false
14 | url:
15 | description: Cimon endpoint for authentication
16 | required: false
17 | prevent:
18 | description: Enable prevention mode
19 | required: true
20 | default: 'false'
21 | allowed-ips:
22 | description: A comma-separated list of allowed IP addresses
23 | required: false
24 | allowed-hosts:
25 | description: A comma-separated list of domain names
26 | required: false
27 | file-integrity:
28 | description: Enable file integrity module in prevent mode
29 | required: false
30 | default: 'false'
31 | memory-protection:
32 | description: Enable memory protection module in prevent mode
33 | required: false
34 | default: 'false'
35 | ignored-ip-nets:
36 | description: |
37 | A comma or white space separated list of ignored IP networks in CIDR
38 | notation, e.g. 10.0.0.0/8, 172.16.0.0/12. This setting is mandatory if
39 | your workflow runs containers attached to a custom network with
40 | configured sub-range. In other words, inter-container networking is
41 | usually ignored by Cimon. Cimon implicitly ignores 10.0.0.0/8 and
42 | 172.16.0.0/12 networks.
43 | required: false
44 | github-token:
45 | description: GitHub token for enriching build information
46 | required: false
47 | default: ${{ github.token }}
48 | report-job-summary:
49 | description: Report results through job summary output
50 | required: false
51 | default: 'true'
52 | report-pr-summary:
53 | description: Report results through PR comment
54 | required: false
55 | default: 'false'
56 | report-process-tree:
57 | description: Enable to report the process tree
58 | required: false
59 | default: 'false'
60 | slack-webhook-endpoint:
61 | description: Slack webhook endpoint to report security events
62 | required: false
63 | apply-fs-events:
64 | description: Enable processing filesystem events and display them in the process tree report
65 | required: false
66 | default: 'false'
67 | log-level:
68 | description: Log level (Used for debugging)
69 | required: false
70 | default: 'info'
71 | feature-gates:
72 | description: Set of key=value pairs that describe Cimon features.
73 | required: false
74 | fail-on-error:
75 | description: Fail the CI if Cimon encountered an error
76 | required: false
77 | default: 'false'
78 | release-path:
79 | description: Path to Cimon release file (Used for debugging)
80 | required: false
81 | default: ''
82 |
83 | runs:
84 | using: node20
85 | main: 'dist/main/index.js'
86 | post: 'dist/post/index.js'
87 |
--------------------------------------------------------------------------------
/src/post/index.js:
--------------------------------------------------------------------------------
1 | import core from '@actions/core';
2 | import exec from '@actions/exec';
3 | import fs from 'fs';
4 | import * as http from '@actions/http-client';
5 |
6 | const CIMON_SCRIPT_DOWNLOAD_URL =
7 | 'https://cimon-releases.s3.amazonaws.com/install.sh';
8 | const CIMON_SCRIPT_PATH = '/tmp/install.sh';
9 | const CIMON_EXECUTABLE_DIR = '/tmp/cimon';
10 | const CIMON_EXECUTABLE_PATH = '/tmp/cimon/cimon';
11 |
12 | const httpClient = new http.HttpClient('cimon-action');
13 |
14 | async function downloadToFile(url, filePath) {
15 | const response = await httpClient.get(url);
16 | const responseBody = await response.readBody();
17 | fs.writeFileSync(filePath, responseBody);
18 | }
19 |
20 | function getActionConfig() {
21 | return {
22 | cimon: {
23 | logLevel: core.getInput('log-level'),
24 | },
25 | };
26 | }
27 |
28 | async function sudoExists() {
29 | try {
30 | const retval = await exec.exec('sudo', ['-v'], {
31 | silent: true,
32 | });
33 | return retval === 0;
34 | } catch (error) {
35 | return false;
36 | }
37 | }
38 |
39 | async function run(config) {
40 | if (!fs.existsSync(CIMON_SCRIPT_PATH)) {
41 | await downloadToFile(CIMON_SCRIPT_DOWNLOAD_URL, CIMON_SCRIPT_PATH);
42 | }
43 |
44 | if (!fs.existsSync(CIMON_EXECUTABLE_DIR)) {
45 | let params = [CIMON_SCRIPT_PATH, '-b', CIMON_EXECUTABLE_DIR];
46 | if (
47 | config.cimon.logLevel == 'debug' ||
48 | config.cimon.logLevel == 'trace'
49 | ) {
50 | params.push('-d');
51 | }
52 | let retval = await exec.exec('sh', params);
53 | if (retval !== 0) {
54 | throw new Error(`Failed installing Cimon: ${retval}`);
55 | }
56 | }
57 |
58 | const env = {
59 | ...process.env,
60 | CIMON_LOG_LEVEL: config.cimon.logLevel,
61 | };
62 |
63 | var retval;
64 | const sudo = await sudoExists();
65 |
66 | if (sudo) {
67 | retval = await exec.exec(
68 | 'sudo',
69 | ['-E', CIMON_EXECUTABLE_PATH, 'agent', 'stop'],
70 | {
71 | env,
72 | silent: false,
73 | }
74 | );
75 | } else {
76 | retval = await exec.exec(CIMON_EXECUTABLE_PATH, ['agent', 'stop'], {
77 | env,
78 | silent: false,
79 | });
80 | }
81 |
82 | if (retval !== 0) {
83 | throw new Error(`Failed stopping Cimon process: ${retval}`);
84 | }
85 | }
86 |
87 | try {
88 | await run(getActionConfig());
89 | } catch (error) {
90 | const failOnError = core.getBooleanInput('fail-on-error');
91 | const reportJobSummary = core.getBooleanInput('report-job-summary');
92 | const log = error.message;
93 | if (failOnError) {
94 | core.setFailed(log);
95 | } else if (reportJobSummary) {
96 | await core.summary
97 | .addHeading('Cimon Security Report - Failure')
98 | .addRaw(
99 | 'Cimon encountered an error and was shut down due to the "fail-on-error=false" flag. Details of the error are below:'
100 | )
101 | .addCodeBlock(log)
102 | .write();
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/attest/action.yml:
--------------------------------------------------------------------------------
1 | name: Cimon by Cycode
2 | description: Runtime Security Solution for your CI/CD Pipeline
3 | branding:
4 | icon: shield
5 | color: green
6 | author: CycodeLabs
7 | inputs:
8 | client-id:
9 | description: Cimon client ID for authentication
10 | required: false
11 | secret:
12 | description: Cimon secret for authentication
13 | required: false
14 | url:
15 | description: Cimon endpoint for authentication
16 | required: false
17 | release-path:
18 | description: Path to Cimon release file (Used for debugging)
19 | required: false
20 | default: ''
21 | subjects:
22 | description: A white space seperated list of paths, or base64-encoded subjects. Each path can be file, directory or image reference
23 | required: false
24 | default: ''
25 | image-ref:
26 | description: (deprecated) The container reference to generate provenance for. Either subjects or imageRef are required
27 | required: false
28 | default: ''
29 | sign-key:
30 | description: Input path to a private ECDSA/RSA/ED25519 key used to sign provenance statement
31 | required: false
32 | default: ''
33 | keyless:
34 | description: Allow keyless signature if no signing key is provided
35 | required: false
36 | default: 'false'
37 | tlog-upload:
38 | description: Allow the creation of a Rekor transparency log (TLog) entry.
39 | required: false
40 | default: 'true'
41 | include-timestamp:
42 | description: Allow timestamping of the artifact signature against a timestamping authority.
43 | required: false
44 | default: 'false'
45 | fulcio-server-url:
46 | description: Fulcio server URL
47 | required: false
48 | default: 'https://fulcio.sigstore.dev'
49 | rekor-server-url:
50 | description: Rekor server URL
51 | required: false
52 | default: 'https://rekor.sigstore.dev'
53 | timestamp-server-url:
54 | description: Timestamp server URL
55 | required: false
56 | allow-submit-data-to-public-sigstore:
57 | description: Agree to submit data to an immutable public transparency log (Needed for public Sigstore)
58 | required: false
59 | default: 'false'
60 | provenance-output:
61 | description: Provenance output path. Can be absolute path, or relative path to the default working directory
62 | required: false
63 | default: 'provenance.intoto.jsonl'
64 | signed-provenance-output:
65 | description: Signed provenance output path. Can be absolute path, or relative path to the default working directory
66 | required: false
67 | default: 'provenance.intoto.jsonl.sig'
68 | github-token:
69 | description: GitHub token for enriching build information
70 | required: false
71 | default: ${{ github.token }}
72 | report-job-summary:
73 | description: Enable to report the provenance documents as job summary output
74 | required: false
75 | default: 'true'
76 | report-artifact:
77 | description: Enable to report the provenance documents as job artifacts
78 | required: false
79 | default: 'true'
80 | log-level:
81 | description: Log level (Used for debugging)
82 | required: false
83 | default: 'info'
84 | fail-on-error:
85 | description: Fail the CI if Cimon encountered an error
86 | required: false
87 | default: 'false'
88 |
89 | runs:
90 | using: node20
91 | main: 'dist/index.js'
92 |
--------------------------------------------------------------------------------
/.github/workflows/verify-pr.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Verify Pull Request
3 |
4 | on:
5 | pull_request:
6 | branches:
7 | - main
8 |
9 | permissions:
10 | contents: read
11 |
12 | # Cancel any in-flight jobs for the same pull request so there is only one job active at a time.
13 | concurrency:
14 | group: ${{ github.workflow }}-${{ github.ref }}
15 | cancel-in-progress: true
16 |
17 | jobs:
18 | verify:
19 | runs-on: ubuntu-22.04
20 | steps:
21 | - name: Checkout Code
22 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
23 |
24 | - name: Run Cimon
25 | uses: ./
26 | with:
27 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
28 | secret: ${{ secrets.CIMON_SECRET }}
29 | prevent: true
30 | allowed-hosts: >
31 | cycode.com
32 | registry.npmjs.org
33 | report-process-tree: true
34 | fail-on-error: true
35 |
36 | - name: Test
37 | run: |
38 | curl -I https://api.github.com
39 | curl -I https://cycode.com
40 | wget --quiet --timeout 1 https://registry.npmjs.org || true
41 |
42 | verify-in-container:
43 | runs-on: ubuntu-22.04
44 | container:
45 | image: alpine:3.18
46 | options: --privileged
47 | steps:
48 | - name: Checkout Code
49 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
50 |
51 | - name: Install dependencies
52 | run: |
53 | apk update
54 | apk add curl wget
55 |
56 | - name: Run Cimon
57 | uses: ./
58 | with:
59 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
60 | secret: ${{ secrets.CIMON_SECRET }}
61 | prevent: true
62 | allowed-hosts: >
63 | cycode.com
64 | registry.npmjs.org
65 | report-process-tree: true
66 | fail-on-error: true
67 |
68 | - name: Test
69 | run: |
70 | curl -I https://api.github.com
71 | curl -I https://cycode.com
72 | wget --quiet --timeout 1 https://registry.npmjs.org || true
73 |
74 | verify-provenance:
75 | runs-on: ubuntu-22.04
76 | steps:
77 | - name: Checkout Code
78 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
79 |
80 | - name: Create Private Key
81 | run: |
82 | openssl genrsa -out private-key.pem 3072
83 |
84 | - name: Run Cimon
85 | uses: ./attest
86 | with:
87 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
88 | secret: ${{ secrets.CIMON_SECRET }}
89 | subjects: NDgyY2U4YzhmN2U4NjdkYTNhM2MwNWE5YWVlNjM3NzAzZTE3NDcwZWQxY2Y4ODJhOWU1YjQwNWU4ZjgyNjE5ZCAgYXJ0aWZhY3QxCjg5Y2ZjNjk1NGU4OGIyZjkyYTdjMjg3OWQ5ZWIwODVjNDJmM2M3MDY1ZDAxMmE1MDY2ZjQ1MGRiZTU5YjJjMDAgIGFydGlmYWN0Mgo=
90 | sign-key: private-key.pem
91 | report-job-summary: true
92 | report-artifact: true
93 | fail-on-error: true
94 |
95 | verify-provenance-image-ref:
96 | runs-on: ubuntu-22.04
97 | steps:
98 | - name: Checkout Code
99 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
100 |
101 | - name: Create Private Key
102 | run: |
103 | openssl genrsa -out private-key.pem 3072
104 |
105 | - name: Run Cimon
106 | uses: ./attest
107 | with:
108 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
109 | secret: ${{ secrets.CIMON_SECRET }}
110 | subjects: 'cycodelabs/cimon@sha256:ad4ab84178621f359a5ec1ba9eff8ba46626d8d1999416646b6aaa96bfcbf802'
111 | sign-key: private-key.pem
112 | report-job-summary: true
113 | report-artifact: true
114 | fail-on-error: true
115 |
116 | verify-agent-and-attest:
117 | runs-on: ubuntu-22.04
118 | steps:
119 | - name: Checkout Code
120 | uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
121 |
122 | - name: Run Cimon Agent
123 | uses: ./
124 | with:
125 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
126 | secret: ${{ secrets.CIMON_SECRET }}
127 | report-process-tree: true
128 | fail-on-error: true
129 |
130 | - name: Create Artifact
131 | run: |
132 | echo artifact > artifact.txt
133 |
134 | - name: Run Cimon Attest
135 | uses: ./attest
136 | with:
137 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
138 | secret: ${{ secrets.CIMON_SECRET }}
139 | subjects: artifact.txt
140 | report-job-summary: true
141 | fail-on-error: true
142 |
143 | - name: Test
144 | run: |
145 | curl -I https://api.github.com
146 | curl -I https://cycode.com
147 | wget --quiet --timeout 1 https://registry.npmjs.org || true
148 |
--------------------------------------------------------------------------------
/src/main/index.js:
--------------------------------------------------------------------------------
1 | import core from '@actions/core';
2 | import exec from '@actions/exec';
3 | import fs from 'fs';
4 | import * as http from '@actions/http-client';
5 |
6 | const CIMON_SCRIPT_DOWNLOAD_URL =
7 | 'https://cimon-releases.s3.amazonaws.com/install.sh';
8 | const CIMON_SCRIPT_PATH = '/tmp/install.sh';
9 | const CIMON_EXECUTABLE_DIR = '/tmp/cimon';
10 | const CIMON_EXECUTABLE_PATH = '/tmp/cimon/cimon';
11 |
12 | const httpClient = new http.HttpClient('cimon-action');
13 |
14 | async function downloadToFile(url, filePath) {
15 | const response = await httpClient.get(url);
16 | const responseBody = await response.readBody();
17 | fs.writeFileSync(filePath, responseBody);
18 | }
19 |
20 | function getActionConfig() {
21 | return {
22 | github: {
23 | token: core.getInput('github-token'),
24 | jobSummary: core.getBooleanInput('report-job-summary'),
25 | prSummary: core.getBooleanInput('report-pr-summary'),
26 | },
27 | cimon: {
28 | logLevel: core.getInput('log-level'),
29 | preventionMode: core.getBooleanInput('prevent'),
30 | allowedIPs: core.getInput('allowed-ips'),
31 | allowedHosts: core.getInput('allowed-hosts'),
32 | fileIntegrity: core.getBooleanInput('file-integrity'),
33 | memoryProtection: core.getBooleanInput('memory-protection'),
34 | ignoredIPNets: core.getInput('ignored-ip-nets'),
35 | applyFsEvents: core.getBooleanInput('apply-fs-events'),
36 | clientId: core.getInput('client-id'),
37 | secret: core.getInput('secret'),
38 | url: core.getInput('url'),
39 | featureGates: core.getMultilineInput('feature-gates'),
40 | releasePath: core.getInput('release-path'),
41 | },
42 | report: {
43 | processTree: core.getBooleanInput('report-process-tree'),
44 | slackWebhookEndpoint: core.getInput('slack-webhook-endpoint'),
45 | },
46 | };
47 | }
48 |
49 | async function sudoExists() {
50 | try {
51 | const retval = await exec.exec('sudo', ['-v'], {
52 | silent: true,
53 | });
54 | return retval === 0;
55 | } catch (error) {
56 | return false;
57 | }
58 | }
59 |
60 | async function run(config) {
61 | let releasePath;
62 |
63 | if (config.cimon.releasePath != '') {
64 | core.info(
65 | `Running Cimon from release path: ${config.cimon.releasePath}`
66 | );
67 |
68 | if (!fs.existsSync(config.cimon.releasePath)) {
69 | throw new Error(
70 | `Cimon release path does not exist: ${config.cimon.releasePath}`
71 | );
72 | }
73 |
74 | releasePath = config.cimon.releasePath;
75 | } else {
76 | core.info('Running Cimon from latest release path');
77 |
78 | if (!fs.existsSync(CIMON_SCRIPT_PATH)) {
79 | await downloadToFile(CIMON_SCRIPT_DOWNLOAD_URL, CIMON_SCRIPT_PATH);
80 | }
81 |
82 | if (!fs.existsSync(CIMON_EXECUTABLE_DIR)) {
83 | let params = [CIMON_SCRIPT_PATH, '-b', CIMON_EXECUTABLE_DIR];
84 | if (
85 | config.cimon.logLevel == 'debug' ||
86 | config.cimon.logLevel == 'trace'
87 | ) {
88 | params.push('-d');
89 | }
90 | let retval = await exec.exec('sh', params);
91 | if (retval !== 0) {
92 | throw new Error(`Failed installing Cimon: ${retval}`);
93 | }
94 | }
95 |
96 | releasePath = CIMON_EXECUTABLE_PATH;
97 | }
98 |
99 | const env = {
100 | ...process.env,
101 | CIMON_PREVENT: config.cimon.preventionMode,
102 | CIMON_ALLOWED_IPS: config.cimon.allowedIPs,
103 | CIMON_ALLOWED_HOSTS: config.cimon.allowedHosts,
104 | CIMON_FILE_INTEGRITY: config.cimon.fileIntegrity,
105 | CIMON_MEM_PROT: config.cimon.memoryProtection,
106 | CIMON_IGNORED_IP_NETS: config.cimon.ignoredIPNets,
107 | CIMON_REPORT_GITHUB_JOB_SUMMARY: config.github.jobSummary,
108 | CIMON_REPORT_PR_SUMMARY: config.github.prSummary,
109 | CIMON_REPORT_PROCESS_TREE: config.report.processTree,
110 | CIMON_SLACK_WEBHOOK_ENDPOINT: config.report.slackWebhookEndpoint,
111 | CIMON_APPLY_FS_EVENTS: config.cimon.applyFsEvents,
112 | CIMON_CLIENT_ID: config.cimon.clientId,
113 | CIMON_SECRET: config.cimon.secret,
114 | CIMON_URL: config.cimon.url,
115 | CIMON_FEATURE_GATES: config.cimon.featureGates,
116 | GITHUB_TOKEN: config.github.token,
117 | CIMON_LOG_LEVEL: config.cimon.logLevel,
118 | CIMON_ENABLE_GITHUB_NETWORK_POLICY: true,
119 | };
120 |
121 | if (config.cimon.fileIntegrity) {
122 | // Feature flags that required for the file integrity module.
123 | env.CIMON_FEATURE_GATES = 'FSSensor=1,DataAnalysis=1';
124 |
125 | // Remove FS performance to catch large files.
126 | env.CIMON_FS_SENSOR_PERF_MODE = false;
127 | }
128 |
129 | if (config.cimon.memoryProtection) {
130 | // Feature flags that required for the memory protection module.
131 | env.CIMON_FEATURE_GATES = 'FSSensor=1';
132 | }
133 |
134 | var retval;
135 | const sudo = await sudoExists();
136 | const options = {
137 | env,
138 | detached: true,
139 | silent: false,
140 | };
141 |
142 | if (sudo) {
143 | retval = await exec.exec(
144 | 'sudo',
145 | ['-E', releasePath, 'agent', 'start-background'],
146 | options
147 | );
148 | } else {
149 | retval = await exec.exec(
150 | releasePath,
151 | ['agent', 'start-background'],
152 | options
153 | );
154 | }
155 |
156 | if (retval !== 0) {
157 | throw new Error(`Failed starting Cimon process: ${retval}`);
158 | }
159 | }
160 |
161 | try {
162 | await run(getActionConfig());
163 | } catch (error) {
164 | const failOnError = core.getBooleanInput('fail-on-error');
165 | const log = error.message;
166 | if (failOnError) {
167 | core.setFailed(log);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/attest/index.js:
--------------------------------------------------------------------------------
1 | import core from '@actions/core';
2 | import exec from '@actions/exec';
3 | import { DefaultArtifactClient } from '@actions/artifact';
4 | import * as http from '@actions/http-client';
5 | import path from 'path';
6 | import fs from 'fs';
7 |
8 | const CIMON_SCRIPT_DOWNLOAD_URL =
9 | 'https://cimon-releases.s3.amazonaws.com/install.sh';
10 | const CIMON_SCRIPT_PATH = '/tmp/install.sh';
11 | const CIMON_EXECUTABLE_DIR = '/tmp/cimon';
12 | const CIMON_EXECUTABLE_PATH = '/tmp/cimon/cimon';
13 |
14 | const httpClient = new http.HttpClient('cimon-action');
15 |
16 | async function downloadToFile(url, filePath) {
17 | const response = await httpClient.get(url);
18 | const responseBody = await response.readBody();
19 | fs.writeFileSync(filePath, responseBody);
20 | }
21 |
22 | function getActionConfig() {
23 | return {
24 | cimon: {
25 | logLevel: core.getInput('log-level'),
26 | clientId: core.getInput('client-id'),
27 | secret: core.getInput('secret'),
28 | url: core.getInput('url'),
29 | releasePath: core.getInput('release-path'),
30 | },
31 | github: {
32 | token: core.getInput('github-token'),
33 | },
34 | attest: {
35 | subjects: core.getInput('subjects'),
36 | imageRef: core.getInput('image-ref'),
37 | signKey: core.getInput('sign-key'),
38 | keyless: core.getBooleanInput('keyless'),
39 | tlogUpload: core.getBooleanInput('tlog-upload'),
40 | includeTimestamp: core.getBooleanInput('include-timestamp'),
41 | fulcioServerUrl: core.getInput('fulcio-server-url'),
42 | rekorServerUrl: core.getInput('rekor-server-url'),
43 | timestampServerUrl: core.getInput('timestamp-server-url'),
44 | allowSubmitDataToPublicSigstore: core.getBooleanInput(
45 | 'allow-submit-data-to-public-sigstore'
46 | ),
47 | provenanceOutput: core.getInput('provenance-output'),
48 | signedProvenanceOutput: core.getInput('signed-provenance-output'),
49 | },
50 | report: {
51 | reportJobSummary: core.getBooleanInput('report-job-summary'),
52 | reportArtifact: core.getBooleanInput('report-artifact'),
53 | },
54 | };
55 | }
56 |
57 | async function run(config) {
58 | let releasePath;
59 |
60 | if (config.cimon.releasePath !== '') {
61 | core.info(
62 | `Running Cimon from release path: ${config.cimon.releasePath}`
63 | );
64 |
65 | if (!fs.existsSync(config.cimon.releasePath)) {
66 | throw new Error(
67 | `Cimon release path does not exist: ${config.cimon.releasePath}`
68 | );
69 | }
70 |
71 | releasePath = config.cimon.releasePath;
72 | } else {
73 | core.info('Running Cimon from latest release path');
74 |
75 | if (!fs.existsSync(CIMON_SCRIPT_PATH)) {
76 | await downloadToFile(CIMON_SCRIPT_DOWNLOAD_URL, CIMON_SCRIPT_PATH);
77 | }
78 |
79 | if (!fs.existsSync(CIMON_EXECUTABLE_DIR)) {
80 | let params = [CIMON_SCRIPT_PATH, '-b', CIMON_EXECUTABLE_DIR];
81 | if (
82 | config.cimon.logLevel == 'debug' ||
83 | config.cimon.logLevel == 'trace'
84 | ) {
85 | params.push('-d');
86 | }
87 | let retval = await exec.exec('sh', params);
88 | if (retval !== 0) {
89 | throw new Error(`Failed installing Cimon: ${retval}`);
90 | }
91 | }
92 |
93 | releasePath = CIMON_EXECUTABLE_PATH;
94 | }
95 |
96 | if (config.attest.imageRef !== '') {
97 | core.warning(
98 | 'image-ref parameter is deprecated and will be removed in future versions. Please use subjects parameter instead.'
99 | );
100 | config.attest.subjects = config.attest.imageRef;
101 | }
102 |
103 | // Prepare CLI arguments conditionally
104 | const args = ['attest', 'generate-and-sign'];
105 | if (config.attest.subjects !== '')
106 | args.push('--subjects', config.attest.subjects);
107 | if (config.attest.provenanceOutput !== '')
108 | args.push('--output-prov', config.attest.provenanceOutput);
109 | if (config.attest.signedProvenanceOutput !== '')
110 | args.push('--output-signed-prov', config.attest.signedProvenanceOutput);
111 | if (config.attest.signKey !== '') args.push('--key', config.attest.signKey);
112 | if (config.cimon.clientId !== '')
113 | args.push('--client-id', config.cimon.clientId);
114 | if (config.cimon.secret !== '') args.push('--secret', config.cimon.secret);
115 | if (config.cimon.url !== '') args.push('--url', config.cimon.url);
116 | if (config.cimon.logLevel !== '')
117 | args.push('--log-level', config.cimon.logLevel);
118 | if (config.report.reportJobSummary) args.push('--report-job-summary');
119 | if (config.attest.keyless) {
120 | args.push('--keyless');
121 |
122 | args.push(`--tlog-upload=${config.attest.tlogUpload}`);
123 | args.push(`--include-timestamp=${config.attest.includeTimestamp}`);
124 | args.push(`--yes=${config.attest.allowSubmitDataToPublicSigstore}`);
125 |
126 | if (config.attest.fulcioServerUrl !== '') {
127 | args.push(`--fulcio-server-url=${config.attest.fulcioServerUrl}`);
128 | }
129 |
130 | if (config.attest.rekorServerUrl !== '') {
131 | args.push(`--rekor-server-url=${config.attest.rekorServerUrl}`);
132 | }
133 |
134 | if (config.attest.timestampServerUrl !== '') {
135 | args.push(
136 | `--timestamp-server-url=${config.attest.timestampServerUrl}`
137 | );
138 | }
139 | }
140 |
141 | await exec.exec(releasePath, args, {
142 | env: {
143 | ...process.env,
144 | GITHUB_TOKEN: config.github.token,
145 | },
146 | });
147 |
148 | if (config.report.reportArtifact) {
149 | const client = new DefaultArtifactClient();
150 | const jobId = process.env.GITHUB_JOB;
151 | const randomSuffix = Math.floor(Math.random() * 1000);
152 |
153 | client.uploadArtifact(
154 | `provenance-${jobId}-${randomSuffix}`,
155 | [config.attest.provenanceOutput],
156 | path.dirname(config.attest.provenanceOutput),
157 | { continueOnError: true }
158 | );
159 | if (config.attest.signKey !== '') {
160 | client.uploadArtifact(
161 | `signed-provenance-${jobId}-${randomSuffix}`,
162 | [config.attest.signedProvenanceOutput],
163 | path.dirname(config.attest.signedProvenanceOutput),
164 | { continueOnError: true }
165 | );
166 | }
167 | }
168 |
169 | core.info(`Build runtime SLSA provenance finished successfully`);
170 | }
171 |
172 | try {
173 | await run(getActionConfig());
174 | } catch (error) {
175 | const failOnError = core.getBooleanInput('fail-on-error');
176 | const log = error.message;
177 | if (failOnError) {
178 | core.setFailed(log);
179 | } else {
180 | core.warning(log);
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🦫 Secure your CI with Cimon
2 |
3 | 
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | [Cimon](https://cimon.build) (pronounced "Simon") is a runtime security solution that detects and stops software supply-chain attacks on your pipeline, including those targeting SolarWinds and CodeCov, through easy onboarding and a developer-friendly experience.
14 |
15 | By utilizing the revolutionary eBPF technology, Cimon monitors and mitigates attacks within the kernel, denying access to users' assets as soon as they arise.
16 |
17 | This action helps seamlessly deploy the agent into any desired GitHub Actions build. The action is based on the NodeJS engine and contains simple `pre` and `post` scripts to deploy and gracefully shut down the agent.
18 |
19 | Learn more about Cimon in our [docs](https://docs.cimon.build).
20 |
21 | ## 🏃♂️ Getting Started with Cimon
22 |
23 | Getting started with Cimon is as simple as introducing a single step in the pipeline. Cimon Action should be the first step in each of your jobs.
24 |
25 | We recommend starting Cimon in “Detect Mode” to allow it to learn your environment before applying preventive policies.
26 |
27 | ```yaml
28 | - uses: cycodelabs/cimon-action@v0
29 | ```
30 |
31 | ## 🔨 Usage
32 |
33 | The action supports the following parameters:
34 |
35 | | Name | Default | Description |
36 | | ------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37 | | `client-id` | | Cimon client ID for authentication |
38 | | `secret` | | Cimon secret for authentication |
39 | | `url` | | Cimon endpoint for authentication |
40 | | `prevent` | `false` | Enable prevention mode |
41 | | `allowed-ips` | | A comma or white space separated list of allowed IP addresses |
42 | | `allowed-hosts` | | A comma or white space separated list of allowed domain names. The left-most label can be the wildcard character (`*`) to match multiple subdomains (e.g. `*.example.com`). |
43 | | `ignored-ip-nets` | | A comma or white space separated list of ignored IP networks in CIDR notation, e.g. 10.0.0.0/8, 172.16.0.0/12. This setting is mandatory if your workflow runs containers attached to a custom network with configured sub-range. In other words, inter-container networking is usually ignored by Cimon. Cimon implicitly ignores 10.0.0.0/8 and 172.16.0.0/12 networks. |
44 | | `github-token` | `${{ github.token }}` | GitHub token (used to overcome GitHub rate limiting) |
45 | | `report-job-summary` | `true` | Report results through job summary output |
46 | | `report-process-tree` | `false` | Enable to report the process tree |
47 | | `slack-webhook-endpoint` | | Slack webhook endpoint to report security events |
48 | | `apply-fs-events` | `false` | Enable processing filesystem events and display them in the process tree report |
49 | | `log-level` | `info` | Log level (Used for debugging) |
50 | | `feature-gates` | | Set of key=value pairs that describe Cimon features |
51 | | `fail-on-error` | `false` | Fail the CI if Cimon encountered an error |
52 |
53 | ## ⚙️ Scenarios
54 |
55 | ### Running Cimon on detect mode
56 |
57 | ```yaml
58 | steps:
59 | - uses: cycodelabs/cimon-action@v0
60 | ```
61 |
62 | ### Running Cimon on prevent mode
63 |
64 | ```yaml
65 | steps:
66 | - uses: cycodelabs/cimon-action@v0
67 | with:
68 | prevent: true
69 | allowed-hosts: >
70 | cycode.com
71 | ```
72 |
73 | ### Running Cimon on detect mode with a process tree and file system events
74 |
75 | ```yaml
76 | steps:
77 | - uses: cycodelabs/cimon-action@v0
78 | with:
79 | report-process-tree: true
80 | apply-fs-events: true
81 | ```
82 |
83 | ### Running Cimon with enhanced Cycode capabiltiies
84 |
85 | You can read more about it [here](https://docs.cimon.build/#cimon-with-cycode).
86 |
87 | ```yaml
88 | steps:
89 | - uses: cycodelabs/cimon-action@v0
90 | with:
91 | client-id: ${{ secrets.CIMON_CLIENT_ID }}
92 | secret: ${{ secrets.CIMON_SECRET }}
93 | ```
94 |
95 | ## 🛡️ Security Report
96 |
97 | Each pipeline run will report its findings through a security report embedded within the pipeline summary in GitHub Actions.
98 |
99 | Here is an example of a Cimon report:
100 |
101 | 
102 |
103 | The report, created as a job summary, contains the profile of the running job based on the configuration and includes a snippet to assist the user with transitioning from detection to prevention.
104 |
105 | When the policy is set to "prevent", any security anomalies matching the profile are displayed on the report:
106 |
107 | 
108 |
109 | ## 🪚 Development
110 |
111 | Contributions to GitHub Action are welcome. After changes were made to the `src` folder, these changes should be reflected to the `dist` folder through the following build process:
112 |
113 | 1. Install or update package dependencies:
114 | ```
115 | npm install
116 | ```
117 | 2. Compile JavaScript source files into single entrypoint files with [ncc]:
118 | ```
119 | npm run all
120 | ```
121 |
122 | The build script will update the actions' entry points code in the [dist](dist) directory, which should be added to the Git repository.
123 |
124 | [ncc]: https://github.com/vercel/ncc
125 |
126 | ## 🪪 License
127 |
128 | [Apache License 2.0](./LICENSE.md)
129 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/assets/images/Cygives-lightmode.svg:
--------------------------------------------------------------------------------
1 |
118 |
--------------------------------------------------------------------------------
/assets/images/Cygives-darkmode.svg:
--------------------------------------------------------------------------------
1 |
118 |
--------------------------------------------------------------------------------