mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(github): handle separately path and content of github app key
This commit is contained in:
@@ -242,6 +242,7 @@ class Provider(ABC):
|
||||
provider_class(
|
||||
personal_access_token=arguments.personal_access_token,
|
||||
oauth_app_token=arguments.oauth_app_token,
|
||||
github_app_key=arguments.github_app_key,
|
||||
github_app_key_path=arguments.github_app_key_path,
|
||||
github_app_id=arguments.github_app_id,
|
||||
mutelist_path=arguments.mutelist_file,
|
||||
|
||||
@@ -102,6 +102,7 @@ class GithubProvider(Provider):
|
||||
# Authentication credentials
|
||||
personal_access_token: str = "",
|
||||
oauth_app_token: str = "",
|
||||
github_app_key: str = "",
|
||||
github_app_key_path: str = "",
|
||||
github_app_key_content: str = "",
|
||||
github_app_id: int = 0,
|
||||
@@ -120,8 +121,9 @@ class GithubProvider(Provider):
|
||||
Args:
|
||||
personal_access_token (str): GitHub personal access token.
|
||||
oauth_app_token (str): GitHub OAuth App token.
|
||||
github_app_key (str): GitHub App key content.
|
||||
github_app_key_path (str): Path to GitHub App private key file.
|
||||
github_app_key_content (str): GitHub App private key content.
|
||||
github_app_key_content (str): GitHub App private key content (legacy parameter).
|
||||
github_app_id (int): GitHub App ID.
|
||||
config_path (str): Path to the audit configuration file.
|
||||
config_content (dict): Audit configuration content.
|
||||
@@ -147,6 +149,7 @@ class GithubProvider(Provider):
|
||||
personal_access_token,
|
||||
oauth_app_token,
|
||||
github_app_id,
|
||||
github_app_key,
|
||||
github_app_key_path,
|
||||
github_app_key_content,
|
||||
)
|
||||
@@ -156,7 +159,9 @@ class GithubProvider(Provider):
|
||||
self._auth_method = "Personal Access Token"
|
||||
elif oauth_app_token:
|
||||
self._auth_method = "OAuth App Token"
|
||||
elif github_app_id and (github_app_key_path or github_app_key_content):
|
||||
elif github_app_id and (
|
||||
github_app_key or github_app_key_path or github_app_key_content
|
||||
):
|
||||
self._auth_method = "GitHub App Token"
|
||||
elif environ.get("GITHUB_PERSONAL_ACCESS_TOKEN", ""):
|
||||
self._auth_method = "Environment Variable for Personal Access Token"
|
||||
@@ -250,6 +255,7 @@ class GithubProvider(Provider):
|
||||
personal_access_token: str = None,
|
||||
oauth_app_token: str = None,
|
||||
github_app_id: int = 0,
|
||||
github_app_key: str = None,
|
||||
github_app_key_path: str = None,
|
||||
github_app_key_content: str = None,
|
||||
) -> GithubSession:
|
||||
@@ -260,8 +266,9 @@ class GithubProvider(Provider):
|
||||
personal_access_token (str): GitHub personal access token.
|
||||
oauth_app_token (str): GitHub OAuth App token.
|
||||
github_app_id (int): GitHub App ID.
|
||||
github_app_key (str): GitHub App key content.
|
||||
github_app_key_path (str): Path to GitHub App private key file.
|
||||
github_app_key_content (str): GitHub App private key content.
|
||||
github_app_key_content (str): GitHub App private key content (legacy parameter).
|
||||
Returns:
|
||||
GithubSession: Authenticated session token for API requests.
|
||||
"""
|
||||
@@ -278,11 +285,35 @@ class GithubProvider(Provider):
|
||||
elif oauth_app_token:
|
||||
session_token = oauth_app_token
|
||||
|
||||
elif github_app_id and (github_app_key_path or github_app_key_content):
|
||||
elif github_app_id and (
|
||||
github_app_key or github_app_key_path or github_app_key_content
|
||||
):
|
||||
app_id = github_app_id
|
||||
if github_app_key_path:
|
||||
with open(github_app_key_path, "r") as rsa_key:
|
||||
app_key = rsa_key.read()
|
||||
try:
|
||||
with open(github_app_key_path, "r") as rsa_key:
|
||||
app_key = rsa_key.read()
|
||||
except OSError as e:
|
||||
if e.errno == 63:
|
||||
raise GithubEnvironmentVariableError(
|
||||
file=os.path.basename(__file__),
|
||||
message="--github-app-key-path expects a file path, not key content. Use --github-app-key for key content instead.",
|
||||
)
|
||||
else:
|
||||
raise GithubEnvironmentVariableError(
|
||||
file=os.path.basename(__file__),
|
||||
message=f"Could not read GitHub App key file '{github_app_key_path}': {e.message}",
|
||||
)
|
||||
elif github_app_key:
|
||||
if github_app_key.startswith("-----BEGIN"):
|
||||
app_key = format_rsa_key(github_app_key)
|
||||
elif os.path.isfile(github_app_key):
|
||||
raise GithubEnvironmentVariableError(
|
||||
file=os.path.basename(__file__),
|
||||
message="--github-app-key expects key content, not a file path. Use --github-app-key-path for file paths instead.",
|
||||
)
|
||||
else:
|
||||
app_key = format_rsa_key(github_app_key)
|
||||
else:
|
||||
app_key = format_rsa_key(github_app_key_content)
|
||||
|
||||
@@ -303,22 +334,27 @@ class GithubProvider(Provider):
|
||||
if not session_token:
|
||||
# APP
|
||||
logger.info(
|
||||
"Looking for GITHUB_APP_ID and GITHUB_APP_KEY environment variables as user has not provided any token...."
|
||||
"Looking for GitHub App environment variables as user has not provided any token...."
|
||||
)
|
||||
app_id = environ.get("GITHUB_APP_ID", "")
|
||||
env_key = environ.get("GITHUB_APP_KEY", "")
|
||||
|
||||
if app_id and env_key:
|
||||
if env_key.startswith("-----BEGIN"):
|
||||
app_key = format_rsa_key(env_key)
|
||||
elif os.path.isfile(env_key):
|
||||
with open(env_key, "r") as rsa_key:
|
||||
app_key = rsa_key.read()
|
||||
else:
|
||||
raise GithubEnvironmentVariableError(
|
||||
file=os.path.basename(__file__),
|
||||
message="GITHUB_APP_KEY must contain either RSA key content (starting with -----BEGIN) or a valid file path.",
|
||||
)
|
||||
app_key_path = environ.get("GITHUB_APP_KEY_PATH", "")
|
||||
if app_key_path:
|
||||
with open(app_key_path, "r") as rsa_key:
|
||||
app_key = rsa_key.read()
|
||||
else:
|
||||
env_key = environ.get("GITHUB_APP_KEY", "")
|
||||
if env_key:
|
||||
if env_key.startswith("-----BEGIN"):
|
||||
app_key = format_rsa_key(env_key)
|
||||
elif os.path.isfile(env_key):
|
||||
with open(env_key, "r") as rsa_key:
|
||||
app_key = rsa_key.read()
|
||||
else:
|
||||
raise GithubEnvironmentVariableError(
|
||||
file=os.path.basename(__file__),
|
||||
message="GITHUB_APP_KEY must contain either RSA key content (starting with -----BEGIN) or a valid file path.",
|
||||
)
|
||||
|
||||
if not session_token and not (app_id and app_key):
|
||||
raise GithubEnvironmentVariableError(
|
||||
@@ -528,6 +564,7 @@ class GithubProvider(Provider):
|
||||
def test_connection(
|
||||
personal_access_token: str = "",
|
||||
oauth_app_token: str = "",
|
||||
github_app_key: str = "",
|
||||
github_app_key_path: str = "",
|
||||
github_app_key_content: str = "",
|
||||
github_app_id: int = 0,
|
||||
@@ -541,8 +578,9 @@ class GithubProvider(Provider):
|
||||
Args:
|
||||
personal_access_token (str): GitHub personal access token.
|
||||
oauth_app_token (str): GitHub OAuth App token.
|
||||
github_app_key (str): GitHub App key content.
|
||||
github_app_key_path (str): Path to GitHub App private key file.
|
||||
github_app_key_content (str): GitHub App private key content.
|
||||
github_app_key_content (str): GitHub App private key content (legacy parameter).
|
||||
github_app_id (int): GitHub App ID.
|
||||
raise_on_exception (bool): Flag indicating whether to raise an exception if the connection fails.
|
||||
provider_id (str): The provider ID, in this case it's the GitHub organization/username.
|
||||
@@ -573,6 +611,7 @@ class GithubProvider(Provider):
|
||||
personal_access_token=personal_access_token,
|
||||
oauth_app_token=oauth_app_token,
|
||||
github_app_id=github_app_id,
|
||||
github_app_key=github_app_key,
|
||||
github_app_key_path=github_app_key_path,
|
||||
github_app_key_content=github_app_key_content,
|
||||
)
|
||||
|
||||
@@ -30,9 +30,15 @@ def init_parser(self):
|
||||
metavar="GITHUB_APP_ID",
|
||||
)
|
||||
github_auth_subparser.add_argument(
|
||||
"--github-app-key-path",
|
||||
"--github-app-key",
|
||||
nargs="?",
|
||||
help="GitHub App Key content (PEM format) to log in against GitHub",
|
||||
default=None,
|
||||
metavar="GITHUB_APP_KEY_CONTENT",
|
||||
)
|
||||
github_auth_subparser.add_argument(
|
||||
"--github-app-key-path",
|
||||
nargs="?",
|
||||
help="Path to GitHub App private key file",
|
||||
default=None,
|
||||
metavar="GITHUB_APP_KEY_PATH",
|
||||
@@ -55,3 +61,31 @@ def init_parser(self):
|
||||
default=None,
|
||||
metavar="ORGANIZATION",
|
||||
)
|
||||
|
||||
|
||||
def validate_arguments(arguments) -> tuple[bool, str]:
|
||||
"""
|
||||
Validate GitHub provider arguments
|
||||
|
||||
Args:
|
||||
arguments: Parsed command line arguments
|
||||
|
||||
Returns:
|
||||
tuple[bool, str]: (is_valid, error_message)
|
||||
"""
|
||||
|
||||
if arguments.github_app_key and arguments.github_app_key_path:
|
||||
return (
|
||||
False,
|
||||
"Cannot specify both --github-app-key and --github-app-key-path simultaneously",
|
||||
)
|
||||
|
||||
if arguments.github_app_id and not (
|
||||
arguments.github_app_key or arguments.github_app_key_path
|
||||
):
|
||||
return (
|
||||
False,
|
||||
"GitHub App ID requires either --github-app-key or --github-app-key-path",
|
||||
)
|
||||
|
||||
return True, ""
|
||||
|
||||
@@ -217,7 +217,7 @@ class TestGitHubProvider:
|
||||
),
|
||||
):
|
||||
connection = GithubProvider.test_connection(
|
||||
github_app_id=APP_ID, github_app_key_path=APP_KEY
|
||||
github_app_id=APP_ID, github_app_key_content=APP_KEY
|
||||
)
|
||||
|
||||
assert isinstance(connection, Connection)
|
||||
|
||||
@@ -61,7 +61,7 @@ class Test_GitHubArguments:
|
||||
arguments.init_parser(mock_github_args)
|
||||
|
||||
# Verify authentication arguments were added
|
||||
assert self.mock_auth_group.add_argument.call_count == 4
|
||||
assert self.mock_auth_group.add_argument.call_count == 5
|
||||
|
||||
# Check that all authentication arguments are present
|
||||
calls = self.mock_auth_group.add_argument.call_args_list
|
||||
@@ -76,8 +76,8 @@ class Test_GitHubArguments:
|
||||
assert "--personal-access-token" in auth_args
|
||||
assert "--oauth-app-token" in auth_args
|
||||
assert "--github-app-id" in auth_args
|
||||
assert "--github-app-key-path" in auth_args
|
||||
assert "--github-app-key" in auth_args
|
||||
assert "--github-app-key-path" in auth_args
|
||||
|
||||
def test_init_parser_adds_scoping_arguments(self):
|
||||
"""Test that init_parser adds all scoping arguments"""
|
||||
@@ -309,3 +309,81 @@ class Test_GitHubArguments_Integration:
|
||||
assert args.personal_access_token == "test-token"
|
||||
assert args.repository == []
|
||||
assert args.organization == []
|
||||
|
||||
|
||||
class Test_GitHubArguments_Validation:
|
||||
def test_validate_arguments_both_key_methods_provided(self):
|
||||
"""Test validation fails when both key methods are provided"""
|
||||
from argparse import Namespace
|
||||
|
||||
args = Namespace()
|
||||
args.github_app_key = "key-content"
|
||||
args.github_app_key_path = "/path/to/key.pem"
|
||||
args.github_app_id = "12345"
|
||||
|
||||
is_valid, error_msg = arguments.validate_arguments(args)
|
||||
|
||||
assert not is_valid
|
||||
assert (
|
||||
"Cannot specify both --github-app-key and --github-app-key-path simultaneously"
|
||||
in error_msg
|
||||
)
|
||||
|
||||
def test_validate_arguments_app_id_without_key(self):
|
||||
"""Test validation fails when app ID is provided without any key"""
|
||||
from argparse import Namespace
|
||||
|
||||
args = Namespace()
|
||||
args.github_app_key = None
|
||||
args.github_app_key_path = None
|
||||
args.github_app_id = "12345"
|
||||
|
||||
is_valid, error_msg = arguments.validate_arguments(args)
|
||||
|
||||
assert not is_valid
|
||||
assert (
|
||||
"GitHub App ID requires either --github-app-key or --github-app-key-path"
|
||||
in error_msg
|
||||
)
|
||||
|
||||
def test_validate_arguments_valid_key_content(self):
|
||||
"""Test validation passes with valid key content"""
|
||||
from argparse import Namespace
|
||||
|
||||
args = Namespace()
|
||||
args.github_app_key = "-----BEGIN RSA PRIVATE KEY-----"
|
||||
args.github_app_key_path = None
|
||||
args.github_app_id = "12345"
|
||||
|
||||
is_valid, error_msg = arguments.validate_arguments(args)
|
||||
|
||||
assert is_valid
|
||||
assert error_msg == ""
|
||||
|
||||
def test_validate_arguments_valid_key_path(self):
|
||||
"""Test validation passes with valid key path"""
|
||||
from argparse import Namespace
|
||||
|
||||
args = Namespace()
|
||||
args.github_app_key = None
|
||||
args.github_app_key_path = "/path/to/key.pem"
|
||||
args.github_app_id = "12345"
|
||||
|
||||
is_valid, error_msg = arguments.validate_arguments(args)
|
||||
|
||||
assert is_valid
|
||||
assert error_msg == ""
|
||||
|
||||
def test_validate_arguments_no_app_auth(self):
|
||||
"""Test validation passes when no app authentication is used"""
|
||||
from argparse import Namespace
|
||||
|
||||
args = Namespace()
|
||||
args.github_app_key = None
|
||||
args.github_app_key_path = None
|
||||
args.github_app_id = None
|
||||
|
||||
is_valid, error_msg = arguments.validate_arguments(args)
|
||||
|
||||
assert is_valid
|
||||
assert error_msg == ""
|
||||
|
||||
Reference in New Issue
Block a user