Skip to content

Commit

Permalink
Add workflows for building and pushing container image
Browse files Browse the repository at this point in the history
  • Loading branch information
sfowl committed Oct 1, 2024
1 parent 630a06d commit 6c24eba
Show file tree
Hide file tree
Showing 34 changed files with 257 additions and 535 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build and push container image

env:
IMAGE_NAME: "rapidast"
IMAGE_TAGS: "${{ github.sha }}"
IMAGE_REGISTRY: quay.io/redhatproductsecurity
IMAGE_REGISTRY_USER: ${{ secrets.IMAGE_REGISTRY_USER }}
IMAGE_REGISTRY_PASSWORD: ${{ secrets.IMAGE_REGISTRY_PASSWORD }}

on:
push:
branches: ["development", "main"]

jobs:

build-and-push:

runs-on: ubuntu-latest

# https://github.com/redhat-actions/buildah-build#readme
steps:
- name: Build container image
uses: redhat-actions/buildah-build@v2
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
dockerfiles: |
./containerize/Containerfile
# https://github.com/redhat-actions/push-to-registry#readme
- name: Push to registry
id: push-image
uses: redhat-actions/push-to-registry@v2
with:
image: ${{ steps.build-image.outputs.image }}
tags: ${{ steps.build-image.outputs.tags }}
registry: ${{ env.IMAGE_REGISTRY }}
username: ${{ env.IMAGE_REGISTRY_USER }}
password: ${{ env.IMAGE_REGISTRY_PASSWORD }}
25 changes: 25 additions & 0 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Build container image

env:
IMAGE_NAME: "rapidast"
IMAGE_TAGS: "${{ github.sha }}"

on:
pull_request:
branches: ["development", "main"]

jobs:

build-image:

runs-on: ubuntu-latest

# https://github.com/redhat-actions/buildah-build#readme
steps:
- name: Build container image
uses: redhat-actions/buildah-build@v2
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAGS }}
dockerfiles: |
./containerize/Containerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: RapiDAST
name: Run tests

on:
push:
Expand Down
20 changes: 5 additions & 15 deletions configmodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ def delete(self, path):
except AttributeError:
pass
# Failed to iterate until the end: the path does not exist
logging.warning(
f"RapidastConfigModel.delete(): Config path {path} was not found. No deletion"
)
logging.warning(f"RapidastConfigModel.delete(): Config path {path} was not found. No deletion")
return False

def exists(self, path):
Expand Down Expand Up @@ -122,9 +120,7 @@ def set(self, path, value, overwrite=True):
tmp = walk[key]
# case 3: not a "dictionary" type: warn and overwrite (if True)
if not isinstance(tmp, dict):
logging.warning(
f"RapidastConfigModel.set: Incompatible {path} at {tmp}"
)
logging.warning(f"RapidastConfigModel.set: Incompatible {path} at {tmp}")
if not overwrite:
logging.info("RapidastConfigModel.set: no overwrite: early return")
return False
Expand Down Expand Up @@ -162,9 +158,7 @@ def merge(self, merge, preserve=False, root=None):
if not merge:
return
if not isinstance(merge, dict):
raise TypeError(
f"RapidastConfigModel.merge: merge must be a dict (was: {type(merge)})"
)
raise TypeError(f"RapidastConfigModel.merge: merge must be a dict (was: {type(merge)})")

root = path_to_list(root)

Expand Down Expand Up @@ -200,9 +194,7 @@ def descend(root):
if key.endswith("_from_var"):
new[key.removesuffix("_from_var")] = os.environ[val]
if not new[key.removesuffix("_from_var")]:
logging.warning(
f"configuration {key} points to environment variable {val}, which is empty"
)
logging.warning(f"configuration {key} points to environment variable {val}, which is empty")
else:
new[key] = descend(val)
return new
Expand All @@ -220,9 +212,7 @@ def descend(root):
return None

if not isinstance(subtree, dict):
raise KeyError(
f"subtree_to_dict(): '{path}' does not point to a dictionary in the config"
)
raise KeyError(f"subtree_to_dict(): '{path}' does not point to a dictionary in the config")

return descend(subtree)

Expand Down
35 changes: 9 additions & 26 deletions configmodel/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ def dispatch(version):
def convert_configmodel(conf):
"""This is the base function, attached to error reporting"""
version = conf.get("config.configVersion", 0)
raise RuntimeError(
f"There was an error in converting configuration. No convertion available for version {version}"
)
raise RuntimeError(f"There was an error in converting configuration. No convertion available for version {version}")


@convert_configmodel.register(4)
Expand All @@ -60,9 +58,7 @@ def convert_from_version_4_to_5(old):
new = copy.deepcopy(old)

for key in old.conf["scanners"]:
if key.startswith("zap") and old.exists(
f"scanners.{key}.miscOptions.oauth2OpenapiManualDownload"
):
if key.startswith("zap") and old.exists(f"scanners.{key}.miscOptions.oauth2OpenapiManualDownload"):
new.move(
f"scanners.{key}.miscOptions.oauth2OpenapiManualDownload",
f"scanners.{key}.miscOptions.oauth2ManualDownload",
Expand Down Expand Up @@ -174,29 +170,22 @@ def convert_from_version_0_to_1(old):
auth_method = old.get("scan.auth_method", default=None)
if (
auth_method == "scriptBasedAuthentication"
and old.get("scan.scriptAuth.authScriptFilePath", default="")
== "scripts/offline-token.js"
and old.get("scan.scriptAuth.authScriptFilePath", default="") == "scripts/offline-token.js"
):
# probably OAuth2
new.set(
"general.authentication",
{
"type": "oauth2_rtoken",
"parameters": {
"client_id": old.get(
"scan.scriptAuth.authClientID", default="cloud-services"
),
"token_endpoint": old.get(
"scan.scriptAuth.authTokenEndpoint", default=""
),
"client_id": old.get("scan.scriptAuth.authClientID", default="cloud-services"),
"token_endpoint": old.get("scan.scriptAuth.authTokenEndpoint", default=""),
"rtoken_var_name": "RTOKEN",
},
},
)
else:
logging.warning(
"The config version translator does not support this particular authentication"
)
logging.warning("The config version translator does not support this particular authentication")

# "Scanners.Zap" section
new.set(
Expand All @@ -206,13 +195,9 @@ def convert_from_version_0_to_1(old):

### OpenAPI
if old.get("openapi.importFromUrl", default=False):
new.set(
"scanners.zap.apiScan.apis.apiUrl", old.get("openapi.url", default=None)
)
new.set("scanners.zap.apiScan.apis.apiUrl", old.get("openapi.url", default=None))
elif old.get("openapi.directory", default=""):
logging.warning(
"The config version translator does not support Directory based OpenAPI"
)
logging.warning("The config version translator does not support Directory based OpenAPI")

## Passive scan
new.set("scanners.zap.passiveScan", {})
Expand All @@ -225,9 +210,7 @@ def convert_from_version_0_to_1(old):
## Active scan
# Active scanner was always enabled, so we do the same:
new.set("scanners.zap.activeScan", {})
new.set(
"scanners.zap.activeScan.policy", old.get("scan.policies.scanPolicyName", None)
)
new.set("scanners.zap.activeScan.policy", old.get("scan.policies.scanPolicyName", None))

# Finally, set the correct version number
new.set("config.configVersion", 1)
Expand Down
48 changes: 12 additions & 36 deletions exports/defect_dojo.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ class DefectDojo:

def __init__(self, base_url, login=None, token=None, ssl=None):
if not base_url:
raise ValueError(
"Defect Dojo invalid configuration: URL is a mandatory value"
)
raise ValueError("Defect Dojo invalid configuration: URL is a mandatory value")
parsed = parse.urlparse(base_url) # expects to raise exception on invalid URL
if parsed.scheme not in ["http", "https"]:
raise ValueError("Defect Dojo invalid configuration: URL is not correct")
Expand All @@ -27,9 +25,7 @@ def __init__(self, base_url, login=None, token=None, ssl=None):
self.username = login["username"]
self.password = login["password"]
except KeyError:
logging.error(
"RapiDAST BUG: DefectDojo was created with invalid login information..."
)
logging.error("RapiDAST BUG: DefectDojo was created with invalid login information...")
logging.error("RapiDAST BUG: ...[continuing without credentials]")

self.token = token
Expand All @@ -47,9 +43,7 @@ def _auth_and_set_token(self):
"""Force a refresh of the token using the username/password"""
logging.debug("Defect Dojo: refreshing token")
if not self.username or not self.password:
raise ValueError(
"Defect Dojo invalid configuration: A username and a password are required to get a token"
)
raise ValueError("Defect Dojo invalid configuration: A username and a password are required to get a token")
url = self.base_url + "/api/v2/api-token-auth/"
data = {"username": self.username, "password": self.password}

Expand All @@ -63,9 +57,7 @@ def _auth_and_set_token(self):
self.headers["Authorization"] = f"Token {self.token}"
logging.debug("Defect Dojo: successfully refreshed token")
except requests.exceptions.ConnectTimeout as e:
logging.error(
f"Getting token failed. Check the URL for defectDojo in config file. err details: {e}"
)
logging.error(f"Getting token failed. Check the URL for defectDojo in config file. err details: {e}")
return 1
except requests.exceptions.HTTPError as e:
logging.error(
Expand Down Expand Up @@ -96,9 +88,7 @@ def engagement_exists(self, engagement_id=None, name=None):
raise ValueError("Either an engagement name or ID must be provided")

if resp.status_code >= 400:
logging.warning(
f"Error while looking for engagement ({resp.status_code}, {resp.get('message')})"
)
logging.warning(f"Error while looking for engagement ({resp.status_code}, {resp.get('message')})")
counts = resp.json()["counts"]
if counts > 1:
logging.warning("Error while looking for engagement: too many hits")
Expand Down Expand Up @@ -134,9 +124,7 @@ def _private_import(self, endpoint, data, filename):
logging.error(f"Error while exporting ({resp.status_code}, {err})")

if "Invalid token" in err["detail"]:
logging.error(
"Please check your token in 'config.defectDojo' of the config file"
)
logging.error("Please check your token in 'config.defectDojo' of the config file")

return 1

Expand All @@ -146,31 +134,19 @@ def reimport_scan(self, data, filename):
"""Reimport to an existing engagement with an existing compatible scan."""

if not data.get("test") and not (
data.get("engagement_name")
and data.get("product_name")
and data.get("test_title")
data.get("engagement_name") and data.get("product_name") and data.get("test_title")
):
raise ValueError(
"Reimport needs to identify an existing test (by ID or names of product+engagement+test)"
)
raise ValueError("Reimport needs to identify an existing test (by ID or names of product+engagement+test)")

return self._private_import(
f"{self.base_url}/api/v2/reimport-scan/", data, filename
)
return self._private_import(f"{self.base_url}/api/v2/reimport-scan/", data, filename)

def import_scan(self, data, filename):
"""export to an existing engagement, via the `import-scan` endpoint."""

if not data.get("engagement") and not (
data.get("engagement_name") and data.get("product_name")
):
raise ValueError(
"Import needs to identify an existing engagement (by ID or names of product+engagement)"
)
if not data.get("engagement") and not (data.get("engagement_name") and data.get("product_name")):
raise ValueError("Import needs to identify an existing engagement (by ID or names of product+engagement)")

return self._private_import(
f"{self.base_url}/api/v2/import-scan/", data, filename
)
return self._private_import(f"{self.base_url}/api/v2/import-scan/", data, filename)

def export_scan(self, data, filename):
"""Decide wether to import or reimport. Based on:
Expand Down
10 changes: 2 additions & 8 deletions exports/google_cloud_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ def export_scan(self, data, filename):

metadata = self.create_metadata(data)

logging.info(
f"GoogleCloudStorage: sending {filename}. UUID: {metadata['uuid']}"
)
logging.info(f"GoogleCloudStorage: sending {filename}. UUID: {metadata['uuid']}")

# export data as a metadata.json file
json_stream = StringIO()
Expand All @@ -82,11 +80,7 @@ def export_scan(self, data, filename):
unique_id = "{}-RapiDAST-{}-{}.tgz".format( # pylint: disable=C0209
datetime.datetime.now(tz=datetime.timezone.utc).isoformat(),
self.app_name,
"".join(
random.choices(
string.ascii_letters + string.ascii_uppercase + string.digits, k=6
)
),
"".join(random.choices(string.ascii_letters + string.ascii_uppercase + string.digits, k=6)),
)
blob_name = self.directory + "/" + unique_id

Expand Down
Loading

0 comments on commit 6c24eba

Please sign in to comment.