Skip to content

feat(sandbox): implement secret visibility lockdown for env files#23712

Open
DavidAPierce wants to merge 7 commits intomainfrom
sandbox_visibility_lock
Open

feat(sandbox): implement secret visibility lockdown for env files#23712
DavidAPierce wants to merge 7 commits intomainfrom
sandbox_visibility_lock

Conversation

@DavidAPierce
Copy link
Contributor

Summary
This PR implements a strict visibility lockdown for secret files (specifically .env and .env.*) across all supported sandbox environments (Linux, macOS, and Windows). This ensures that sensitive credentials and API keys
are effectively invisible and inaccessible to any tools running within the sandbox.

Details

  • Unified Secret Detection: Centralized secret file patterns and discovery logic in packages/core/src/services/sandboxManager.ts.
  • Linux (Bubblewrap): Utilizes the native find command to locate secret files up to a depth of 3 and masks them using --bind with a 000-permission temporary file.
  • macOS (Seatbelt): Implements regex-based deny file-read* file-write* rules within the Seatbelt profile to block access to secrets without requiring a pre-scan.
  • Windows (Low Integrity & Helper):
    • Uses icacls to apply Medium(NW,NR) (No-Read-Up, No-Write-Up) mandatory labels to secret files, preventing the Low Integrity sandboxed process from accessing them.
    • Updated the C# GeminiSandbox helper to enforce a new --forbidden path-blocking flag for internal commands.
  • Explicit Forbidden Paths: Added general support for forbiddenPaths in the sandbox policy across all platforms.

Related Issues
Fixes https://github.com/google-gemini/maintainers-gemini-cli/issues/1560

How to Validate

  1. Automated Tests:
    • Run npm test packages/core/src/services/sandboxManager.test.ts
    • Run npm test packages/core/src/sandbox/linux/LinuxSandboxManager.test.ts
  2. Manual Verification (Linux/macOS):
    • Create a .env file in your workspace: echo "SECRET=123" > .env
    • Use the CLI to run a command that tries to read it: cat .env
    • Expected: Output should be Permission denied or the file should appear non-existent.
  3. Manual Verification (Windows):
    • Similar to above, verify that type .env fails when executed within the sandbox.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • Seatbelt
    • Windows
      • npm run
    • Linux
      • npm run

@DavidAPierce DavidAPierce requested a review from a team as a code owner March 24, 2026 20:53
@gemini-cli
Copy link
Contributor

gemini-cli bot commented Mar 24, 2026

Hi @DavidAPierce, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.

We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: Improving Our Contribution Process & Introducing New Guidelines.

Key Update: Starting January 26, 2026, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.

Thank you for your understanding and for being a part of our community!

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the security posture of the sandbox environments by implementing a robust system to prevent unauthorized access to sensitive files, such as .env files. It introduces platform-specific mechanisms for Linux, macOS, and Windows to ensure that secret credentials and API keys are effectively isolated and inaccessible to sandboxed processes, thereby mitigating potential data leakage risks.

Highlights

  • Unified Secret Detection: Centralized secret file patterns and discovery logic in packages/core/src/services/sandboxManager.ts to ensure consistent handling across platforms.
  • Linux (Bubblewrap) Implementation: Utilized the native find command to locate secret files up to a depth of 3 and masked them using bwrap --bind with a 000-permission temporary file, making them inaccessible.
  • macOS (Seatbelt) Implementation: Implemented regex-based deny file-read* file-write* rules within the Seatbelt profile to block access to secrets without requiring a pre-scan.
  • Windows (Low Integrity & Helper) Implementation: Used icacls to apply Medium(NW,NR) mandatory labels to secret files, preventing Low Integrity sandboxed processes from accessing them. The C# GeminiSandbox helper was updated to enforce a new --forbidden path-blocking flag for internal commands.
  • Explicit Forbidden Paths: Added general support for explicitly forbidden paths in the sandbox policy across all supported platforms.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances sandboxing capabilities across Linux, macOS, and Windows to protect sensitive files (like .env files) and enforce explicitly forbidden paths. It introduces new utility functions for identifying secret files and integrates these protections into the platform-specific sandbox managers. Specifically, the Linux sandbox uses bwrap to mask secrets and forbidden paths, the macOS sandbox updates seatbelt profiles with regex-based deny rules, and the Windows sandbox uses icacls to set integrity levels for secret files and a C# helper to enforce forbidden paths. Key security issues identified include a symlink attack vulnerability in the Linux sandbox's temporary mask file creation, a path normalization bypass in the Windows sandbox's CheckForbidden method using 8.3 short names, and silent error handling in the Linux sandbox when finding and masking secret files, which should be logged. Additionally, a configuration change to disable auto-updates in the test rig is included.

Comment on lines +117 to +118
const maskPath = join(os.tmpdir(), `gemini-cli-mask-${process.pid}`);
fs.writeFileSync(maskPath, '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Similar to the seccomp BPF file, this temporary mask file is created with a predictable name in a global directory, making it susceptible to symlink attacks. An attacker could use this to truncate arbitrary files that the user has write access to.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is valid comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

I refactored the creation of the seccomp BPF file, the Linux mask file, the Linux mask directory, and the Windows forbidden paths manifest to use fs.mkdtempSync(). This creates securely generated, uniquely named temporary
directories (e.g., /tmp/gemini-cli-mask-file-XXX/mask) instead of placing files directly into the global /tmp directory with easily guessable names (/tmp/gemini-cli-mask-file-).

By doing this, we completely mitigate the risk of an attacker pre-creating a symlink with that predictable name to truncate arbitrary files on the host system. I've also verified that the tests still pass successfully with
this updated logic.

}

private static void CheckForbidden(string path, List<string> forbiddenPaths) {
string fullPath = Path.GetFullPath(path).ToLower();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The CheckForbidden method relies on Path.GetFullPath for path normalization, which does not resolve 8.3 short names (e.g., PROGRA~1 for Program Files) on Windows. An attacker can bypass the forbiddenPaths check by providing the short name of a restricted file or directory. Since the subsequent file access calls in the helper (and the sandboxed process) will resolve and accept these short names, the security policy can be circumvented.

To fix this, you should resolve all paths to their long-name equivalents before performing the comparison. This can be achieved by using the GetLongPathName Win32 API via P/Invoke.

Comment on lines +210 to +212
} catch {
// Ignore errors finding secrets
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This try...catch block silently ignores any errors that occur while finding and masking secret files. If an error occurs (e.g., the find command is not available or fails for some reason), the secret protection will fail silently, which poses a security risk. It's better to log this failure for debugging purposes, similar to how it's handled in the WindowsSandboxManager.

You will also need to import debugLogger from ../../utils/debugLogger.js.

    } catch (e) {
      // Ignore errors finding secrets, but log them for debugging.
      debugLogger.log(
        'LinuxSandboxManager: Failed to secure secret files',
        e,
      );
    }
References
  1. When catching exceptions, log the detailed error for debugging instead of providing only a generic error message.

@github-actions
Copy link

github-actions bot commented Mar 24, 2026

Size Change: +6.43 kB (+0.02%)

Total Size: 26.3 MB

Filename Size Change
./bundle/chunk-4L3MYAOH.js 0 B -14.6 MB (removed) 🏆
./bundle/chunk-BI2HCBTD.js 0 B -3.64 MB (removed) 🏆
./bundle/chunk-K7UW2BKN.js 0 B -3.4 kB (removed) 🏆
./bundle/core-TF5IFBKD.js 0 B -43.5 kB (removed) 🏆
./bundle/devtoolsService-FOGO3YHI.js 0 B -27.7 kB (removed) 🏆
./bundle/gemini-6OJBBZVL.js 0 B -528 kB (removed) 🏆
./bundle/interactiveCli-JNWWP6FX.js 0 B -1.62 MB (removed) 🏆
./bundle/oauth2-provider-HBPGFC34.js 0 B -9.16 kB (removed) 🏆
./bundle/chunk-G3FC3IKR.js 3.4 kB +3.4 kB (new file) 🆕
./bundle/chunk-GYJARPTC.js 14.7 MB +14.7 MB (new file) 🆕
./bundle/chunk-ND7MNNIX.js 3.64 MB +3.64 MB (new file) 🆕
./bundle/core-MY6ZE5BV.js 43.7 kB +43.7 kB (new file) 🆕
./bundle/devtoolsService-6Q4QXX4C.js 27.7 kB +27.7 kB (new file) 🆕
./bundle/gemini-6JSPPICJ.js 528 kB +528 kB (new file) 🆕
./bundle/interactiveCli-RK6GF6Q5.js 1.62 MB +1.62 MB (new file) 🆕
./bundle/oauth2-provider-NHGAIL3C.js 9.16 kB +9.16 kB (new file) 🆕
ℹ️ View Unchanged
Filename Size Change
./bundle/chunk-34MYV7JD.js 2.45 kB 0 B
./bundle/chunk-5AUYMPVF.js 858 B 0 B
./bundle/chunk-664ZODQF.js 124 kB 0 B
./bundle/chunk-DAHVX5MI.js 206 kB 0 B
./bundle/chunk-GWQRIYMB.js 1.96 MB 0 B
./bundle/chunk-IUUIT4SU.js 56.5 kB 0 B
./bundle/chunk-RJTRUG2J.js 39.8 kB 0 B
./bundle/cleanup-BY7VIV6H.js 0 B -856 B (removed) 🏆
./bundle/devtools-36NN55EP.js 696 kB 0 B
./bundle/dist-T73EYRDX.js 356 B 0 B
./bundle/gemini.js 2.06 kB 0 B
./bundle/getMachineId-bsd-TXG52NKR.js 1.55 kB 0 B
./bundle/getMachineId-darwin-7OE4DDZ6.js 1.55 kB 0 B
./bundle/getMachineId-linux-SHIFKOOX.js 1.34 kB 0 B
./bundle/getMachineId-unsupported-5U5DOEYY.js 1.06 kB 0 B
./bundle/getMachineId-win-6KLLGOI4.js 1.72 kB 0 B
./bundle/memoryDiscovery-GX6HE5X5.js 922 B 0 B
./bundle/multipart-parser-KPBZEGQU.js 11.7 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/client/main.js 221 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/_client-assets.js 227 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/index.js 11.5 kB 0 B
./bundle/node_modules/@google/gemini-cli-devtools/dist/src/types.js 132 B 0 B
./bundle/sandbox-macos-permissive-open.sb 890 B 0 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB 0 B
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB 0 B
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB 0 B
./bundle/sandbox-macos-strict-open.sb 4.82 kB 0 B
./bundle/sandbox-macos-strict-proxied.sb 5.02 kB 0 B
./bundle/src-QVCVGIUX.js 47 kB 0 B
./bundle/tree-sitter-7U6MW5PS.js 274 kB 0 B
./bundle/tree-sitter-bash-34ZGLXVX.js 1.84 MB 0 B
./bundle/cleanup-6Z6FCAMT.js 856 B +856 B (new file) 🆕

compressed-size-action

@gemini-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Mar 24, 2026
@galz10
Copy link
Collaborator

galz10 commented Mar 25, 2026

Code Review

Scope: Pull Request #23712

This PR implements a strict visibility lockdown for .env and .env.* files across Linux, macOS, and Windows sandboxes, and adds generalized support for forbiddenPaths. Overall, the OS-specific strategies (Bubblewrap bind mounts, Seatbelt regex denies, and Windows Integrity Levels) are clever and well-architected. However, there are significant implementation flaws—particularly a shell globbing bug on Linux, a Mandatory Integrity Control (MIC) bypass on Windows, and potential command-line length limits—that undermine the security goals and require remediation before merging.

Metadata Review

  • The PR Title correctly follows the Conventional Commits specification (feat(sandbox): ...).
  • The PR Body is excellent. It clearly breaks down the mechanism used for each operating system, references the related issue, and provides actionable manual verification steps.

Concerns (Action Required)

Linux (packages/core/src/sandbox/linux/LinuxSandboxManager.ts & Tests):

  • Globbing Bug in find: The find command uses -not -path '*/.*' in an attempt to skip hidden directories like .git. Unfortunately, this pattern explicitly filters out any path containing a dot component, which completely excludes the very .env and .env.* files it is trying to find! As a result, the Linux sandbox will not mask any secret files. Additionally, find still traverses into ignored directories before filtering them out, which is inefficient.
    Suggested Improvement: Use -prune to explicitly skip targeted directories without breaking dotfile matching. Since spawnSync accepts an array of arguments, you can structure it safely like this:
    [dir, '-maxdepth', '3', '-type', 'd', '(', '-name', '.git', '-o', '-name', 'node_modules', '-o', '-name', '.venv', '-o', '-name', '__pycache__', ')', '-prune', '-o', '-type', 'f', ...findPatterns, '-print']
  • Synchronous Event Loop Blocking: The prepareCommand method is asynchronous, but it invokes spawnSync('find', ...) synchronously. Scanning the workspace up to depth 3 on every single command execution will block the Node.js event loop, potentially freezing the CLI/Agent during heavy tool usage.
    Suggested Improvement: Swap spawnSync for await spawnAsync('find', ...) to ensure the event loop remains responsive.
  • Directory Masking Failure: The maskPath generated for Bubblewrap is a file. If a user specifies a directory in req.policy?.forbiddenPaths (e.g., ['/home/user/.ssh']), Bubblewrap will attempt to bind a file over a directory and immediately abort the sandbox creation with a fatal error: bwrap: Can't bind /tmp/... over /home/user/.ssh: Not a directory.
    Suggested Improvement: You must determine if the forbidden path is a file or a directory before binding. If it's a directory, bind a 000-permission dummy directory over it instead of a dummy file.
  • Flawed Test Mocks: The spawnSync mock in the test blocks .env and .env.* files in the workspace root blindly returns a mocked string regardless of the actual arguments passed to find. This lack of fidelity is why the test passed despite the -not -path '*/.*' bug in the implementation.
    Suggested Improvement: Update the mock to assert that the arguments don't improperly exclude dotfiles to make this test robust.

Windows (packages/core/src/sandbox/windows/WindowsSandboxManager.ts & GeminiSandbox.cs):

  • Mandatory Integrity Control (MIC) Bypass: While secret .env files correctly receive a Medium(NW,NR) Integrity Level (blocking Low Integrity reads), arbitrary paths added via req.policy?.forbiddenPaths do not receive this label. The C# helper GeminiSandbox.exe only enforces --forbidden for its internal __read/__write commands. If an external sandboxed process (e.g., cmd.exe /c type <forbidden_file>) is executed, it runs with Low Integrity but will successfully read the forbidden path because Windows Low Integrity processes have read access to default Medium Integrity files.
    Suggested Improvement: Dynamically applying icacls to arbitrary forbiddenPaths (like system files) is highly dangerous, meaning Windows currently cannot securely enforce forbiddenPaths for external commands using this architecture. This limitation must be explicitly documented, or at a minimum, a warning should be logged when forbiddenPaths are supplied on Windows.
  • Synchronous Event Loop Blocking: Similar to Linux, findSecretFiles uses fs.readdirSync to scan the workspace synchronously, which blocks the event loop.
    Suggested Improvement: Refactor findSecretFiles to use fs.promises.readdir (or a lightweight async glob).
  • Fragile Error Handling: The try...catch block in prepareCommand wraps the entire directory traversal and icacls execution loop. If spawnAsync('icacls', ...) throws an error for a single file (e.g., the file is locked by another process), the entire try block aborts, silently abandoning the protection of all subsequent secret files.
    Suggested Improvement: Move the try...catch inside the inner loop so that a failure to secure one file logs a warning but allows the remaining files to be secured.
  • Command-Line Length Limits: Every discovered secret file is appended to the GeminiSandbox.exe command as a --forbidden <path> argument. Passing dozens of .env files (e.g., test fixtures) as command-line arguments risks exceeding the Windows maximum command-line length (32,767 characters), which would cause the spawn to fail entirely.
    Suggested Improvement: Write the forbidden paths to a temporary file (e.g., a .json or .txt manifest) and pass the manifest path to the helper (--forbidden-manifest <path>).
  • Unhandled Exceptions in C# Helper: When evaluating the internal commands __read or __write, the C# helper assumes the path argument is present and accesses args[argIndex + 1]. If invoked malformedly, this throws an IndexOutOfRangeException and crashes the process abruptly.
    Suggested Improvement: Add a bounds check: if (argIndex + 1 >= args.Length) { Console.WriteLine("Error: Missing path"); return 1; }.

Nits (Suggestions)

  • packages/core/src/sandbox/macos/seatbeltArgsBuilder.ts (Global Regex Impact):
    The Seatbelt regex .*/\.env$ is evaluated globally, denying access to .env files anywhere on the system (e.g., inside arbitrary node_modules folders or system paths). Linux and Windows restrict masking to a depth of 3 within the workspace/allowed paths. While secure, this platform inconsistency might cause unexpected "Access Denied" errors for legitimate tools that use dummy .env files. Consider anchoring the regex to the workspace path if a global ban isn't strictly intended.
  • packages/core/src/sandbox/linux/LinuxSandboxManager.ts (Temporary File Leaks):
    The getMaskPath method creates a 000-permission file in the OS temp directory (gemini-cli-mask-<pid>) but never cleans it up. Running the CLI many times will eventually clutter the /tmp directory.
    Suggested Improvement: Register a cleanup handler (e.g., process.on('exit', () => { try { fs.unlinkSync(maskPath); } catch {} })) to remove the mask file when the CLI exits.
  • packages/core/src/services/sandboxManager.ts (Hardcoded Directory Exclusions):
    findSecretFiles currently skips .git, node_modules, .venv, and __pycache__. It might also be worth adding other common heavy directories like dist, build, .next, or .idea to prevent unnecessary recursive traversal.

@DavidAPierce
Copy link
Contributor Author

Updated PR to merge with incoming changes and address comments.

Summary
This PR implements a strict visibility lockdown for secret files (specifically .env and .env.*) across all supported sandbox environments (Linux, macOS, and Windows). This ensures that sensitive credentials and API keys
are effectively invisible and inaccessible to any tools running within the sandbox, fulfilling the requirements of Issue #1560 (https://github.com/google-gemini/maintainers-gemini-cli/issues/1560).

Details & Review Remediation
Based on recent feedback, several significant implementation flaws were addressed to harden the security model and improve reliability:

Linux (Bubblewrap)

  • Fixed find Globbing: Resolved a bug where dot-files were accidentally excluded. Now uses -prune to skip heavy directories (e.g., node_modules, .git) while correctly matching .env secrets.
  • Asynchronous Discovery: Replaced spawnSync with spawnAsync for workspace scanning to prevent blocking the Node.js event loop.
  • Directory-Aware Masking: Implemented logic to detect if a forbidden path is a directory and uses a dummy directory for masking, preventing Bubblewrap initialization failures.
  • Leak Prevention: Added process-level cleanup handlers to remove temporary 000-permission mask files/directories on exit.

Windows (Low Integrity & Helper)

  • Command-Line manifest: Switched to a manifest file (--forbidden-manifest) to pass blocked paths to the C# helper, bypassing the 32,767-character command-line length limit.
  • Resilient ACL Enforcement: Moved icacls enforcement into a resilient loop so that a failure to secure one file (e.g., a file lock) does not abandon protection for the rest.
  • System Protection: Added safeguards to prevent applying integrity labels or Deny ACEs to system directories (e.g., C:\Windows).
  • Helper Hardening: Added bounds checks to the C# internal command parser and implemented strict manifest-based path enforcement for __read and __write.

macOS (Seatbelt)

  • Anchored Regex: Anchored secret-blocking regex patterns to the workspace and allowed paths. This prevents accidental "Access Denied" errors for system-level .env files while maintaining total protection for project
    secrets.
  • Regex Injection Protection: Implemented proper escaping for workspace paths used within Seatbelt regex rules.

Unified Secret Service

  • Asynchronous findSecretFiles: Fully refactored the core secret discovery logic to use fs.promises, ensuring the CLI remains responsive during deep workspace scans.
  • Expanded Exclusions: Added dist, build, .next, and .idea to the default traversal ignore list for better performance.

Related Issues
Fixes https://github.com/google-gemini/maintainers-gemini-cli/issues/1560

How to Validate

  1. Automated Tests:
    • npm test packages/core/src/services/sandboxManager.test.ts
    • npm test packages/core/src/sandbox/linux/LinuxSandboxManager.test.ts
  2. Manual Verification:
    • Create a .env file in the workspace.
    • Run a sandboxed command (e.g., cat .env).
    • Expected: Output is Permission denied or Access is denied.

Pre-Merge Checklist

  • Resolved Linux globbing and sync-blocking issues
  • Implemented Windows manifest files for path limits
  • Anchored macOS Seatbelt rules to workspace
  • Added resilience and system directory protection
  • Verified all changes with updated asynchronous test suites

const escapedBase = escapeRegex(resolvedBase);
if (secret.pattern.endsWith('*')) {
// .env.* -> .env\..+ (match .env followed by dot and something)
const basePattern = secret.pattern.slice(0, -1).replace(/\./g, '\\.');

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.
regexPattern = `^${escapedBase}/.*${basePattern}[^/]+$`;
} else {
// .env -> \.env$
const basePattern = secret.pattern.replace(/\./g, '\\.');

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants