---
# System prepended metadata

title: 'Exposing Hidden Dangers: The Essential Guide to Secret Scanning in Package Repositories'

---

# Exposing Hidden Dangers: The Essential Guide to Secret Scanning in Package Repositories

In the ever-shifting realm of cybersecurity, staying one step ahead of potential threats is a non-negotiable mission. Package repositories like PyPI, npm, NuGet, and RubyGems are goldmines of software packages, cherished by developers worldwide. While these packages are indispensable for crafting powerful applications, they may also harbor concealed secrets, making developers and organizations susceptible to data breaches and malicious exploits. In this blog post, we embark on a journey to unearth the significance of secret scanning within the latest packages from various repositories, revealing some startling revelations.

## The Crucial Role of Package Repository Secret Scanning

Package repositories stand as the go-to source for software enthusiasts, housing a myriad of open-source libraries. They are often the starting point for developers when questing for packages to infuse into their projects. However, these packages are not immune to vulnerabilities, and secret leaks represent a perilous abyss.

### Demystifying Secrets

Secrets come in various guises, encompassing API keys, authentication tokens, passwords, and encryption keys. These are classified as sensitive nuggets of information that should never see the light of day, for their compromise can herald catastrophic consequences.

### The Far-reaching Ramifications of Secret Leaks

Inadvertent inclusion of secrets in packages exposes a chink in the armor, inviting nefarious actors to wreak havoc. For instance, a mishandled AWS (Amazon Web Services) access key might pave the way for unauthorized entry, unleashing a torrent of data breaches, financial setbacks, and operational chaos.

## Unveiling Secrets: A Three-Pronged Approach

To underscore the gravity of secret scanning, we've adopted an innovative approach using dedicated EC2 machines for each major package manager, namely PyPI, npm, RubyGems, and NuGet. Let's delve into the specifics of our approach for each:

### PyPI: Python Package Index

Our PyPI-specific EC2 machine tirelessly parses the latest PyPI package downloads, extracts their contents, and performs a thorough GitLeaks scan to identify any secrets hidden within Python packages. PyPI packages are a cornerstone of the Python ecosystem, and securing them is paramount.

![PyPI](https://miro.medium.com/v2/resize:fit:800/0*3M8qFS_OXqZNhR9H.png)

*PyPI Scraper in Python*

```
import requests
from bs4 import BeautifulSoup
import os
import subprocess
import zipfile
import time

# Maximum page number
max_page_number = 5
extracted = []
# Initialize the page number
page_number = 1

# Create a directory to store the downloaded packages if it doesn't exist
os.makedirs("downloaded_packages", exist_ok=True)

# Load the previously saved findings from the previous scan file
previous_findings_file = "/tmp/previous_findings.txt"
previous_findings = set()

if os.path.exists(previous_findings_file):
    with open(previous_findings_file, "r") as f:
        previous_findings.update(f.read().splitlines())

# Create a temporary file to store the findings from the current scan
current_findings_file = "/tmp/current_findings.txt"

while page_number <= max_page_number:
    # Define the URL of the PyPI search page for Python 3 packages sorted by creation date
    url = f"https://pypi.org/search/?q=&o=-created&c=Programming+Language+%3A%3A+Python+%3A%3A+3&page={page_number}"

    # Send a GET request to the PyPI search page
    response = requests.get(url)

    # Check if the request was successful
    if response.status_code == 200:
        # Parse the HTML content of the page
        soup = BeautifulSoup(response.text, 'html.parser')

        # Find all the package name and version elements on the page
        package_snippets = soup.find_all('span', class_='package-snippet__name')
        package_versions = soup.find_all('span', class_='package-snippet__version')

        # If there are no package results on the current page, break out of the loop
        if not package_snippets:
            break

        # Loop through the package names and versions and download each package
        for name, version in zip(package_snippets, package_versions):
            package_name = name.text.strip()
            package_version = version.text.strip()

            # Define the file path for the downloaded package
            wheel_file_path = os.path.join("downloaded_packages", f"{package_name}-{package_version}.whl")

            # Check if the wheel package file already exists in the downloaded_packages directory
            if not os.path.exists(wheel_file_path):
                # Construct the download URL for the wheel package
                print(package_name)
                download_url = f"https://pypi.org/simple/{package_name}/"

                # Send a GET request to the package download URL
                package_response = requests.get(download_url)

                # Check if the package request was successful
                if package_response.status_code == 200:
                    # Parse the HTML content of the package page
                    package_soup = BeautifulSoup(package_response.text, 'html.parser')

                    # Find all the wheel download links
                    wheel_links = package_soup.find_all('a', href=True, text=lambda text: text and text.endswith('.whl'))

                    if wheel_links:
                        wheel_link = wheel_links[0]['href']
                        wheel_url = f"{wheel_link}"
                        print(wheel_link)

                        # Download the wheel package
                        package_response = requests.get(wheel_url)
                        print(wheel_link + " is downloading")
                        if package_response.status_code == 200:
                            # Save the wheel package to the specified file path
                            with open(wheel_file_path, 'wb') as f:
                                f.write(package_response.content)
                            print(f"Downloaded: {package_name}-{package_version}")

                            # Extract the wheel package directly
                            with zipfile.ZipFile(wheel_file_path, 'r') as wheel_zip:
                                wheel_zip.extractall("downloaded_packages")
                                extracted.append(wheel_zip.namelist())

                            # Increment the page number to fetch the next page
                            page_number += 1
                        else:
                            print(f"Failed to download: {package_name}-{package_version}")
                    else:
                        print(f"No wheel links found for: {package_name}-{package_version}")
                else:
                    print(f"Failed to download: {package_name}-{package_version}")
            else:
                print(f"Package already exists: {package_name}-{package_version}")

    else:
        print("Failed to access PyPI")

print(extracted)
print("Finished downloading packages from all pages.")

```

### npm: Node Package Manager

Dedicated to the Node.js ecosystem, our npm EC2 machine is on a mission to parse the latest npm package downloads, extract them, and run GitLeaks scans to uncover any concealed secrets within Node.js packages. npm is the backbone of JavaScript development, and safeguarding it is essential.

![NPM](https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/Npm-logo.svg/1200px-Npm-logo.svg.png)


*NPM Scraper in Python*

```
import requests
from bs4 import BeautifulSoup
import os
import subprocess
import time

base_url = 'https://www.npmjs.com'

def extract_package(package_full_name):
    try:
        if is_package_installed(package_full_name):
            package_tarball = f'{package_full_name}.tgz'
            package_path = f'downloaded_packages/node_modules/{package_name}'
            subprocess.run(['tar', 'xvzf', package_tarball], check=True, cwd=package_path)
            print(f'Extracted: {package_full_name}')
        else:
            print(f'Package not installed: {package_full_name}')
    except Exception as e:
        print(f'Error extracting package: {e}')


def scrape_npm_updates(offset):
    global page_count
    try:
        url = f'{base_url}/browse/updated?offset={offset}'
        response = requests.get(url)
        soup = BeautifulSoup(response.content, 'html.parser')

        hrefs = [link.get('href') for link in soup.find_all('a', href=True)]

        for href in hrefs:
            if "/package/" in href:
                package_name = href.split("/package/")[1]
                if not is_package_installed(package_name):
                    install_package(package_name)

    except Exception as e:
        print('Error:', e)

def is_package_installed(package_name):
    # Check if the package exists in the downloaded_packages/node_modules directory
    node_modules_path = os.path.join('downloaded_packages', 'node_modules', package_name)
    return os.path.exists(node_modules_path)

def install_package(package_full_name):
    try:
        if not is_package_installed(package_full_name):
            print(package_full_name)
            subprocess.run(['npm', 'pack', package_full_name], check=True, cwd='downloaded_packages')
            print(f'Installed: {package_full_name}')
            time.sleep(1)
            extract_package(package_full_name)
        else:
            print(f'Package already installed: {package_full_name}')
    except Exception as e:
        print(f'Error installing package: {e}')

if not os.path.exists('downloaded_packages'):
    os.makedirs('downloaded_packages')

scrape_npm_updates(0)
# Define the shell script path
script_path = "/root/extract.sh"

# Run the shell script
try:
    subprocess.run([script_path], check=True, shell=True)
    print("Script executed successfully")
except subprocess.CalledProcessError as e:
    print(f"Error running the script: {e}")
except FileNotFoundError:
    print(f"Script not found at: {script_path}")
```

### RubyGems and NuGet: Multitasking Marvel

Our third EC2 machine is a multitasker, handling both RubyGems and NuGet repositories. It extracts the latest RubyGems and NuGet packages, meticulously scans them using GitLeaks, and reports any secrets that may compromise the security of Ruby and .NET applications. RubyGems and NuGet are pillars of their respective ecosystems, and their security is non-negotiable.

![RubyGems](https://miro.medium.com/v2/resize:fit:695/1*f71hejJUs7K0PDDh59JzVQ.png)

*RubyGems Scraper in Python*

```
import os
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

# Define the URL to crawl with pagination
base_url = "https://rubygems.org/news?page="
download_folder = "rubygems_downloads"

# Create the download folder if it doesn't exist
if not os.path.exists(download_folder):
    os.mkdir(download_folder)

cookies = {
    '_rubygems_session': 'U%2BAxJUBuLA1w9%2BT0ga0FlSxr2TsaOnPnvU4vakJsRSvSvCKG7BqZaXVUBb%2FV8sHF%2BY3G3tAHkx4uqWxbdecqblGMGeHp63pYDc2D1X6HnSFbqfqLIme6sl2PRhoQ8Jx68WYIm%2FgzNOSFmw0H9c74egjxrb4xZ4lYRa%2FhbIDRLZuhK1AgWf3w2iUR%2FLOOhd8znvziOgFKsP0qiSd%2FbQmxUbpq2VSTegSLThozeCXKJ4zcWAqvMsusDxRaKxl3KWw%2BIRpnDumIBfF1rJCSRjpUug3X4qGWDW42Lw%3D%3D--%2F%2F58flb5zXxqQJ1i--Y%2FyMpE2Jz%2FlTOq6r0Wta%2Fw%3D%3D',
}

headers = {
    'authority': 'rubygems.org',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7',
    'accept-language': 'tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7',
    'cache-control': 'max-age=0',
    # 'cookie': '_rubygems_session=U%2BAxJUBuLA1w9%2BT0ga0FlSxr2TsaOnPnvU4vakJsRSvSvCKG7BqZaXVUBb%2FV8sHF%2BY3G3tAHkx4uqWxbdecqblGMGeHp63pYDc2D1X6HnSFbqfqLIme6sl2PRhoQ8Jx68WYIm%2FgzNOSFmw0H9c74egjxrb4xZ4lYRa%2FhbIDRLZuhK1AgWf3w2iUR%2FLOOhd8znvziOgFKsP0qiSd%2FbQmxUbpq2VSTegSLThozeCXKJ4zcWAqvMsusDxRaKxl3KWw%2BIRpnDumIBfF1rJCSRjpUug3X4qGWDW42Lw%3D%3D--%2F%2F58flb5zXxqQJ1i--Y%2FyMpE2Jz%2FlTOq6r0Wta%2Fw%3D%3D',
    'if-none-match': '"98b42067eac3741b7e95a4ba52a84e2d"',
    'referer': 'https://www.google.com/',
    'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Linux"',
    'sec-fetch-dest': 'document',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-site': 'cross-site',
    'sec-fetch-user': '?1',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
}

page_number = 1

while True:
    # Construct the URL for the current page
    current_page_url = base_url + str(page_number)

    # Send an HTTP GET request to the current page
    response = requests.get(current_page_url, cookies=cookies, headers=headers, allow_redirects=False)

    # Check if the request was successful
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, "html.parser")

        # Find all the release links on the page
        release_links = soup.find_all("a", class_="gems__gem")

        # If there are no release links, break out of the loop
        if not release_links:
            break

        # Loop through the release links and download the latest releases
        for release_link in release_links:
            release_name = release_link.get("href").split("/")[-1]
            release_url = urljoin(current_page_url, release_link.get("href"))

            # Check if the release file already exists in the download folder
            if not os.path.exists(os.path.join(download_folder, release_name)):
                print(f"Getting download URL for {release_name}...")
                release_response = requests.get(release_url)

                # Check if the download request was successful
                if release_response.status_code == 200:
                    release_soup = BeautifulSoup(release_response.text, "html.parser")
                    download_link = release_soup.find("a", id="download")

                    if download_link:
                        download_url = urljoin(release_url, download_link.get("href"))
                        print(f"Downloading {release_name} from {download_url}...")
                        download_response = requests.get(download_url)

                        # Check if the download request was successful
                        if download_response.status_code == 200:
                            with open(os.path.join(download_folder, release_name), "wb") as file:
                                file.write(download_response.content)
                            print(f"{release_name} downloaded successfully.")
                        else:
                            print(f"Failed to download {release_name} from {download_url}.")
                    else:
                        print(f"No download link found for {release_name}.")
                else:
                    print(f"Failed to fetch {release_url}.")
            else:
                print(f"{release_name} already exists. Skipping.")

        page_number += 1
    else:
        print(f"Failed to fetch {current_page_url}. Status code: {response.status_code}")
        exit(1)

print("All latest releases downloaded.")
```

*Nuget Scraper in Python*

![Nuget](https://miro.medium.com/v2/resize:fit:1000/1*AsmNd94WNvD1XPKnWdWv_w.png)

```
import requests
from bs4 import BeautifulSoup
import xml.etree.ElementTree as ET
import os
import urllib.request

# Define the NuGet URL
nuget_url = "https://www.nuget.org/packages?q=&prerel=true&sortby=created-desc"
download_directory = "nuget_packages"  # Directory to save downloaded packages

# Send a GET request to the NuGet URL
response = requests.get(nuget_url)

# Check if the request was successful
if response.status_code == 200:
    soup = BeautifulSoup(response.text, 'html.parser')
    
    # Find all package links on the page
    package_links = soup.find_all('a', class_='package-title')

    for package_link in package_links:
        # Extract the package name
        package_name = package_link.text.strip()

        # Construct the URL for the package's page
        package_url = f"https://www.nuget.org/packages/{package_name}/"
        
        # Send a GET request to the package URL
        package_response = requests.get(package_url)

        # Check if the request was successful
        if package_response.status_code == 200:
            package_soup = BeautifulSoup(package_response.text, 'html.parser')
            
            # Find the download link
            download_link = package_soup.find('a', {'title': 'Download the raw nupkg file.'})
            if download_link:
                download_url = download_link['href']
                
                # Download the package
                response = urllib.request.urlopen(download_url)

                if not os.path.exists(download_directory):
                    os.makedirs(download_directory)
                
                # Download the package and save it to the download directory
                package_filename = f"{package_name}.nupkg"
                download_path = os.path.join(download_directory, package_filename)
                with open(download_path, 'wb') as f:
                        f.write(response.read())
                print(f"Downloaded {package_name}")
            else:
                print(f"No download link found for {package_name}")
        else:
            print(f"Failed to fetch package page for {package_name}")
else:
    print("Failed to fetch NuGet packages page")

```
## Automating Full Process

When running tasks that involve downloading and analyzing large amounts of data, it's crucial to monitor and manage disk space. Without proper disk space management, the system can run out of space, causing disruptions and potentially failing the task. To address this issue, we've created a script that automates both the secret scan and disk space management.

## Script Overview

Let's break down the free.sh script step by step to understand its functionality:

```bash
#!/bin/bash

# Define the threshold for available disk space in GB
threshold=2
```
The script begins by defining a threshold variable, threshold, which represents the minimum amount of available disk space (in gigabytes) required for the script to proceed without cleaning up.

```bash
# Check available disk space in GB
available_space=$(df -h / | awk 'NR==2 { print $4 }' | sed 's/G//')
```

Next, it checks the current available disk space on the root filesystem (i.e., /). It uses the df command to retrieve disk space information, and then, with the help of awk and sed, extracts the available space in gigabytes.

```bash
# Convert the available space to a numeric value
available_space_numeric=$(echo $available_space | sed 's/,//')
```
The script converts the available space into a numeric value to facilitate comparison with the defined threshold.

```bash
# Compare available space with the threshold
if [ "$available_space_numeric" -lt "$threshold" ]; then
```
Here, it compares the available disk space with the threshold. If the available space falls below the specified threshold, the script proceeds to perform cleanup and initiate the secret scan. Otherwise, it simply reports that the available disk space is sufficient.

```bash
    # Run gitleaks and write the output to a temporary file
    tmp_file=$(mktemp)
    echo $tmp_file | notify
    gitleaks detect --no-git -v downloaded_packages/ --config ~/config.toml -r=$tmp_file
```

Within this conditional block, the script runs a secret scan using the gitleaks tool. It generates a temporary file to capture the scan results and uses the notify command to send a notification (you may need to customize this part depending on your notification system).

```bash
    # Check if the downloaded_packages directory exists
    if [ -d "downloaded_packages" ]; then
        # Delete the downloaded_packages directory
        rm -rf downloaded_packages
	    rm -rf .npm
    fi
```
In this part of the script, it checks if a directory named downloaded_packages exists. If it does, it deletes this directory along with the .npm directory, which is often used for package management in Node.js projects. This cleanup helps free up disk space by removing unnecessary downloaded files.

```bash
else
    echo "Available disk space is greater than or equal to 2GB."
fi
```

Finally, if the available disk space is equal to or greater than 2GB (as specified by the threshold), the script reports that there's no need for cleanup, ensuring that the secret scan project can continue without interruption.

## PackageSpy

PackageSpy is an innovative, open-source tool designed to scan package managers for secrets, user-defined keywords, and patterns. It helps developers safeguard their projects and ensure that sensitive information remains hidden from prying eyes. Here's how PackageSpy works:

1. **Support for Multiple Package Managers**: PackageSpy supports popular package managers like npm, PyPI, RubyGems, and more, making it versatile and adaptable to different development environments.

2. **Customizable Scanning Rules**: Developers can define their own scanning rules, keywords, and patterns to identify secrets specific to their projects. This flexibility ensures that PackageSpy can cater to diverse security requirements.

3. **Command-Line Interface (CLI)**: PackageSpy's user-friendly CLI interface allows developers to initiate scans easily and integrate it into their development workflows.

4. **Interactive Reports**: After scanning, PackageSpy generates detailed reports highlighting any secrets or keywords found, their locations, and suggested actions for mitigation.

5. **Continuous Integration (CI) Integration**: PackageSpy seamlessly integrates with CI/CD pipelines, allowing developers to automate scans during the development process, preventing secrets from being committed to repositories.

### Usage and Benefits

PackageSpy is simple to use, yet it provides a robust security layer for package manager repositories. Here's how developers can benefit from this tool:

1. **Enhanced Security**: By proactively scanning for secrets and keywords, PackageSpy helps developers identify vulnerabilities and maintain the confidentiality of sensitive information.

2. **Time and Cost Savings**: Detecting secrets early in the development process saves time and resources compared to dealing with potential breaches and their aftermath.

3. **Compliance and Peace of Mind**: PackageSpy aids in compliance with security best practices and industry standards, offering peace of mind to developers and stakeholders alike.

4. **Open-Source Community**: PackageSpy is open-source, encouraging collaboration and contribution from the development community to improve its capabilities and security.

https://github.com/aydinnyunus/PackageSpy


## Analyzing Secret Scan Output

### Understanding the Risks of Exposed Secrets in NPM Packages: A Breakdown

If you're a developer using Node.js, you're likely familiar with the Node Package Manager (NPM), a lifeline for importing countless libraries and tools to streamline your development process. However, there's a hidden risk lurking in the shadows: the inadvertent exposure of secrets. In this post, we dive into recent scan results revealing the types of secrets most commonly found in NPM packages and discuss the potential risks associated with such exposures.

![NPM](https://i.imgur.com/U7bXtLE.png)

#### The Pervasiveness of AWS Access Tokens

The scan results are alarming: a whopping 34.3% of secrets found in NPM packages were AWS access tokens. These tokens are like digital keys to the kingdom of Amazon Web Services, allowing access to a vast array of resources. If these tokens fall into the wrong hands, it could lead to unauthorized access and control over cloud resources, leading to data breaches or costly usage charges.

#### HashiCorp Terraform Passwords – A Close Second

Following closely are HashiCorp Terraform passwords, constituting 20.6% of the findings. Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Passwords for Terraform can provide access to modify infrastructure, potentially allowing attackers to disrupt operations or create malicious environments.

#### The Danger of Exposed Private Keys

With 12.2% of the secrets being private keys, this issue presents a severe security threat. Private keys are used in cryptographic protocols to ensure secure communications. Exposure of these keys can lead to interception of sensitive data, impersonation, and a host of other security nightmares.

#### Financial Risks with Stripe Access Tokens

Stripe access tokens, at 7.2%, represent a direct financial risk. These tokens allow applications to charge credit cards and manage payments. Unauthorized access to these tokens could lead to fraudulent transactions and financial losses.

#### Social Media API Secrets

LinkedIn, Twitter, and Facebook secrets combined make up a small but significant percentage. These are used to interact with social media platforms' APIs and could lead to unauthorized posting, access to sensitive profile information, and data harvesting.

#### The Slack and Telegram Tokens

Slack webhook URLs (25.7%) and Telegram Bot API tokens (14.3%) are particularly concerning as they allow for the sending of messages within these platforms. Unauthorized access here could lead to phishing attacks, spreading of malware, or leaking of confidential communication.

#### The Lesser-Known Culprits

GitHub app tokens, Google Cloud Platform API keys, and Microsoft Teams webhooks, while less prevalent, still pose significant risks. They provide authenticated access to source code, cloud resources, and team communications, respectively.

![NPM](https://i.imgur.com/5Q7VguE.png)

### The Silent Alarm: Exposed Secrets in PyPI Packages

Python developers, take heed. The Python Package Index (PyPI) is an indispensable resource, but recent findings show that it's also a minefield of security risks due to exposed secrets. In this post, we'll dissect the nature of these secrets and the potential hazards they pose.

![PyPI](https://i.imgur.com/1ucnQ5l.png)

#### AWS Access Tokens: A Dominant Risk

An astounding 54.6% of the secrets found in PyPI packages were AWS access tokens. These tokens serve as a passport to Amazon Web Services, granting various levels of access to a plethora of services. The exposure of these tokens is akin to leaving the key to your house under the doormat, inviting a host of security issues ranging from data breaches to unauthorized operations that could rack up substantial costs.

#### HashiCorp Terraform Passwords: The Runner-Up

Making up 20.8% of the secrets, HashiCorp Terraform passwords are the second most common leak. Terraform automates the deployment of infrastructure, and these passwords could allow attackers to alter cloud environments, potentially leading to service disruptions or even data destruction.

#### Private Keys: The Hidden Dangers

Private keys, which represent 10.4% of the findings, are vital for secure communications in various protocols. If compromised, the consequences can be dire, including data leaks and man-in-the-middle attacks.

#### JWTs: Small Percentage, Big Problems

JWTs, or JSON Web Tokens, constitute 9.6% of the exposed secrets. These tokens are widely used for authentication and information exchange. Their exposure could lead to unauthorized access and manipulation of user sessions.

### Smaller Shares with Significant Impacts

Other exposed secrets include Etsy access tokens, Slack webhook URLs, and Telegram Bot API tokens, each accounting for less than 2% of the findings. Despite their smaller shares, they hold the potential for significant damage. Etsy tokens could allow unauthorized transactions, Slack URLs could enable spreading misinformation or phishing within organizations, and Telegram tokens could compromise bot interactions.

#### The Less Than 1% Club: Varied and Volatile

A diverse array of secrets fall into this category, including API keys for Google Cloud Platform, GitHub personal access tokens, and Stripe access tokens. Each of these can open the door to their respective services, allowing for unauthorized actions that could range from code theft to financial fraud.

![PyPI](https://i.imgur.com/rm0msos.png)


### The Red Flags in Ruby: Secrets Exposure in RubyGems

Ruby developers, it's time for a security check-up. RubyGems, the package manager that serves as a hub for distributing Ruby programs and libraries, has become a hotbed for exposed secrets. Let's dissect the recent findings from a security scan and discuss the implications for the Ruby community.

![RubyGems](https://i.imgur.com/dMHFFP8.png)

#### AWS Access Tokens Take the Lion's Share

The scan results are striking. An overwhelming 66.5% of the secrets found in RubyGems packages were AWS access tokens. This is not just a majority; it's a dominance that should raise eyebrows. AWS tokens are the master keys to cloud services that can control virtually every aspect of AWS. From spinning up servers to accessing databases, the potential for misuse here is vast and the ramifications, from data leakage to service interruption, are serious.

#### HashiCorp Terraform Passwords – A Distinct Concern

HashiCorp Terraform passwords account for 15.8% of the secrets exposed. As a tool that manages infrastructure as code, Terraform has the power to create and destroy environments. Exposure of these passwords can lead to unauthorized changes to infrastructure, making it a significant point of vulnerability.

#### Private Keys and JWTs: Small Pieces, Big Puzzle

Though they represent smaller portions—9.3% for private keys and 6.8% for JWTs—their impact is disproportionately large. Private keys are crucial for the security of communications in various encryption protocols, and JWTs are heavily used for authentication processes. Leaks in these areas can lead to a range of security issues, including unauthorized access and eavesdropping.

#### Stripe Access Tokens: Financial Implications

Stripe access tokens, which stand at 1.7%, may seem minor but hold the keys to financial transactions. Misuse of these tokens can lead to financial fraud and loss, highlighting the need for stringent protection measures.

#### The Under 1%: Slack Webhooks and OpenAI API Keys

Even less prevalent but noteworthy are Slack webhook URLs and OpenAI API keys. Slack webhooks can be used to send messages to teams, potentially spreading misinformation or phishing attacks. OpenAI API keys give access to powerful AI tools, which, if misused, could lead to unethical generation of content or exploitation of AI resources.

![RubyGems](https://i.imgur.com/AIIQ4mx.png)

### Reporting Findings

Our secret scanning efforts have uncovered critical vulnerabilities within packages hosted on popular repositories, exposing sensitive information that could lead to severe security breaches. We take the responsibility of reporting these findings to the respective companies and organizations that own or manage the affected services. Below is a summary of our reporting process:

#### Finding Contacts

To identify and contact the owners or maintainers of the affected projects associated with the following companies, we utilize information available through package managers such as npm, PyPI, RubyGems, NuGet, and others. The process involves:

1. **Package Manager Investigation:**
   - For npm packages: We inspect the `package.json` file for contact information, including the "maintainers" field.
   - For PyPI packages: We check the `METADATA` or `pyproject.toml` files for maintainers' details.
   - For RubyGems: We explore the gemspec file for owner/maintainer information.
   - For NuGet packages: We review the nuspec file for contact details.

2. **Project Documentation and Repository:**
   - We explore the official documentation and repository of the project, searching for maintainers' or owners' contact information.

3. **Publicly Available Communication Channels:**
   - Look for mailing lists, forums, or community channels associated with the project where maintainers can be reached.

4. **Package Manager Messaging System:**
   - Utilize the messaging systems provided by package managers, such as npm's `npm owner add` or PyPI's maintainers messaging system.

#### Reporting Method

Once the contact information is obtained, we initiate the reporting process to the respective companies:

1. **Microsoft**
2. **Automattic**
3. **Mapbox**
4. **Keeper Security**
5. **Pulumi**
6. **Weblate**
7. **Palo Alto Networks**
8. **Telefonica Global**
9. **Private (+7.5M Downloads)**

#### Reporting Channels

For each company, consider using a combination of the following reporting channels:

- **Email Communication:**
  - Send detailed emails to the identified contacts within the companies, providing an overview of the discovered vulnerabilities, potential impact, and recommended actions for mitigation.

- **HackerOne/Bugcrowd Platforms:**
  - If the companies participate in bug bounty programs, submit the vulnerabilities through platforms like HackerOne or Bugcrowd, following their respective guidelines.

- **Security Disclosure Policy:**
  - Adhere to the companies' security disclosure policies if available, ensuring that the vulnerabilities are reported responsibly and in compliance with their guidelines.

- **Follow-Up and Collaboration:**
  - Maintain open lines of communication with the companies' security teams, responding promptly to any inquiries, and collaborating on the development of patches or mitigations.

By following these steps, we aim to responsibly disclose critical vulnerabilities associated with the mentioned companies and contribute to the overall security of the software ecosystem.


## Conclusion

Secret scanning within the latest packages from various repositories is an indispensable practice for upholding the security of software applications. Our three-pronged approach with dedicated EC2 machines, along with the introduction of the user-centric scanning tool, highlights our commitment to thorough security. By proactively identifying and mitigating secrets, developers can significantly diminish the odds of security breaches, safeguarding their organizations and users from the perils that lurk in the shadows.

Always remember, the potency of open source blossoms through collaboration and responsible coding practices. Let us join hands in fortifying the software ecosystem, rendering it a safer haven for all.

For more insights into the realm of cybersecurity, consider subscribing to our newsletter, where we unravel the latest threats and best practices.