Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,34 @@ The Socket Security CLI was created to enable integrations with other tools like
## Usage

```` shell
socketcli [-h] [--api_token API_TOKEN] [--repo REPO] [--branch BRANCH] [--committer COMMITTER] [--pr_number PR_NUMBER] [--commit_message COMMIT_MESSAGE] [--default_branch DEFAULT_BRANCH]
[--target_path TARGET_PATH] [--mode {diff,new,license}] [--scm {api,github}] [--generate-license GENERATE_LICENSE]
socketcli [-h] [--api_token API_TOKEN] [--repo REPO] [--branch BRANCH] [--committer COMMITTER] [--pr_number PR_NUMBER]
[--commit_message COMMIT_MESSAGE] [--default_branch] [--target_path TARGET_PATH] [--scm {api,github,gitlab}] [--sbom-file SBOM_FILE]
[--commit-sha COMMIT_SHA] [--generate-license GENERATE_LICENSE] [-v] [--enable-debug] [--enable-json] [--disable-overview]
[--disable-security-issue] [--files FILES]
````

If you don't want to provide the Socket API Token every time then you can use the environment variable `SOCKET_SECURITY_API_KEY`


| Parameter | Alternate Name | Required | Default | Description |
|:-------------------|:---------------|:---------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| -h | --help | False | | Show the CLI help message |
| --api_token | | False | | Provides the Socket API Token |
| --repo | | True | | The string name in a git approved name for repositories. |
| --branch | | False | | The string name in a git approved name for branches. |
| --committer | | False | | The string name of the person doing the commit or running the CLI. Can be specified multiple times to have more than one committer |
| --pr_number | | False | 0 | The integer for the PR or MR number |
| --commit_message | | False | | The string for a commit message if there is one |
| --default_branch | | False | False | If the flag is specified this will signal that this is the default branch. This needs to be enabled for a report to update Org Alerts and Org Dependencies |
| --target_path | | False | ./ | This is the path to where the manifest files are location. The tool will recursively search for all supported manifest files |
| --scm | | False | api | This is the mode that the tool is to run in. For local runs `api` would be the mode. Other options are `gitlab` and `github` |
| --generate-license | | False | False | If this flag is specified it will generate a json file with the license per package and license text in the current working directory |
| --version | -v | False | | Prints the version and exits |
| --enable-debug | | False | False | Enables debug messaging for the CLI |
| Parameter | Alternate Name | Required | Default | Description |
|:-------------------------|:---------------|:---------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| -h | --help | False | | Show the CLI help message |
| --api_token | | False | | Provides the Socket API Token |
| --repo | | True | | The string name in a git approved name for repositories. |
| --branch | | False | | The string name in a git approved name for branches. |
| --committer | | False | | The string name of the person doing the commit or running the CLI. Can be specified multiple times to have more than one committer |
| --pr_number | | False | 0 | The integer for the PR or MR number |
| --commit_message | | False | | The string for a commit message if there is one |
| --default_branch | | False | False | If the flag is specified this will signal that this is the default branch. This needs to be enabled for a report to update Org Alerts and Org Dependencies |
| --target_path | | False | ./ | This is the path to where the manifest files are location. The tool will recursively search for all supported manifest files |
| --scm | | False | api | This is the mode that the tool is to run in. For local runs `api` would be the mode. Other options are `gitlab` and `github` |
| --generate-license | | False | False | If this flag is specified it will generate a json file with the license per package and license text in the current working directory |
| --version | -v | False | | Prints the version and exits |
| --enable-debug | | False | False | Enables debug messaging for the CLI |
| --sbom-file | | False | False | Creates a JSON file with all dependencies and alerts |
| --commit-sha | | False | | The commit hash for the commit |
| --generate-license | | False | False | If enabled with `--sbom-file` will include license details |
| --enable-json | | False | False | If enabled will change the console output format to JSON |
| --disable-overview | | False | False | If enabled will disable Dependency Overview comments |
| --disable-security-issue | | False | False | If enabled will disable Security Issue Comments |
| --files | | False | | If provided in the format of `["file1", "file2"]` it will only look for those files and not glob the path |
3 changes: 2 additions & 1 deletion scripts/build_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ echo $VERSION
if [ -z $BYPASS_PYPI_BUILD ] || [ $BYPASS_PYPI_BUILD -eq 0 ]; then
python -m build --wheel --sdist
twine upload dist/*$VERSION*
sleep 180
sleep 240
fi

docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:$VERSION . \
&& docker build --no-cache --build-arg CLI_VERSION=$VERSION --platform linux/amd64,linux/arm64 -t socketdev/cli:latest . \
&& docker push socketdev/cli:$VERSION \
Expand Down
2 changes: 1 addition & 1 deletion socketsecurity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__author__ = 'socket.dev'
__version__ = '0.0.86'
__version__ = '0.0.95'
41 changes: 23 additions & 18 deletions socketsecurity/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
)
import platform
from glob import glob
import fnmatch
import time

__all__ = [
"Core",
"log",
"__version__"
"__version__",
"do_request"
]


Expand Down Expand Up @@ -63,27 +63,32 @@ def do_request(
payload: [dict, str] = None,
files: list = None,
method: str = "GET",
base_url: str = None,
) -> requests.request:
"""
do_requests is the shared function for making HTTP calls

:param base_url:
:param path: Required path for the request
:param headers: Optional dictionary of headers. If not set will use a default set
:param payload: Optional dictionary or string of the payload to pass
:param files: Optional list of files to upload
:param method: Optional method to use, defaults to GET
:return:
"""
if encoded_key is None or encoded_key == "":
raise APIKeyMissing

if base_url is not None:
url = f"{base_url}/{path}"
else:
if encoded_key is None or encoded_key == "":
raise APIKeyMissing
url = f"{api_url}/{path}"

if headers is None:
headers = {
'Authorization': f"Basic {encoded_key}",
'User-Agent': f'SocketPythonCLI/{__version__}',
"accept": "application/json"
}
url = f"{api_url}/{path}"
response = requests.request(
method.upper(),
url,
Expand All @@ -92,8 +97,8 @@ def do_request(
files=files,
timeout=timeout
)
output_headers = headers
output_headers['Authorization'] = "Basic API_KEY_REDACTED"
output_headers = headers.copy()
output_headers['Authorization'] = "API_KEY_REDACTED"
output = {
"url": url,
"headers": output_headers,
Expand All @@ -107,14 +112,7 @@ def do_request(
if response.status_code <= 399:
return response
elif response.status_code == 400:
print(f"url={url}")
print(f"payload={payload}")
print(f"files={files}")
error = {
"msg": "bad request",
"error": response.text
}
raise APIFailure(error)
raise APIFailure(output)
elif response.status_code == 401:
raise APIAccessDenied("Unauthorized")
elif response.status_code == 403:
Expand All @@ -128,8 +126,10 @@ def do_request(
else:
msg = {
"status_code": response.status_code,
"UnexpectedError": "There was an unexpected error using the API",
"error": response.text,
"UnexpectedError": "There was an unexpected error using the API"
"payload": payload,
"url": url
}
raise APIFailure(msg)

Expand All @@ -155,6 +155,11 @@ def __init__(self, token: str, base_api_url=None, request_timeout=None, enable_a
all_new_alerts = True
Core.set_org_vars()

@staticmethod
def enable_debug_log(level: int):
global log
log.setLevel(level)

@staticmethod
def set_org_vars() -> None:
"""
Expand Down Expand Up @@ -394,7 +399,7 @@ def find_files(path: str, new_files: list = None) -> list:
_, base_name = file.rsplit("/", 1)
else:
base_name = file
if new_files is not None and base_name not in new_files:
if new_files is not None and len(new_files) > 0 and base_name not in new_files:
continue
if platform.system() == "Windows":
file = file.replace("\\", "/")
Expand Down
74 changes: 18 additions & 56 deletions socketsecurity/core/github.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import json
import os
from socketsecurity.core import log
from socketsecurity.core import log, do_request
import requests
from socketsecurity.core.exceptions import *
from socketsecurity.core.classes import Comment
from socketsecurity.core.scm_comments import Comments
import sys
Expand Down Expand Up @@ -53,58 +52,15 @@
is_default_branch = False
else:
is_default_branch = True
if var_name != "gh_api_token":
value = globals()[var_name] = os.getenv(env) or None
log.debug(f"{env}={value}")


def do_request(
path: str,
headers: dict = None,
payload: [dict, str] = None,
files: list = None,
method: str = "GET",
) -> dict:
"""
do_requests is the shared function for making HTTP calls

:param path: Required path for the request
:param headers: Optional dictionary of headers. If not set will use a default set
:param payload: Optional dictionary or string of the payload to pass
:param files: Optional list of files to upload
:param method: Optional method to use, defaults to GET
:return:
"""
if gh_api_token is None or gh_api_token == "":
raise APIKeyMissing

if headers is None:
headers = {
'Authorization': f"Bearer {gh_api_token}",
'User-Agent': 'SocketPythonScript/0.0.1',
"accept": "application/json"
}
url = f"{github_api_url}/{path}"
response = requests.request(
method.upper(),
url,
headers=headers,
data=payload,
files=files
)
if response.status_code <= 399:
try:
return response.json()
except Exception as error:
response = {
"error": error,
"response": response.text
}
return response
else:
msg = {
"status_code": response.status_code,
"UnexpectedError": "There was an unexpected error using the API",
"error": response.text
}
raise APIFailure(msg)
headers = {
'Authorization': f"Bearer {gh_api_token}",
'User-Agent': 'SocketPythonScript/0.0.1',
"accept": "application/json"
}


class Github:
Expand Down Expand Up @@ -174,16 +130,22 @@ def add_socket_comments(
existing_overview_comment = comments.get("overview")
existing_security_comment = comments.get("security")
if new_overview_comment:
log.debug("New Dependency Overview comment")
if existing_overview_comment is not None:
log.debug("Previous version of Dependency Overview, updating")
existing_overview_comment: Comment
Github.update_comment(overview_comment, str(existing_overview_comment.id))
else:
log.debug("No previous version of Dependency Overview, posting")
Github.post_comment(overview_comment)
if new_security_comment:
log.debug("New Security Issue Comment")
if existing_security_comment is not None:
log.debug("Previous version of Security Issue comment, updating")
existing_security_comment: Comment
Github.update_comment(security_comment, str(existing_security_comment.id))
else:
log.debug("No Previous version of Security Issue comment, posting")
Github.post_comment(security_comment)

@staticmethod
Expand All @@ -194,7 +156,7 @@ def post_comment(body: str) -> None:
"body": body
}
payload = json.dumps(payload)
do_request(path, payload=payload, method="POST")
do_request(path, payload=payload, method="POST", headers=headers, base_url=github_api_url)

@staticmethod
def update_comment(body: str, comment_id: str) -> None:
Expand All @@ -204,7 +166,7 @@ def update_comment(body: str, comment_id: str) -> None:
"body": body
}
payload = json.dumps(payload)
do_request(path, payload=payload, method="PATCH")
do_request(path, payload=payload, method="PATCH", headers=headers, base_url=github_api_url)

@staticmethod
def write_new_env(name: str, content: str) -> None:
Expand All @@ -216,7 +178,7 @@ def write_new_env(name: str, content: str) -> None:
@staticmethod
def get_comments_for_pr(repo: str, pr: str) -> dict:
path = f"repos/{github_repository_owner}/{repo}/issues/{pr}/comments"
raw_comments = do_request(path)
raw_comments = Comments.process_response(do_request(path, headers=headers, base_url=github_api_url))
comments = {}
if "error" not in raw_comments:
for item in raw_comments:
Expand Down
Loading