prismor 1.0.5__tar.gz → 1.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: 1.0.5
3
+ Version: 1.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
@@ -280,6 +280,33 @@ prismor --repo git@github.com:nodejs/node.git --detect-secret --sbom
280
280
  prismor --repo www.github.com/vercel/next.js --fullscan
281
281
  ```
282
282
 
283
+ ### Example 7: Save Results to File
284
+
285
+ ```bash
286
+ # Save full scan results to JSON file
287
+ prismor --repo username/repo --fullscan --output results.json
288
+
289
+ # Save vulnerability scan with specific branch
290
+ prismor --repo username/repo --scan --branch develop --output vuln-scan.json
291
+
292
+ # Quiet mode with file output (no console output)
293
+ prismor --repo username/repo --sbom --output sbom.json --quiet
294
+ ```
295
+
296
+ ### Example 8: CI/CD Integration
297
+
298
+ ```bash
299
+ # Minimal output for CI/CD pipelines
300
+ prismor --repo $REPO_NAME --scan --quiet --json > scan-results.json
301
+
302
+ # Exit with error code if scan fails
303
+ prismor --repo username/repo --fullscan --quiet || exit 1
304
+
305
+ # Save results and continue pipeline
306
+ prismor --repo username/repo --scan --output scan.json --quiet && \
307
+ echo "Scan completed, results saved to scan.json"
308
+ ```
309
+
283
310
  ### Example 7: Async Scan with Status Checking
284
311
 
285
312
  ```bash
@@ -433,43 +460,232 @@ Prismor CLI communicates with the Prismor API at `https://api.prismor.dev`. The
433
460
  - Response parsing
434
461
  - Result presentation
435
462
 
463
+ ## Advanced Usage
464
+
465
+ ### Save Results to File
466
+
467
+ Save scan results to a JSON file for later analysis:
468
+
469
+ ```bash
470
+ prismor --repo username/repo --fullscan --output results.json
471
+ ```
472
+
473
+ ### Quiet Mode
474
+
475
+ Run scans with minimal output (useful for CI/CD pipelines):
476
+
477
+ ```bash
478
+ prismor --repo username/repo --scan --quiet
479
+ ```
480
+
481
+ ### Combining Options
482
+
483
+ Combine multiple options for customized workflows:
484
+
485
+ ```bash
486
+ # Scan specific branch and save to file
487
+ prismor --repo username/repo --scan --branch develop --output scan-results.json
488
+
489
+ # Quiet mode with JSON output
490
+ prismor --repo username/repo --fullscan --quiet --json
491
+
492
+ # Save results without console output
493
+ prismor --repo username/repo --sbom --output sbom.json --quiet
494
+ ```
495
+
436
496
  ## Troubleshooting
437
497
 
438
498
  ### API Key Not Set
439
499
 
440
- If you see an error about `PRISMOR_API_KEY` not being set:
500
+ **Error:**
501
+ ```
502
+ ✗ PRISMOR_API_KEY environment variable is not set.
503
+ ```
441
504
 
505
+ **Solution:**
442
506
  ```bash
507
+ # Set temporarily (current session only)
443
508
  export PRISMOR_API_KEY=your_api_key_here
509
+
510
+ # Set permanently (add to ~/.bashrc or ~/.zshrc)
511
+ echo 'export PRISMOR_API_KEY=your_api_key_here' >> ~/.zshrc
512
+ source ~/.zshrc
444
513
  ```
445
514
 
515
+ **Get your API key:**
516
+ 1. Visit [https://prismor.dev/cli](https://prismor.dev/cli)
517
+ 2. Sign up for a free account
518
+ 3. Generate an API key from your dashboard
519
+
520
+ ---
521
+
446
522
  ### Invalid Repository Format
447
523
 
448
- Ensure your repository is in one of the supported formats:
524
+ **Error:**
525
+ ```
526
+ ✗ Unrecognized repository format
527
+ ```
449
528
 
450
529
  **Supported formats:**
451
- - `username/repository` (recommended)
452
- - `https://github.com/username/repository`
453
- - `https://www.github.com/username/repository`
454
- - `http://github.com/username/repository`
455
- - `http://www.github.com/username/repository`
456
- - `github.com/username/repository`
457
- - `www.github.com/username/repository`
458
- - `git@github.com:username/repository.git`
459
- - `https://github.com/username/repository/tree/branch`
460
- - `https://github.com/username/repository/blob/branch/file`
530
+ - `username/repository` (recommended)
531
+ - `https://github.com/username/repository`
532
+ - `https://www.github.com/username/repository`
533
+ - `http://github.com/username/repository`
534
+ - `http://www.github.com/username/repository`
535
+ - `github.com/username/repository`
536
+ - `www.github.com/username/repository`
537
+ - `git@github.com:username/repository.git`
538
+ - `https://github.com/username/repository/tree/branch`
539
+ - `https://github.com/username/repository/blob/branch/file`
461
540
 
462
541
  **Not supported:**
463
- - Non-GitHub URLs (GitLab, Bitbucket, etc.)
464
- - Invalid URL formats
465
- - Empty or malformed repository names
542
+ - Non-GitHub URLs (GitLab, Bitbucket, etc.)
543
+ - Invalid URL formats
544
+ - Empty or malformed repository names
545
+ - ❌ Repository names with invalid characters
546
+
547
+ **Valid characters:**
548
+ - Alphanumeric (a-z, A-Z, 0-9)
549
+ - Hyphens (-)
550
+ - Underscores (_)
551
+ - Dots (.)
552
+ - Cannot start or end with special characters
553
+
554
+ ---
466
555
 
467
556
  ### Connection Issues
468
557
 
469
- If you experience connection issues:
470
- 1. Check your internet connection
471
- 2. Verify the API endpoint is accessible
472
- 3. Ensure your API key is valid
558
+ **Error:**
559
+ ```
560
+ Failed to connect to Prismor API
561
+ ```
562
+
563
+ **Solutions:**
564
+
565
+ 1. **Check Internet Connection**
566
+ ```bash
567
+ ping prismor.dev
568
+ ```
569
+
570
+ 2. **Verify API Endpoint**
571
+ ```bash
572
+ curl -I https://prismor.dev
573
+ ```
574
+
575
+ 3. **Test API Key**
576
+ ```bash
577
+ prismor config
578
+ ```
579
+
580
+ 4. **Check Firewall/Proxy**
581
+ - Ensure your firewall allows HTTPS connections
582
+ - Configure proxy if needed:
583
+ ```bash
584
+ export HTTPS_PROXY=http://proxy.example.com:8080
585
+ ```
586
+
587
+ 5. **Retry with Automatic Retries**
588
+ - The CLI automatically retries failed requests 3 times with exponential backoff
589
+ - If issues persist, check your network configuration
590
+
591
+ ---
592
+
593
+ ### Timeout Issues
594
+
595
+ **Error:**
596
+ ```
597
+ ✗ Request timed out
598
+ ```
599
+
600
+ **Solutions:**
601
+
602
+ 1. **Large Repositories**
603
+ - Vulnerability scans can take up to 10 minutes for large repositories
604
+ - The CLI will wait automatically
605
+ - Use `--quiet` mode to reduce output during long scans
606
+
607
+ 2. **Network Latency**
608
+ - Check your internet speed
609
+ - Try again during off-peak hours
610
+ - Consider using a wired connection
611
+
612
+ 3. **Check Scan Status**
613
+ ```bash
614
+ # Start scan asynchronously
615
+ prismor start-scan username/repo
616
+
617
+ # Check status later
618
+ prismor scan-status <job_id>
619
+ ```
620
+
621
+ ---
622
+
623
+ ### Private Repository Access
624
+
625
+ **Error:**
626
+ ```
627
+ ✗ GitHub integration required
628
+ ```
629
+
630
+ **Solution:**
631
+ 1. Visit [https://prismor.dev/dashboard](https://prismor.dev/dashboard)
632
+ 2. Navigate to Settings → Integrations
633
+ 3. Connect your GitHub account
634
+ 4. Authorize Prismor to access private repositories
635
+ 5. Try scanning again
636
+
637
+ ---
638
+
639
+ ### Invalid Characters in Repository Name
640
+
641
+ **Error:**
642
+ ```
643
+ ✗ Invalid Username: 'user@name'. Must contain only alphanumeric characters...
644
+ ```
645
+
646
+ **Solution:**
647
+ - Ensure repository name follows GitHub naming conventions
648
+ - Remove special characters like `@`, `#`, `$`, etc.
649
+ - Valid example: `username/my-repo-name`
650
+ - Invalid example: `user@name/repo#123`
651
+
652
+ ---
653
+
654
+ ### Rate Limiting
655
+
656
+ **Error:**
657
+ ```
658
+ ✗ API error: Rate limit exceeded
659
+ ```
660
+
661
+ **Solution:**
662
+ 1. Wait a few minutes before retrying
663
+ 2. Check your account limits at [prismor.dev/dashboard](https://prismor.dev/dashboard)
664
+ 3. Upgrade your plan if needed for higher limits
665
+
666
+ ---
667
+
668
+ ### Getting Help
669
+
670
+ If you're still experiencing issues:
671
+
672
+ 1. **Check Configuration**
673
+ ```bash
674
+ prismor config
675
+ ```
676
+
677
+ 2. **View Account Status**
678
+ ```bash
679
+ prismor status
680
+ ```
681
+
682
+ 3. **Enable Verbose Output**
683
+ - Remove `--quiet` flag to see detailed error messages
684
+
685
+ 4. **Contact Support**
686
+ - Visit [https://prismor.dev](https://prismor.dev)
687
+ - Check documentation at [https://docs.prismor.dev](https://docs.prismor.dev)
688
+ - Report issues at [GitHub Issues](https://github.com/PrismorSec/prismor-cli/issues)
473
689
 
474
690
  ## Development
475
691
 
@@ -238,6 +238,33 @@ prismor --repo git@github.com:nodejs/node.git --detect-secret --sbom
238
238
  prismor --repo www.github.com/vercel/next.js --fullscan
239
239
  ```
240
240
 
241
+ ### Example 7: Save Results to File
242
+
243
+ ```bash
244
+ # Save full scan results to JSON file
245
+ prismor --repo username/repo --fullscan --output results.json
246
+
247
+ # Save vulnerability scan with specific branch
248
+ prismor --repo username/repo --scan --branch develop --output vuln-scan.json
249
+
250
+ # Quiet mode with file output (no console output)
251
+ prismor --repo username/repo --sbom --output sbom.json --quiet
252
+ ```
253
+
254
+ ### Example 8: CI/CD Integration
255
+
256
+ ```bash
257
+ # Minimal output for CI/CD pipelines
258
+ prismor --repo $REPO_NAME --scan --quiet --json > scan-results.json
259
+
260
+ # Exit with error code if scan fails
261
+ prismor --repo username/repo --fullscan --quiet || exit 1
262
+
263
+ # Save results and continue pipeline
264
+ prismor --repo username/repo --scan --output scan.json --quiet && \
265
+ echo "Scan completed, results saved to scan.json"
266
+ ```
267
+
241
268
  ### Example 7: Async Scan with Status Checking
242
269
 
243
270
  ```bash
@@ -391,43 +418,232 @@ Prismor CLI communicates with the Prismor API at `https://api.prismor.dev`. The
391
418
  - Response parsing
392
419
  - Result presentation
393
420
 
421
+ ## Advanced Usage
422
+
423
+ ### Save Results to File
424
+
425
+ Save scan results to a JSON file for later analysis:
426
+
427
+ ```bash
428
+ prismor --repo username/repo --fullscan --output results.json
429
+ ```
430
+
431
+ ### Quiet Mode
432
+
433
+ Run scans with minimal output (useful for CI/CD pipelines):
434
+
435
+ ```bash
436
+ prismor --repo username/repo --scan --quiet
437
+ ```
438
+
439
+ ### Combining Options
440
+
441
+ Combine multiple options for customized workflows:
442
+
443
+ ```bash
444
+ # Scan specific branch and save to file
445
+ prismor --repo username/repo --scan --branch develop --output scan-results.json
446
+
447
+ # Quiet mode with JSON output
448
+ prismor --repo username/repo --fullscan --quiet --json
449
+
450
+ # Save results without console output
451
+ prismor --repo username/repo --sbom --output sbom.json --quiet
452
+ ```
453
+
394
454
  ## Troubleshooting
395
455
 
396
456
  ### API Key Not Set
397
457
 
398
- If you see an error about `PRISMOR_API_KEY` not being set:
458
+ **Error:**
459
+ ```
460
+ ✗ PRISMOR_API_KEY environment variable is not set.
461
+ ```
399
462
 
463
+ **Solution:**
400
464
  ```bash
465
+ # Set temporarily (current session only)
401
466
  export PRISMOR_API_KEY=your_api_key_here
467
+
468
+ # Set permanently (add to ~/.bashrc or ~/.zshrc)
469
+ echo 'export PRISMOR_API_KEY=your_api_key_here' >> ~/.zshrc
470
+ source ~/.zshrc
402
471
  ```
403
472
 
473
+ **Get your API key:**
474
+ 1. Visit [https://prismor.dev/cli](https://prismor.dev/cli)
475
+ 2. Sign up for a free account
476
+ 3. Generate an API key from your dashboard
477
+
478
+ ---
479
+
404
480
  ### Invalid Repository Format
405
481
 
406
- Ensure your repository is in one of the supported formats:
482
+ **Error:**
483
+ ```
484
+ ✗ Unrecognized repository format
485
+ ```
407
486
 
408
487
  **Supported formats:**
409
- - `username/repository` (recommended)
410
- - `https://github.com/username/repository`
411
- - `https://www.github.com/username/repository`
412
- - `http://github.com/username/repository`
413
- - `http://www.github.com/username/repository`
414
- - `github.com/username/repository`
415
- - `www.github.com/username/repository`
416
- - `git@github.com:username/repository.git`
417
- - `https://github.com/username/repository/tree/branch`
418
- - `https://github.com/username/repository/blob/branch/file`
488
+ - `username/repository` (recommended)
489
+ - `https://github.com/username/repository`
490
+ - `https://www.github.com/username/repository`
491
+ - `http://github.com/username/repository`
492
+ - `http://www.github.com/username/repository`
493
+ - `github.com/username/repository`
494
+ - `www.github.com/username/repository`
495
+ - `git@github.com:username/repository.git`
496
+ - `https://github.com/username/repository/tree/branch`
497
+ - `https://github.com/username/repository/blob/branch/file`
419
498
 
420
499
  **Not supported:**
421
- - Non-GitHub URLs (GitLab, Bitbucket, etc.)
422
- - Invalid URL formats
423
- - Empty or malformed repository names
500
+ - Non-GitHub URLs (GitLab, Bitbucket, etc.)
501
+ - Invalid URL formats
502
+ - Empty or malformed repository names
503
+ - ❌ Repository names with invalid characters
504
+
505
+ **Valid characters:**
506
+ - Alphanumeric (a-z, A-Z, 0-9)
507
+ - Hyphens (-)
508
+ - Underscores (_)
509
+ - Dots (.)
510
+ - Cannot start or end with special characters
511
+
512
+ ---
424
513
 
425
514
  ### Connection Issues
426
515
 
427
- If you experience connection issues:
428
- 1. Check your internet connection
429
- 2. Verify the API endpoint is accessible
430
- 3. Ensure your API key is valid
516
+ **Error:**
517
+ ```
518
+ Failed to connect to Prismor API
519
+ ```
520
+
521
+ **Solutions:**
522
+
523
+ 1. **Check Internet Connection**
524
+ ```bash
525
+ ping prismor.dev
526
+ ```
527
+
528
+ 2. **Verify API Endpoint**
529
+ ```bash
530
+ curl -I https://prismor.dev
531
+ ```
532
+
533
+ 3. **Test API Key**
534
+ ```bash
535
+ prismor config
536
+ ```
537
+
538
+ 4. **Check Firewall/Proxy**
539
+ - Ensure your firewall allows HTTPS connections
540
+ - Configure proxy if needed:
541
+ ```bash
542
+ export HTTPS_PROXY=http://proxy.example.com:8080
543
+ ```
544
+
545
+ 5. **Retry with Automatic Retries**
546
+ - The CLI automatically retries failed requests 3 times with exponential backoff
547
+ - If issues persist, check your network configuration
548
+
549
+ ---
550
+
551
+ ### Timeout Issues
552
+
553
+ **Error:**
554
+ ```
555
+ ✗ Request timed out
556
+ ```
557
+
558
+ **Solutions:**
559
+
560
+ 1. **Large Repositories**
561
+ - Vulnerability scans can take up to 10 minutes for large repositories
562
+ - The CLI will wait automatically
563
+ - Use `--quiet` mode to reduce output during long scans
564
+
565
+ 2. **Network Latency**
566
+ - Check your internet speed
567
+ - Try again during off-peak hours
568
+ - Consider using a wired connection
569
+
570
+ 3. **Check Scan Status**
571
+ ```bash
572
+ # Start scan asynchronously
573
+ prismor start-scan username/repo
574
+
575
+ # Check status later
576
+ prismor scan-status <job_id>
577
+ ```
578
+
579
+ ---
580
+
581
+ ### Private Repository Access
582
+
583
+ **Error:**
584
+ ```
585
+ ✗ GitHub integration required
586
+ ```
587
+
588
+ **Solution:**
589
+ 1. Visit [https://prismor.dev/dashboard](https://prismor.dev/dashboard)
590
+ 2. Navigate to Settings → Integrations
591
+ 3. Connect your GitHub account
592
+ 4. Authorize Prismor to access private repositories
593
+ 5. Try scanning again
594
+
595
+ ---
596
+
597
+ ### Invalid Characters in Repository Name
598
+
599
+ **Error:**
600
+ ```
601
+ ✗ Invalid Username: 'user@name'. Must contain only alphanumeric characters...
602
+ ```
603
+
604
+ **Solution:**
605
+ - Ensure repository name follows GitHub naming conventions
606
+ - Remove special characters like `@`, `#`, `$`, etc.
607
+ - Valid example: `username/my-repo-name`
608
+ - Invalid example: `user@name/repo#123`
609
+
610
+ ---
611
+
612
+ ### Rate Limiting
613
+
614
+ **Error:**
615
+ ```
616
+ ✗ API error: Rate limit exceeded
617
+ ```
618
+
619
+ **Solution:**
620
+ 1. Wait a few minutes before retrying
621
+ 2. Check your account limits at [prismor.dev/dashboard](https://prismor.dev/dashboard)
622
+ 3. Upgrade your plan if needed for higher limits
623
+
624
+ ---
625
+
626
+ ### Getting Help
627
+
628
+ If you're still experiencing issues:
629
+
630
+ 1. **Check Configuration**
631
+ ```bash
632
+ prismor config
633
+ ```
634
+
635
+ 2. **View Account Status**
636
+ ```bash
637
+ prismor status
638
+ ```
639
+
640
+ 3. **Enable Verbose Output**
641
+ - Remove `--quiet` flag to see detailed error messages
642
+
643
+ 4. **Contact Support**
644
+ - Visit [https://prismor.dev](https://prismor.dev)
645
+ - Check documentation at [https://docs.prismor.dev](https://docs.prismor.dev)
646
+ - Report issues at [GitHub Issues](https://github.com/PrismorSec/prismor-cli/issues)
431
647
 
432
648
  ## Development
433
649
 
@@ -1,6 +1,6 @@
1
1
  """Prismor CLI - Security scanning tool for GitHub repositories."""
2
2
 
3
- __version__ = "1.0.4"
3
+ __version__ = "1.1.0"
4
4
  __author__ = "Prismor"
5
5
  __description__ = "A CLI tool for scanning GitHub repositories for vulnerabilities, secrets, and generating SBOMs"
6
6
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import os
4
4
  import re
5
+ import time
5
6
  import requests
6
7
  from typing import Optional, Dict, Any
7
8
  from urllib.parse import urlparse
@@ -12,6 +13,40 @@ class PrismorAPIError(Exception):
12
13
  pass
13
14
 
14
15
 
16
+ def retry_request(func, max_retries=3, backoff_factor=2):
17
+ """Retry a request function with exponential backoff.
18
+
19
+ Args:
20
+ func: Function to retry (should return requests.Response)
21
+ max_retries: Maximum number of retry attempts
22
+ backoff_factor: Multiplier for delay between retries
23
+
24
+ Returns:
25
+ Response from the function
26
+
27
+ Raises:
28
+ Exception: If all retries fail
29
+ """
30
+ last_exception = None
31
+
32
+ for attempt in range(max_retries):
33
+ try:
34
+ return func()
35
+ except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e:
36
+ last_exception = e
37
+ if attempt < max_retries - 1:
38
+ delay = backoff_factor ** attempt
39
+ time.sleep(delay)
40
+ continue
41
+ raise
42
+ except Exception as e:
43
+ # Don't retry on other exceptions
44
+ raise
45
+
46
+ # If we get here, all retries failed
47
+ raise last_exception
48
+
49
+
15
50
  def parse_github_repo(repo_input: str) -> str:
16
51
  """Extract user/repo_name from various GitHub URL formats or return as-is if already in correct format.
17
52
 
@@ -42,6 +77,19 @@ def parse_github_repo(repo_input: str) -> str:
42
77
  if not repo_input or not isinstance(repo_input, str):
43
78
  raise PrismorAPIError("Repository input cannot be empty")
44
79
 
80
+ # Validate repository name characters (GitHub allows alphanumeric, hyphens, underscores, dots)
81
+ def validate_repo_part(part: str, part_name: str) -> None:
82
+ if not part:
83
+ raise PrismorAPIError(f"{part_name} cannot be empty")
84
+ if len(part) > 100:
85
+ raise PrismorAPIError(f"{part_name} is too long (max 100 characters)")
86
+ # GitHub allows alphanumeric, hyphens, underscores, dots, but not starting/ending with special chars
87
+ if not re.match(r'^[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$', part):
88
+ raise PrismorAPIError(
89
+ f"Invalid {part_name}: '{part}'. Must contain only alphanumeric characters, "
90
+ "hyphens, underscores, or dots, and cannot start or end with special characters."
91
+ )
92
+
45
93
  repo_input = repo_input.strip()
46
94
 
47
95
  # If it's already in user/repo format (no protocol, no domain), return as-is
@@ -50,6 +98,8 @@ def parse_github_repo(repo_input: str) -> str:
50
98
  # Validate it has exactly one slash and both parts are non-empty
51
99
  parts = repo_input.split("/")
52
100
  if len(parts) == 2 and parts[0] and parts[1]:
101
+ validate_repo_part(parts[0], "Username")
102
+ validate_repo_part(parts[1], "Repository name")
53
103
  return repo_input
54
104
  else:
55
105
  raise PrismorAPIError(f"Invalid repository format: {repo_input}. Expected 'user/repo_name'")
@@ -263,12 +313,16 @@ class PrismorClient:
263
313
  try:
264
314
  # Note: Vulnerability scans now run asynchronously and can take up to 10 minutes
265
315
  # The web API handles polling internally, so we just need a longer timeout
266
- response = requests.post(
267
- f"{self.base_url}/api/cli/scan",
268
- json=payload,
269
- headers={"Content-Type": "application/json"},
270
- timeout=600 # 10 minute timeout to accommodate async vulnerability scans
271
- )
316
+ # Use retry logic for network resilience
317
+ def make_request():
318
+ return requests.post(
319
+ f"{self.base_url}/api/cli/scan",
320
+ json=payload,
321
+ headers={"Content-Type": "application/json"},
322
+ timeout=600 # 10 minute timeout to accommodate async vulnerability scans
323
+ )
324
+
325
+ response = retry_request(make_request, max_retries=3)
272
326
 
273
327
  if response.status_code == 401:
274
328
  error_data = response.json()
@@ -290,6 +344,32 @@ class PrismorClient:
290
344
  result = response.json()
291
345
 
292
346
  # Handle the new response format from CLI endpoint
347
+ if result.get("ok") and result.get("status") == "accepted" and "job_id" in result:
348
+ job_id = result["job_id"]
349
+
350
+ # Poll for completion
351
+ while True:
352
+ time.sleep(5) # Wait between polls
353
+
354
+ try:
355
+ status_data = self.check_scan_status(job_id)
356
+ status = status_data.get("status")
357
+
358
+ if status == "completed":
359
+ # Return the results
360
+ if "results" in status_data:
361
+ return status_data["results"]
362
+ return status_data
363
+
364
+ if status == "failed":
365
+ error_msg = status_data.get("error", "Unknown error")
366
+ raise PrismorAPIError(f"Scan failed: {error_msg}")
367
+
368
+ except Exception as e:
369
+ # For now, let exceptions propagate to the caller which will stop the spinner
370
+ # In the future, we might want to implement retry logic for transient errors
371
+ raise e
372
+
293
373
  if result.get("ok") and "results" in result:
294
374
  return result["results"]
295
375
  return result
@@ -167,10 +167,12 @@ def format_scan_results(results: dict, scan_type: str):
167
167
  @click.option("--fullscan", is_flag=True, help="Perform all scan types")
168
168
  @click.option("--branch", type=str, help="Specific branch to scan (defaults to main/master)")
169
169
  @click.option("--json", "output_json", is_flag=True, help="Output results in JSON format")
170
- @click.version_option(version="1.0.5", prog_name="prismor")
170
+ @click.option("--output", "-o", type=click.Path(), help="Save results to file (JSON format)")
171
+ @click.option("--quiet", "-q", is_flag=True, help="Minimal output (only errors and final results)")
172
+ @click.version_option(version="1.1.1", prog_name="prismor")
171
173
  @click.pass_context
172
174
  def cli(ctx, scan_repo: Optional[str], scan: bool, sbom: bool, detect_secret: bool,
173
- fullscan: bool, branch: Optional[str], output_json: bool):
175
+ fullscan: bool, branch: Optional[str], output_json: bool, output: Optional[str], quiet: bool):
174
176
  """Prismor CLI - Security scanning tool for GitHub repositories.
175
177
 
176
178
  Examples:
@@ -196,7 +198,8 @@ def cli(ctx, scan_repo: Optional[str], scan: bool, sbom: bool, detect_secret: bo
196
198
 
197
199
  try:
198
200
  # Initialize API client
199
- print_info(f"Initializing Prismor scan for: {scan_repo}")
201
+ if not quiet:
202
+ print_info(f"Initializing Prismor scan for: {scan_repo}")
200
203
  client = PrismorClient()
201
204
 
202
205
  # Determine scan type for display
@@ -211,15 +214,18 @@ def cli(ctx, scan_repo: Optional[str], scan: bool, sbom: bool, detect_secret: bo
211
214
  if detect_secret:
212
215
  scan_types.append("Secret Detection")
213
216
 
214
- print_info(f"Scan type: {', '.join(scan_types)}")
215
- if scan or fullscan:
216
- print_info("Starting scan... (vulnerability scans run asynchronously and may take up to 10 minutes)")
217
- else:
218
- print_info("Starting scan... (this may take a few minutes)")
217
+ if not quiet:
218
+ print_info(f"Scan type: {', '.join(scan_types)}")
219
+ if scan or fullscan:
220
+ print_info("Starting scan... (vulnerability scans run asynchronously and may take up to 10 minutes)")
221
+ else:
222
+ print_info("Starting scan... (this may take a few minutes)")
219
223
 
220
- # Show loading spinner during scan
221
- spinner = Spinner("Scanning repository")
222
- spinner.start()
224
+ # Show loading spinner during scan (unless quiet mode)
225
+ spinner = None
226
+ if not quiet and not output_json and not output:
227
+ spinner = Spinner("Scanning repository")
228
+ spinner.start()
223
229
 
224
230
  try:
225
231
  # Perform scan
@@ -231,48 +237,64 @@ def cli(ctx, scan_repo: Optional[str], scan: bool, sbom: bool, detect_secret: bo
231
237
  fullscan=fullscan,
232
238
  branch=branch
233
239
  )
234
- spinner.stop()
240
+ if spinner:
241
+ spinner.stop()
235
242
  except Exception as e:
236
- spinner.stop()
243
+ if spinner:
244
+ spinner.stop()
237
245
  raise e
238
246
 
247
+ # Save to file if --output specified
248
+ if output:
249
+ try:
250
+ with open(output, 'w') as f:
251
+ json.dump(results, f, indent=2)
252
+ if not quiet:
253
+ print_success(f"Results saved to: {output}")
254
+ except Exception as e:
255
+ print_error(f"Failed to save results to file: {str(e)}")
256
+ sys.exit(1)
257
+
239
258
  # Output results
240
- if output_json:
241
- click.echo(json.dumps(results, indent=2))
259
+ if output_json or output:
260
+ if not output: # Only print to stdout if not saving to file
261
+ click.echo(json.dumps(results, indent=2))
242
262
  else:
243
- print_success("Scan completed successfully!")
263
+ if not quiet:
264
+ print_success("Scan completed successfully!")
244
265
  format_scan_results(results, ', '.join(scan_types))
245
266
 
246
- # Try to get repository ID and display dashboard link
247
- try:
248
- # Extract repo name from scan input using the comprehensive parser
249
- repo_name = parse_github_repo(scan_repo)
250
-
251
- # Get repository ID
252
- repo_info = client.get_repository_by_name(repo_name)
253
- if repo_info.get("success") and "repository" in repo_info:
254
- repo_id = repo_info["repository"]["id"]
255
- dashboard_url = f"https://prismor.dev/repositories/{repo_id}"
267
+ # Try to get repository ID and display dashboard link (unless quiet mode)
268
+ if not quiet:
269
+ try:
270
+ # Extract repo name from scan input using the comprehensive parser
271
+ repo_name = parse_github_repo(scan_repo)
256
272
 
257
- click.echo("\n" + "=" * 60)
258
- click.secho(" 📊 Dashboard Analysis", fg="cyan", bold=True)
259
- click.echo("=" * 60)
260
- click.secho(f"🔗 View detailed analysis and insights:", fg="blue")
261
- click.secho(f" {dashboard_url}", fg="green", bold=True)
262
- click.echo("\n💡 The dashboard provides:")
263
- click.echo(" Interactive visualizations and charts")
264
- click.echo(" Historical vulnerability trends")
265
- click.echo(" Detailed security reports")
266
- click.echo(" Team collaboration features")
267
- click.echo(" Export capabilities")
268
- click.echo("=" * 60 + "\n")
269
-
270
- except PrismorAPIError as e:
271
- # Repository might not be found, continue without dashboard link
272
- print_warning(f"Could not generate dashboard link: {str(e)}")
273
- except Exception as e:
274
- # Any other error, continue without dashboard link
275
- print_warning(f"Could not generate dashboard link: {str(e)}")
273
+ # Get repository ID
274
+ repo_info = client.get_repository_by_name(repo_name)
275
+ if repo_info.get("success") and "repository" in repo_info:
276
+ repo_id = repo_info["repository"]["id"]
277
+ dashboard_url = f"https://prismor.dev/repositories/{repo_id}"
278
+
279
+ click.echo("\n" + "=" * 60)
280
+ click.secho(" 📊 Dashboard Analysis", fg="cyan", bold=True)
281
+ click.echo("=" * 60)
282
+ click.secho(f"🔗 View detailed analysis and insights:", fg="blue")
283
+ click.secho(f" {dashboard_url}", fg="green", bold=True)
284
+ click.echo("\n💡 The dashboard provides:")
285
+ click.echo(" • Interactive visualizations and charts")
286
+ click.echo(" • Historical vulnerability trends")
287
+ click.echo(" • Detailed security reports")
288
+ click.echo(" Team collaboration features")
289
+ click.echo(" • Export capabilities")
290
+ click.echo("=" * 60 + "\n")
291
+
292
+ except PrismorAPIError as e:
293
+ # Repository might not be found, continue without dashboard link
294
+ print_warning(f"Could not generate dashboard link: {str(e)}")
295
+ except Exception as e:
296
+ # Any other error, continue without dashboard link
297
+ print_warning(f"Could not generate dashboard link: {str(e)}")
276
298
 
277
299
  except PrismorAPIError as e:
278
300
  print_error(str(e))
@@ -285,7 +307,7 @@ def cli(ctx, scan_repo: Optional[str], scan: bool, sbom: bool, detect_secret: bo
285
307
  @cli.command()
286
308
  def version():
287
309
  """Display the version of Prismor CLI."""
288
- click.echo("Prismor CLI v1.0.5")
310
+ click.echo("Prismor CLI v1.1.0")
289
311
 
290
312
 
291
313
  @cli.command()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prismor
3
- Version: 1.0.5
3
+ Version: 1.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
@@ -280,6 +280,33 @@ prismor --repo git@github.com:nodejs/node.git --detect-secret --sbom
280
280
  prismor --repo www.github.com/vercel/next.js --fullscan
281
281
  ```
282
282
 
283
+ ### Example 7: Save Results to File
284
+
285
+ ```bash
286
+ # Save full scan results to JSON file
287
+ prismor --repo username/repo --fullscan --output results.json
288
+
289
+ # Save vulnerability scan with specific branch
290
+ prismor --repo username/repo --scan --branch develop --output vuln-scan.json
291
+
292
+ # Quiet mode with file output (no console output)
293
+ prismor --repo username/repo --sbom --output sbom.json --quiet
294
+ ```
295
+
296
+ ### Example 8: CI/CD Integration
297
+
298
+ ```bash
299
+ # Minimal output for CI/CD pipelines
300
+ prismor --repo $REPO_NAME --scan --quiet --json > scan-results.json
301
+
302
+ # Exit with error code if scan fails
303
+ prismor --repo username/repo --fullscan --quiet || exit 1
304
+
305
+ # Save results and continue pipeline
306
+ prismor --repo username/repo --scan --output scan.json --quiet && \
307
+ echo "Scan completed, results saved to scan.json"
308
+ ```
309
+
283
310
  ### Example 7: Async Scan with Status Checking
284
311
 
285
312
  ```bash
@@ -433,43 +460,232 @@ Prismor CLI communicates with the Prismor API at `https://api.prismor.dev`. The
433
460
  - Response parsing
434
461
  - Result presentation
435
462
 
463
+ ## Advanced Usage
464
+
465
+ ### Save Results to File
466
+
467
+ Save scan results to a JSON file for later analysis:
468
+
469
+ ```bash
470
+ prismor --repo username/repo --fullscan --output results.json
471
+ ```
472
+
473
+ ### Quiet Mode
474
+
475
+ Run scans with minimal output (useful for CI/CD pipelines):
476
+
477
+ ```bash
478
+ prismor --repo username/repo --scan --quiet
479
+ ```
480
+
481
+ ### Combining Options
482
+
483
+ Combine multiple options for customized workflows:
484
+
485
+ ```bash
486
+ # Scan specific branch and save to file
487
+ prismor --repo username/repo --scan --branch develop --output scan-results.json
488
+
489
+ # Quiet mode with JSON output
490
+ prismor --repo username/repo --fullscan --quiet --json
491
+
492
+ # Save results without console output
493
+ prismor --repo username/repo --sbom --output sbom.json --quiet
494
+ ```
495
+
436
496
  ## Troubleshooting
437
497
 
438
498
  ### API Key Not Set
439
499
 
440
- If you see an error about `PRISMOR_API_KEY` not being set:
500
+ **Error:**
501
+ ```
502
+ ✗ PRISMOR_API_KEY environment variable is not set.
503
+ ```
441
504
 
505
+ **Solution:**
442
506
  ```bash
507
+ # Set temporarily (current session only)
443
508
  export PRISMOR_API_KEY=your_api_key_here
509
+
510
+ # Set permanently (add to ~/.bashrc or ~/.zshrc)
511
+ echo 'export PRISMOR_API_KEY=your_api_key_here' >> ~/.zshrc
512
+ source ~/.zshrc
444
513
  ```
445
514
 
515
+ **Get your API key:**
516
+ 1. Visit [https://prismor.dev/cli](https://prismor.dev/cli)
517
+ 2. Sign up for a free account
518
+ 3. Generate an API key from your dashboard
519
+
520
+ ---
521
+
446
522
  ### Invalid Repository Format
447
523
 
448
- Ensure your repository is in one of the supported formats:
524
+ **Error:**
525
+ ```
526
+ ✗ Unrecognized repository format
527
+ ```
449
528
 
450
529
  **Supported formats:**
451
- - `username/repository` (recommended)
452
- - `https://github.com/username/repository`
453
- - `https://www.github.com/username/repository`
454
- - `http://github.com/username/repository`
455
- - `http://www.github.com/username/repository`
456
- - `github.com/username/repository`
457
- - `www.github.com/username/repository`
458
- - `git@github.com:username/repository.git`
459
- - `https://github.com/username/repository/tree/branch`
460
- - `https://github.com/username/repository/blob/branch/file`
530
+ - `username/repository` (recommended)
531
+ - `https://github.com/username/repository`
532
+ - `https://www.github.com/username/repository`
533
+ - `http://github.com/username/repository`
534
+ - `http://www.github.com/username/repository`
535
+ - `github.com/username/repository`
536
+ - `www.github.com/username/repository`
537
+ - `git@github.com:username/repository.git`
538
+ - `https://github.com/username/repository/tree/branch`
539
+ - `https://github.com/username/repository/blob/branch/file`
461
540
 
462
541
  **Not supported:**
463
- - Non-GitHub URLs (GitLab, Bitbucket, etc.)
464
- - Invalid URL formats
465
- - Empty or malformed repository names
542
+ - Non-GitHub URLs (GitLab, Bitbucket, etc.)
543
+ - Invalid URL formats
544
+ - Empty or malformed repository names
545
+ - ❌ Repository names with invalid characters
546
+
547
+ **Valid characters:**
548
+ - Alphanumeric (a-z, A-Z, 0-9)
549
+ - Hyphens (-)
550
+ - Underscores (_)
551
+ - Dots (.)
552
+ - Cannot start or end with special characters
553
+
554
+ ---
466
555
 
467
556
  ### Connection Issues
468
557
 
469
- If you experience connection issues:
470
- 1. Check your internet connection
471
- 2. Verify the API endpoint is accessible
472
- 3. Ensure your API key is valid
558
+ **Error:**
559
+ ```
560
+ Failed to connect to Prismor API
561
+ ```
562
+
563
+ **Solutions:**
564
+
565
+ 1. **Check Internet Connection**
566
+ ```bash
567
+ ping prismor.dev
568
+ ```
569
+
570
+ 2. **Verify API Endpoint**
571
+ ```bash
572
+ curl -I https://prismor.dev
573
+ ```
574
+
575
+ 3. **Test API Key**
576
+ ```bash
577
+ prismor config
578
+ ```
579
+
580
+ 4. **Check Firewall/Proxy**
581
+ - Ensure your firewall allows HTTPS connections
582
+ - Configure proxy if needed:
583
+ ```bash
584
+ export HTTPS_PROXY=http://proxy.example.com:8080
585
+ ```
586
+
587
+ 5. **Retry with Automatic Retries**
588
+ - The CLI automatically retries failed requests 3 times with exponential backoff
589
+ - If issues persist, check your network configuration
590
+
591
+ ---
592
+
593
+ ### Timeout Issues
594
+
595
+ **Error:**
596
+ ```
597
+ ✗ Request timed out
598
+ ```
599
+
600
+ **Solutions:**
601
+
602
+ 1. **Large Repositories**
603
+ - Vulnerability scans can take up to 10 minutes for large repositories
604
+ - The CLI will wait automatically
605
+ - Use `--quiet` mode to reduce output during long scans
606
+
607
+ 2. **Network Latency**
608
+ - Check your internet speed
609
+ - Try again during off-peak hours
610
+ - Consider using a wired connection
611
+
612
+ 3. **Check Scan Status**
613
+ ```bash
614
+ # Start scan asynchronously
615
+ prismor start-scan username/repo
616
+
617
+ # Check status later
618
+ prismor scan-status <job_id>
619
+ ```
620
+
621
+ ---
622
+
623
+ ### Private Repository Access
624
+
625
+ **Error:**
626
+ ```
627
+ ✗ GitHub integration required
628
+ ```
629
+
630
+ **Solution:**
631
+ 1. Visit [https://prismor.dev/dashboard](https://prismor.dev/dashboard)
632
+ 2. Navigate to Settings → Integrations
633
+ 3. Connect your GitHub account
634
+ 4. Authorize Prismor to access private repositories
635
+ 5. Try scanning again
636
+
637
+ ---
638
+
639
+ ### Invalid Characters in Repository Name
640
+
641
+ **Error:**
642
+ ```
643
+ ✗ Invalid Username: 'user@name'. Must contain only alphanumeric characters...
644
+ ```
645
+
646
+ **Solution:**
647
+ - Ensure repository name follows GitHub naming conventions
648
+ - Remove special characters like `@`, `#`, `$`, etc.
649
+ - Valid example: `username/my-repo-name`
650
+ - Invalid example: `user@name/repo#123`
651
+
652
+ ---
653
+
654
+ ### Rate Limiting
655
+
656
+ **Error:**
657
+ ```
658
+ ✗ API error: Rate limit exceeded
659
+ ```
660
+
661
+ **Solution:**
662
+ 1. Wait a few minutes before retrying
663
+ 2. Check your account limits at [prismor.dev/dashboard](https://prismor.dev/dashboard)
664
+ 3. Upgrade your plan if needed for higher limits
665
+
666
+ ---
667
+
668
+ ### Getting Help
669
+
670
+ If you're still experiencing issues:
671
+
672
+ 1. **Check Configuration**
673
+ ```bash
674
+ prismor config
675
+ ```
676
+
677
+ 2. **View Account Status**
678
+ ```bash
679
+ prismor status
680
+ ```
681
+
682
+ 3. **Enable Verbose Output**
683
+ - Remove `--quiet` flag to see detailed error messages
684
+
685
+ 4. **Contact Support**
686
+ - Visit [https://prismor.dev](https://prismor.dev)
687
+ - Check documentation at [https://docs.prismor.dev](https://docs.prismor.dev)
688
+ - Report issues at [GitHub Issues](https://github.com/PrismorSec/prismor-cli/issues)
473
689
 
474
690
  ## Development
475
691
 
@@ -17,7 +17,7 @@ if os.path.exists("README.md"):
17
17
 
18
18
  setup(
19
19
  name="prismor",
20
- version="1.0.5",
20
+ version="1.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",
File without changes
File without changes
File without changes
File without changes