prismor 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prismor
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A CLI tool for scanning GitHub repositories for vulnerabilities, secrets, and generating SBOMs
5
5
  Home-page: https://github.com/PrismorSec/prismor-cli
6
6
  Author: Prismor
@@ -1,6 +1,6 @@
1
1
  """Prismor CLI - Security scanning tool for GitHub repositories."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.1"
4
4
  __author__ = "Prismor"
5
5
  __description__ = "A CLI tool for scanning GitHub repositories for vulnerabilities, secrets, and generating SBOMs"
6
6
 
@@ -26,12 +26,52 @@ class PrismorClient:
26
26
  "Please set it with: export PRISMOR_API_KEY=your_api_key"
27
27
  )
28
28
 
29
- self.base_url = "https://api.prismor.dev"
29
+ # self.base_url = "http://localhost:3000"
30
+ self.base_url = "https://prismor.dev"
30
31
  self.headers = {
31
32
  "Authorization": f"Bearer {self.api_key}",
32
33
  "Content-Type": "application/json"
33
34
  }
34
35
 
36
+ def authenticate(self) -> Dict[str, Any]:
37
+ """Authenticate with the Prismor API using the API key.
38
+
39
+ Returns:
40
+ Dictionary containing user information and repositories
41
+
42
+ Raises:
43
+ PrismorAPIError: If authentication fails
44
+ """
45
+ try:
46
+ response = requests.post(
47
+ f"{self.base_url}/api/cli/auth",
48
+ json={"apiKey": self.api_key},
49
+ headers={"Content-Type": "application/json"},
50
+ timeout=30
51
+ )
52
+
53
+ if response.status_code == 401:
54
+ raise PrismorAPIError("Invalid API key. Please check your PRISMOR_API_KEY.")
55
+
56
+ if response.status_code == 400:
57
+ raise PrismorAPIError("API key is required.")
58
+
59
+ if response.status_code >= 400:
60
+ error_msg = response.json().get("error", "Authentication failed")
61
+ raise PrismorAPIError(f"Authentication error: {error_msg}")
62
+
63
+ response.raise_for_status()
64
+ return response.json()
65
+
66
+ except requests.exceptions.Timeout:
67
+ raise PrismorAPIError("Authentication request timed out.")
68
+ except requests.exceptions.ConnectionError:
69
+ raise PrismorAPIError(
70
+ "Failed to connect to Prismor API. Please check your internet connection."
71
+ )
72
+ except requests.exceptions.RequestException as e:
73
+ raise PrismorAPIError(f"Authentication request failed: {str(e)}")
74
+
35
75
  def normalize_repo_url(self, repo: str) -> str:
36
76
  """Normalize repository input to a full GitHub URL.
37
77
 
@@ -59,7 +99,8 @@ class PrismorClient:
59
99
  vex: bool = False,
60
100
  sbom: bool = False,
61
101
  detect_secret: bool = False,
62
- fullscan: bool = False
102
+ fullscan: bool = False,
103
+ branch: Optional[str] = None
63
104
  ) -> Dict[str, Any]:
64
105
  """Perform security scan on a GitHub repository.
65
106
 
@@ -69,41 +110,59 @@ class PrismorClient:
69
110
  sbom: Enable SBOM generation
70
111
  detect_secret: Enable secret detection
71
112
  fullscan: Enable all scan types
113
+ branch: Specific branch to scan (defaults to main/master)
72
114
 
73
115
  Returns:
74
116
  Dictionary containing scan results
75
117
  """
76
118
  repo_url = self.normalize_repo_url(repo)
77
119
 
78
- # Prepare request payload
120
+ # First authenticate to get user info
121
+ auth_response = self.authenticate()
122
+ user_info = auth_response.get("user", {})
123
+
124
+ # Prepare request payload for CLI scan
79
125
  payload = {
80
126
  "repo_url": repo_url,
127
+ "api_key": self.api_key,
81
128
  "vex": vex or fullscan,
82
129
  "sbom": sbom or fullscan,
83
130
  "detect_secret": detect_secret or fullscan,
84
- "fullscan": fullscan
131
+ "fullscan": fullscan,
132
+ "branch": branch
85
133
  }
86
134
 
87
135
  try:
88
136
  response = requests.post(
89
- f"{self.base_url}/scan",
137
+ f"{self.base_url}/api/cli/scan",
90
138
  json=payload,
91
- headers=self.headers,
139
+ headers={"Content-Type": "application/json"},
92
140
  timeout=300 # 5 minute timeout
93
141
  )
94
142
 
95
143
  if response.status_code == 401:
144
+ error_data = response.json()
145
+ if error_data.get("action") == "integrate_github":
146
+ raise PrismorAPIError(
147
+ f"{error_data.get('message', 'GitHub integration required')}\n"
148
+ f"Please visit: {error_data.get('integration_url', 'https://prismor.dev/dashboard')}"
149
+ )
96
150
  raise PrismorAPIError("Invalid API key. Please check your PRISMOR_API_KEY.")
97
151
 
98
152
  if response.status_code == 404:
99
- raise PrismorAPIError("API endpoint not found. Please check the API URL.")
153
+ raise PrismorAPIError("CLI scan endpoint not found. Please check if CLI endpoints are available.")
100
154
 
101
155
  if response.status_code >= 400:
102
156
  error_msg = response.json().get("error", "Unknown error")
103
157
  raise PrismorAPIError(f"API error: {error_msg}")
104
158
 
105
159
  response.raise_for_status()
106
- return response.json()
160
+ result = response.json()
161
+
162
+ # Handle the new response format from CLI endpoint
163
+ if result.get("ok") and "results" in result:
164
+ return result["results"]
165
+ return result
107
166
 
108
167
  except requests.exceptions.Timeout:
109
168
  raise PrismorAPIError(
@@ -115,4 +174,24 @@ class PrismorClient:
115
174
  )
116
175
  except requests.exceptions.RequestException as e:
117
176
  raise PrismorAPIError(f"Request failed: {str(e)}")
177
+
178
+ def get_repositories(self) -> Dict[str, Any]:
179
+ """Get user's repositories.
180
+
181
+ Returns:
182
+ Dictionary containing user repositories
183
+
184
+ Raises:
185
+ PrismorAPIError: If request fails
186
+ """
187
+ auth_response = self.authenticate()
188
+ user_info = auth_response.get("user", {})
189
+ return {
190
+ "repositories": user_info.get("repositories", []),
191
+ "user": {
192
+ "id": user_info.get("id"),
193
+ "email": user_info.get("email"),
194
+ "name": user_info.get("name")
195
+ }
196
+ }
118
197
 
@@ -0,0 +1,316 @@
1
+ """Command-line interface for Prismor security scanning tool."""
2
+
3
+ import sys
4
+ import json
5
+ import click
6
+ from typing import Optional
7
+ from .api import PrismorClient, PrismorAPIError
8
+
9
+
10
+ def print_success(message: str):
11
+ """Print success message in green."""
12
+ click.secho(f"✓ {message}", fg="green")
13
+
14
+
15
+ def print_error(message: str):
16
+ """Print error message in red."""
17
+ click.secho(f"✗ {message}", fg="red", err=True)
18
+
19
+
20
+ def print_info(message: str):
21
+ """Print info message in blue."""
22
+ click.secho(f"ℹ {message}", fg="blue")
23
+
24
+
25
+ def print_warning(message: str):
26
+ """Print warning message in yellow."""
27
+ click.secho(f"⚠ {message}", fg="yellow")
28
+
29
+
30
+ def format_scan_results(results: dict, scan_type: str):
31
+ """Format and display scan results."""
32
+ click.echo("\n" + "=" * 60)
33
+ click.secho(f" Scan Results - {scan_type}", fg="cyan", bold=True)
34
+ click.echo("=" * 60 + "\n")
35
+
36
+ # Display repository information
37
+ if "repository" in results:
38
+ click.secho("Repository:", fg="yellow", bold=True)
39
+ click.echo(f" {results['repository']}\n")
40
+
41
+ if "branch" in results:
42
+ click.secho("Branch:", fg="yellow", bold=True)
43
+ click.echo(f" {results['branch']}\n")
44
+
45
+ if "commit_sha" in results:
46
+ click.secho("Commit SHA:", fg="yellow", bold=True)
47
+ click.echo(f" {results['commit_sha']}\n")
48
+
49
+ # Display scan results for each scan type
50
+ if "scans" in results:
51
+ scans = results["scans"]
52
+
53
+ # Vulnerability scan results
54
+ if "vulnerability" in scans:
55
+ vuln_scan = scans["vulnerability"]
56
+ click.secho("Vulnerability Scan:", fg="yellow", bold=True)
57
+ status_color = "green" if vuln_scan.get("status") == "success" else "red"
58
+ click.secho(f" Status: {vuln_scan.get('status', 'unknown')}", fg=status_color)
59
+
60
+ if "scan_results" in vuln_scan:
61
+ scan_data = vuln_scan["scan_results"]
62
+ if "vulnerabilities" in scan_data or "Results" in scan_data:
63
+ vuln_data = scan_data.get("vulnerabilities", scan_data.get("Results", []))
64
+ if isinstance(vuln_data, list):
65
+ click.echo(f" Vulnerabilities Found: {len(vuln_data)}")
66
+ else:
67
+ click.echo(f" Vulnerabilities: Data available")
68
+
69
+ if "public_url" in vuln_scan:
70
+ click.echo(f" Results URL: {vuln_scan['public_url']}")
71
+ click.echo()
72
+
73
+ # SBOM results
74
+ if "sbom" in scans:
75
+ sbom_scan = scans["sbom"]
76
+ click.secho("SBOM Generation:", fg="yellow", bold=True)
77
+ status_color = "green" if sbom_scan.get("status") == "success" else "red"
78
+ click.secho(f" Status: {sbom_scan.get('status', 'unknown')}", fg=status_color)
79
+
80
+ if "sbom" in sbom_scan:
81
+ sbom_data = sbom_scan["sbom"]
82
+ if isinstance(sbom_data, list):
83
+ click.echo(f" Artifacts Found: {len(sbom_data)}")
84
+ else:
85
+ click.echo(f" SBOM: Data available")
86
+
87
+ if "public_url" in sbom_scan:
88
+ click.echo(f" Results URL: {sbom_scan['public_url']}")
89
+ click.echo()
90
+
91
+ # Secret scan results
92
+ if "secret" in scans:
93
+ secret_scan = scans["secret"]
94
+ click.secho("Secret Detection:", fg="yellow", bold=True)
95
+ status_color = "green" if secret_scan.get("status") == "success" else "red"
96
+ click.secho(f" Status: {secret_scan.get('status', 'unknown')}", fg=status_color)
97
+
98
+ if "summary" in secret_scan:
99
+ summary = secret_scan["summary"]
100
+ if isinstance(summary, dict):
101
+ for key, value in summary.items():
102
+ click.echo(f" {key}: {value}")
103
+ else:
104
+ click.echo(f" Summary: {summary}")
105
+
106
+ if "public_url" in secret_scan:
107
+ click.echo(f" Results URL: {secret_scan['public_url']}")
108
+ click.echo()
109
+
110
+ # Display scan timestamp
111
+ if "scanned_at" in results:
112
+ click.secho("Scanned At:", fg="yellow", bold=True)
113
+ click.echo(f" {results['scanned_at']}\n")
114
+
115
+ click.echo("=" * 60 + "\n")
116
+
117
+
118
+ @click.group(invoke_without_command=True)
119
+ @click.option(
120
+ "--scan",
121
+ type=str,
122
+ help="Repository to scan (username/repo or full GitHub URL)"
123
+ )
124
+ @click.option("--vex", is_flag=True, help="Perform vulnerability scanning")
125
+ @click.option("--sbom", is_flag=True, help="Generate Software Bill of Materials")
126
+ @click.option("--detect-secret", is_flag=True, help="Detect secrets in repository")
127
+ @click.option("--fullscan", is_flag=True, help="Perform all scan types")
128
+ @click.option("--branch", type=str, help="Specific branch to scan (defaults to main/master)")
129
+ @click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
130
+ @click.version_option(version="0.1.0", prog_name="prismor")
131
+ @click.pass_context
132
+ def cli(ctx, scan: Optional[str], vex: bool, sbom: bool, detect_secret: bool,
133
+ fullscan: bool, branch: Optional[str], output_json: bool):
134
+ """Prismor CLI - Security scanning tool for GitHub repositories.
135
+
136
+ Examples:
137
+ prismor --scan username/repo --vex
138
+ prismor --scan username/repo --fullscan
139
+ prismor --scan https://github.com/username/repo --detect-secret
140
+ prismor --scan username/repo --sbom --branch develop
141
+ prismor status
142
+ prismor repos
143
+ """
144
+ # If no command and no scan option, show help
145
+ if ctx.invoked_subcommand is None and not scan:
146
+ click.echo(ctx.get_help())
147
+ return
148
+
149
+ # If scan option is provided, perform the scan
150
+ if scan:
151
+ # Check if at least one scan type is selected
152
+ if not any([vex, sbom, detect_secret, fullscan]):
153
+ print_error("Please specify at least one scan type: --vex, --sbom, --detect-secret, or --fullscan")
154
+ sys.exit(1)
155
+
156
+ try:
157
+ # Initialize API client
158
+ print_info(f"Initializing Prismor scan for: {scan}")
159
+ client = PrismorClient()
160
+
161
+ # Determine scan type for display
162
+ scan_types = []
163
+ if fullscan:
164
+ scan_types.append("Full Scan (VEX + SBOM + Secret Detection)")
165
+ else:
166
+ if vex:
167
+ scan_types.append("VEX")
168
+ if sbom:
169
+ scan_types.append("SBOM")
170
+ if detect_secret:
171
+ scan_types.append("Secret Detection")
172
+
173
+ print_info(f"Scan type: {', '.join(scan_types)}")
174
+ print_info("Starting scan... (this may take a few minutes)")
175
+
176
+ # Perform scan
177
+ results = client.scan(
178
+ repo=scan,
179
+ vex=vex,
180
+ sbom=sbom,
181
+ detect_secret=detect_secret,
182
+ fullscan=fullscan,
183
+ branch=branch
184
+ )
185
+
186
+ # Output results
187
+ if output_json:
188
+ click.echo(json.dumps(results, indent=2))
189
+ else:
190
+ print_success("Scan completed successfully!")
191
+ format_scan_results(results, ', '.join(scan_types))
192
+
193
+ except PrismorAPIError as e:
194
+ print_error(str(e))
195
+ sys.exit(1)
196
+ except Exception as e:
197
+ print_error(f"Unexpected error: {str(e)}")
198
+ sys.exit(1)
199
+
200
+
201
+ @cli.command()
202
+ def version():
203
+ """Display the version of Prismor CLI."""
204
+ click.echo("Prismor CLI v0.1.0")
205
+
206
+
207
+ @cli.command()
208
+ def config():
209
+ """Display current configuration."""
210
+ import os
211
+
212
+ click.echo("\n" + "=" * 60)
213
+ click.secho(" Prismor CLI Configuration", fg="cyan", bold=True)
214
+ click.echo("=" * 60 + "\n")
215
+
216
+ # Check API key
217
+ api_key = os.environ.get("PRISMOR_API_KEY")
218
+ if api_key:
219
+ # Show only first and last 4 characters
220
+ masked_key = f"{api_key[:4]}...{api_key[-4:]}" if len(api_key) > 8 else "***"
221
+ print_success(f"PRISMOR_API_KEY: {masked_key}")
222
+ else:
223
+ print_error("PRISMOR_API_KEY: Not set")
224
+ click.echo("\nTo set your API key, run:")
225
+ click.echo(" export PRISMOR_API_KEY=your_api_key")
226
+
227
+ # click.echo("\nAPI Endpoint: http://localhost:3000")
228
+ click.echo("=" * 60 + "\n")
229
+
230
+
231
+ @cli.command()
232
+ def repos():
233
+ """List your connected repositories."""
234
+ try:
235
+ client = PrismorClient()
236
+ repos_data = client.get_repositories()
237
+
238
+ click.echo("\n" + "=" * 60)
239
+ click.secho(" Your Repositories", fg="cyan", bold=True)
240
+ click.echo("=" * 60 + "\n")
241
+
242
+ user_info = repos_data.get("user", {})
243
+ if user_info:
244
+ click.secho(f"User: {user_info.get('name', 'Unknown')} ({user_info.get('email', 'No email')})", fg="yellow")
245
+ click.echo()
246
+
247
+ repositories = repos_data.get("repositories", [])
248
+ if repositories:
249
+ for repo in repositories:
250
+ click.secho(f"• {repo.get('name', 'Unknown')}", fg="green")
251
+ click.echo(f" URL: {repo.get('htmlUrl', 'No URL')}")
252
+ click.echo(f" Owner: {repo.get('githubOwner', 'Unknown')}")
253
+ click.echo()
254
+ else:
255
+ print_warning("No repositories found. Connect repositories through the web interface.")
256
+
257
+ click.echo("=" * 60 + "\n")
258
+
259
+ except PrismorAPIError as e:
260
+ print_error(str(e))
261
+ sys.exit(1)
262
+ except Exception as e:
263
+ print_error(f"Unexpected error: {str(e)}")
264
+ sys.exit(1)
265
+
266
+
267
+ @cli.command()
268
+ def status():
269
+ """Check your account status and GitHub integration."""
270
+ try:
271
+ client = PrismorClient()
272
+ auth_data = client.authenticate()
273
+
274
+ click.echo("\n" + "=" * 60)
275
+ click.secho(" Account Status", fg="cyan", bold=True)
276
+ click.echo("=" * 60 + "\n")
277
+
278
+ user_info = auth_data.get("user", {})
279
+ if user_info:
280
+ click.secho(f"User: {user_info.get('name', 'Unknown')} ({user_info.get('email', 'No email')})", fg="yellow")
281
+ click.echo()
282
+
283
+ repositories = user_info.get("repositories", [])
284
+ click.secho(f"Connected Repositories: {len(repositories)}", fg="green")
285
+
286
+ if repositories:
287
+ click.echo("\nRepository List:")
288
+ for repo in repositories:
289
+ click.echo(f" • {repo.get('name', 'Unknown')} ({repo.get('htmlUrl', 'No URL')})")
290
+ else:
291
+ print_warning("No repositories connected.")
292
+ click.echo("\nTo connect repositories:")
293
+ click.echo(" 1. Visit https://prismor.dev/dashboard")
294
+ click.echo(" 2. Connect your GitHub account")
295
+ click.echo(" 3. Select repositories to scan")
296
+ else:
297
+ print_error("Failed to retrieve account information.")
298
+
299
+ click.echo("\n" + "=" * 60 + "\n")
300
+
301
+ except PrismorAPIError as e:
302
+ print_error(str(e))
303
+ sys.exit(1)
304
+ except Exception as e:
305
+ print_error(f"Unexpected error: {str(e)}")
306
+ sys.exit(1)
307
+
308
+
309
+ def main():
310
+ """Entry point for the CLI."""
311
+ cli()
312
+
313
+
314
+ if __name__ == "__main__":
315
+ main()
316
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prismor
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: A CLI tool for scanning GitHub repositories for vulnerabilities, secrets, and generating SBOMs
5
5
  Home-page: https://github.com/PrismorSec/prismor-cli
6
6
  Author: Prismor
@@ -17,7 +17,7 @@ if os.path.exists("README.md"):
17
17
 
18
18
  setup(
19
19
  name="prismor",
20
- version="0.1.0",
20
+ version="0.1.1",
21
21
  author="Prismor",
22
22
  author_email="support@prismor.dev",
23
23
  description="A CLI tool for scanning GitHub repositories for vulnerabilities, secrets, and generating SBOMs",
@@ -1,210 +0,0 @@
1
- """Command-line interface for Prismor security scanning tool."""
2
-
3
- import sys
4
- import json
5
- import click
6
- from typing import Optional
7
- from .api import PrismorClient, PrismorAPIError
8
-
9
-
10
- def print_success(message: str):
11
- """Print success message in green."""
12
- click.secho(f"✓ {message}", fg="green")
13
-
14
-
15
- def print_error(message: str):
16
- """Print error message in red."""
17
- click.secho(f"✗ {message}", fg="red", err=True)
18
-
19
-
20
- def print_info(message: str):
21
- """Print info message in blue."""
22
- click.secho(f"ℹ {message}", fg="blue")
23
-
24
-
25
- def print_warning(message: str):
26
- """Print warning message in yellow."""
27
- click.secho(f"⚠ {message}", fg="yellow")
28
-
29
-
30
- def format_scan_results(results: dict, scan_type: str):
31
- """Format and display scan results."""
32
- click.echo("\n" + "=" * 60)
33
- click.secho(f" Scan Results - {scan_type}", fg="cyan", bold=True)
34
- click.echo("=" * 60 + "\n")
35
-
36
- # Display repository information
37
- if "repository" in results:
38
- click.secho("Repository:", fg="yellow", bold=True)
39
- click.echo(f" {results['repository']}\n")
40
-
41
- # Display scan status
42
- if "status" in results:
43
- status_color = "green" if results["status"] == "success" else "red"
44
- click.secho(f"Status: ", fg="yellow", bold=True, nl=False)
45
- click.secho(results["status"], fg=status_color)
46
- click.echo()
47
-
48
- # Display scan results based on type
49
- if "scan_results" in results:
50
- scan_data = results["scan_results"]
51
-
52
- # Vulnerability scan results
53
- if "vulnerabilities" in scan_data or "Results" in scan_data:
54
- click.secho("Vulnerabilities Found:", fg="yellow", bold=True)
55
- vuln_data = scan_data.get("vulnerabilities", scan_data.get("Results", []))
56
- if isinstance(vuln_data, list):
57
- click.echo(f" Total: {len(vuln_data)}")
58
- else:
59
- click.echo(f" Data available in detailed output")
60
- click.echo()
61
-
62
- # Secret scan results
63
- if "secrets" in scan_data or "findings_summary" in scan_data:
64
- click.secho("Secrets Detected:", fg="yellow", bold=True)
65
- secrets = scan_data.get("secrets", scan_data.get("findings_summary", {}))
66
- if isinstance(secrets, dict):
67
- for key, value in secrets.items():
68
- click.echo(f" {key}: {value}")
69
- else:
70
- click.echo(f" {len(secrets) if isinstance(secrets, list) else 'Data available'}")
71
- click.echo()
72
-
73
- # SBOM results
74
- if "sbom" in scan_data or "artifacts" in scan_data:
75
- click.secho("SBOM Generated:", fg="yellow", bold=True)
76
- sbom_data = scan_data.get("sbom", scan_data.get("artifacts", []))
77
- if isinstance(sbom_data, list):
78
- click.echo(f" Total artifacts: {len(sbom_data)}")
79
- else:
80
- click.echo(f" SBOM data available")
81
- click.echo()
82
-
83
- # Display result URLs if available
84
- if "public_url" in results:
85
- click.secho("Results URL:", fg="yellow", bold=True)
86
- click.echo(f" {results['public_url']}\n")
87
-
88
- if "presigned_url" in results:
89
- click.secho("Download URL:", fg="yellow", bold=True)
90
- click.echo(f" {results['presigned_url']}\n")
91
-
92
- click.echo("=" * 60 + "\n")
93
-
94
-
95
- @click.group(invoke_without_command=True)
96
- @click.option(
97
- "--scan",
98
- type=str,
99
- help="Repository to scan (username/repo or full GitHub URL)"
100
- )
101
- @click.option("--vex", is_flag=True, help="Perform vulnerability scanning")
102
- @click.option("--sbom", is_flag=True, help="Generate Software Bill of Materials")
103
- @click.option("--detect-secret", is_flag=True, help="Detect secrets in repository")
104
- @click.option("--fullscan", is_flag=True, help="Perform all scan types")
105
- @click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
106
- @click.version_option(version="0.1.0", prog_name="prismor")
107
- @click.pass_context
108
- def cli(ctx, scan: Optional[str], vex: bool, sbom: bool, detect_secret: bool,
109
- fullscan: bool, output_json: bool):
110
- """Prismor CLI - Security scanning tool for GitHub repositories.
111
-
112
- Examples:
113
- prismor --scan username/repo --vex
114
- prismor --scan username/repo --fullscan
115
- prismor --scan https://github.com/username/repo --detect-secret
116
- """
117
- # If no command and no scan option, show help
118
- if ctx.invoked_subcommand is None and not scan:
119
- click.echo(ctx.get_help())
120
- return
121
-
122
- # If scan option is provided, perform the scan
123
- if scan:
124
- # Check if at least one scan type is selected
125
- if not any([vex, sbom, detect_secret, fullscan]):
126
- print_error("Please specify at least one scan type: --vex, --sbom, --detect-secret, or --fullscan")
127
- sys.exit(1)
128
-
129
- try:
130
- # Initialize API client
131
- print_info(f"Initializing Prismor scan for: {scan}")
132
- client = PrismorClient()
133
-
134
- # Determine scan type for display
135
- scan_types = []
136
- if fullscan:
137
- scan_types.append("Full Scan (VEX + SBOM + Secret Detection)")
138
- else:
139
- if vex:
140
- scan_types.append("VEX")
141
- if sbom:
142
- scan_types.append("SBOM")
143
- if detect_secret:
144
- scan_types.append("Secret Detection")
145
-
146
- print_info(f"Scan type: {', '.join(scan_types)}")
147
- print_info("Starting scan... (this may take a few minutes)")
148
-
149
- # Perform scan
150
- results = client.scan(
151
- repo=scan,
152
- vex=vex,
153
- sbom=sbom,
154
- detect_secret=detect_secret,
155
- fullscan=fullscan
156
- )
157
-
158
- # Output results
159
- if output_json:
160
- click.echo(json.dumps(results, indent=2))
161
- else:
162
- print_success("Scan completed successfully!")
163
- format_scan_results(results, ', '.join(scan_types))
164
-
165
- except PrismorAPIError as e:
166
- print_error(str(e))
167
- sys.exit(1)
168
- except Exception as e:
169
- print_error(f"Unexpected error: {str(e)}")
170
- sys.exit(1)
171
-
172
-
173
- @cli.command()
174
- def version():
175
- """Display the version of Prismor CLI."""
176
- click.echo("Prismor CLI v0.1.0")
177
-
178
-
179
- @cli.command()
180
- def config():
181
- """Display current configuration."""
182
- import os
183
-
184
- click.echo("\n" + "=" * 60)
185
- click.secho(" Prismor CLI Configuration", fg="cyan", bold=True)
186
- click.echo("=" * 60 + "\n")
187
-
188
- # Check API key
189
- api_key = os.environ.get("PRISMOR_API_KEY")
190
- if api_key:
191
- # Show only first and last 4 characters
192
- masked_key = f"{api_key[:4]}...{api_key[-4:]}" if len(api_key) > 8 else "***"
193
- print_success(f"PRISMOR_API_KEY: {masked_key}")
194
- else:
195
- print_error("PRISMOR_API_KEY: Not set")
196
- click.echo("\nTo set your API key, run:")
197
- click.echo(" export PRISMOR_API_KEY=your_api_key")
198
-
199
- click.echo("\nAPI Endpoint: https://api.prismor.dev")
200
- click.echo("=" * 60 + "\n")
201
-
202
-
203
- def main():
204
- """Entry point for the CLI."""
205
- cli()
206
-
207
-
208
- if __name__ == "__main__":
209
- main()
210
-
File without changes
File without changes
File without changes
File without changes
File without changes