diff --git a/.github/workflows/scans.yml b/.github/workflows/scans.yml
new file mode 100644
index 0000000000..93d0d75425
--- /dev/null
+++ b/.github/workflows/scans.yml
@@ -0,0 +1,30 @@
+# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: Security Scans
+
+on:
+ pull_request:
+ branches: [ develop ]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v3
+ with:
+ python-version: "3.8"
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install .
+ - name: Hadolint Dockerfile Scan
+ run: |
+ docker run -v ${PWD}/openfl-docker:/openfl-docker --rm -i hadolint/hadolint hadolint -t error /openfl-docker/Dockerfile.base
diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml
new file mode 100644
index 0000000000..8618bdae16
--- /dev/null
+++ b/.github/workflows/trivy.yml
@@ -0,0 +1,29 @@
+name: build
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Build an image from Dockerfile
+ run: |
+ docker build -t docker.io/securefederatedai/openfl:${{ github.sha }} -f openfl-docker/Dockerfile.base .
+
+ - name: Run Trivy vulnerability scanner
+ uses: aquasecurity/trivy-action@master
+ with:
+ image-ref: 'docker.io/securefederatedai/openfl:${{ github.sha }}'
+ format: 'sarif'
+ output: 'trivy-results.sarif'
+
+ - name: Upload Trivy scan results to GitHub Security tab
+ uses: github/codeql-action/upload-sarif@v2
+ with:
+ sarif_file: 'trivy-results.sarif'
diff --git a/README.md b/README.md
index 0db6182bb2..ea4b780fc2 100644
--- a/README.md
+++ b/README.md
@@ -12,8 +12,12 @@
[](https://join.slack.com/t/openfl/shared_invite/zt-ovzbohvn-T5fApk05~YS_iZhjJ5yaTw)
[![License](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)](https://opensource.org/licenses/Apache-2.0)
[![Citation](https://img.shields.io/badge/cite-citation-brightgreen)](https://arxiv.org/abs/2105.06413)
-[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/interactive_api/numpy_linear_regression/workspace/SingleNotebook.ipynb)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6599/badge)](https://bestpractices.coreinfrastructure.org/projects/6599)
+
+
+
+[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/intel/openfl/blob/develop/openfl-tutorials/interactive_api/numpy_linear_regression/workspace/SingleNotebook.ipynb)
OpenFL is a Python 3 framework for Federated Learning. OpenFL is designed to be a _flexible_, _extensible_ and _easily learnable_ tool for data scientists. OpenFL is hosted by The Linux Foundation, aims to be community-driven, and welcomes contributions back to the project.
diff --git a/openfl/experimental/utilities/metaflow_utils.py b/openfl/experimental/utilities/metaflow_utils.py
index 9e26d73409..9dccca1487 100644
--- a/openfl/experimental/utilities/metaflow_utils.py
+++ b/openfl/experimental/utilities/metaflow_utils.py
@@ -25,7 +25,8 @@
from metaflow.task import MetaDatum
import fcntl
import hashlib
-from dill.source import getsource
+from dill.source import getsource # nosec
+# getsource only used to determine structure of FlowGraph
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from openfl.experimental.interface import FLSpec
@@ -62,7 +63,9 @@ def __init__(self, name):
self.name = name
def __enter__(self):
- lock_id = hashlib.new('md5', self.name.encode("utf8"), usedforsecurity=False).hexdigest()
+ lock_id = hashlib.new('md5', self.name.encode("utf8"),
+ usedforsecurity=False).hexdigest() # nosec
+ # MD5sum used for concurrency purposes, not security
self.fp = open(f"/tmp/.lock-{lock_id}.lck", "wb")
fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX)
diff --git a/openfl/interface/tutorial.py b/openfl/interface/tutorial.py
index 634e9b107f..85a0cb3f66 100644
--- a/openfl/interface/tutorial.py
+++ b/openfl/interface/tutorial.py
@@ -30,7 +30,7 @@ def start(ip, port):
"""Start the Jupyter Lab from the tutorials directory."""
from os import environ
from os import sep
- from subprocess import check_call
+ from subprocess import check_call # nosec
from sys import executable
from openfl.interface.cli_helper import TUTORIALS
diff --git a/openfl/interface/workspace.py b/openfl/interface/workspace.py
index e28e6534f5..1ee747ca7b 100644
--- a/openfl/interface/workspace.py
+++ b/openfl/interface/workspace.py
@@ -3,7 +3,7 @@
"""Workspace module."""
import os
-import subprocess
+import subprocess # nosec
import sys
from pathlib import Path
from typing import Tuple, Union
@@ -86,7 +86,7 @@ def create_(prefix, template):
def create(prefix, template):
"""Create federated learning workspace."""
from os.path import isfile
- from subprocess import check_call
+ from subprocess import check_call # nosec
from sys import executable
from openfl.interface.cli_helper import print_tree
@@ -196,7 +196,7 @@ def import_(archive):
from os.path import basename
from os.path import isfile
from shutil import unpack_archive
- from subprocess import check_call
+ from subprocess import check_call # nosec
from sys import executable
archive = Path(archive).absolute()
diff --git a/openfl/plugins/interface_serializer/dill_serializer.py b/openfl/plugins/interface_serializer/dill_serializer.py
index 5b3aaaa42e..f4bb9ffd58 100644
--- a/openfl/plugins/interface_serializer/dill_serializer.py
+++ b/openfl/plugins/interface_serializer/dill_serializer.py
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
"""Dill serializer plugin."""
-import dill
+import dill # nosec
from .serializer_interface import Serializer
@@ -24,4 +24,4 @@ def serialize(object_, filename):
def restore_object(filename):
"""Load and deserialize an object."""
with open(filename, 'rb') as f:
- return dill.load(f)
+ return dill.load(f) # nosec
diff --git a/openfl/transport/grpc/aggregator_server.py b/openfl/transport/grpc/aggregator_server.py
index fa71d98c9d..39fde16445 100644
--- a/openfl/transport/grpc/aggregator_server.py
+++ b/openfl/transport/grpc/aggregator_server.py
@@ -83,7 +83,7 @@ def validate_collaborator(self, request, context):
if not self.aggregator.valid_collaborator_cn_and_id(
common_name, collaborator_common_name):
# Random delay in authentication failures
- sleep(5 * random())
+ sleep(5 * random()) # nosec
context.abort(
StatusCode.UNAUTHENTICATED,
f'Invalid collaborator. CN: |{common_name}| '
diff --git a/openfl/utilities/ca/ca.py b/openfl/utilities/ca/ca.py
index f5dace0be7..1ff88c5742 100644
--- a/openfl/utilities/ca/ca.py
+++ b/openfl/utilities/ca/ca.py
@@ -9,11 +9,11 @@
import sys
import shutil
import signal
-import subprocess
+import subprocess # nosec
import time
from logging import getLogger
from pathlib import Path
-from subprocess import check_call
+from subprocess import check_call # nosec
from click import confirm
diff --git a/openfl/utilities/ca/downloader.py b/openfl/utilities/ca/downloader.py
index 10b9465b79..9331d1fd4a 100644
--- a/openfl/utilities/ca/downloader.py
+++ b/openfl/utilities/ca/downloader.py
@@ -67,5 +67,6 @@ def _download(url, prefix, confirmation):
if confirmation:
confirm('CA binaries will be downloaded now', default=True, abort=True)
name = url.split('/')[-1]
- urllib.request.urlretrieve(url, f'{prefix}/{name}')
+ # nosec: private function definition with static urls
+ urllib.request.urlretrieve(url, f'{prefix}/{name}') # nosec
shutil.unpack_archive(f'{prefix}/{name}', f'{prefix}/step')
diff --git a/openfl/utilities/workspace.py b/openfl/utilities/workspace.py
index bf392e0259..c1561b726e 100644
--- a/openfl/utilities/workspace.py
+++ b/openfl/utilities/workspace.py
@@ -10,7 +10,7 @@
import time
from contextlib import contextmanager
from pathlib import Path
-from subprocess import check_call
+from subprocess import check_call # nosec
from sys import executable
from typing import Optional
from typing import Tuple