Skip to content

Commit

Permalink
Fix security vulnerability, add --no-pretty-print argument to envio l…
Browse files Browse the repository at this point in the history
…ist command, and update update-checking process
  • Loading branch information
Humble Penguin committed Apr 3, 2023
1 parent 772cdbc commit ed18848
Show file tree
Hide file tree
Showing 11 changed files with 399 additions and 197 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "envio"
version = "0.3.0"
version = "0.4.0"
rust-version = "1.64.0"
description = "Envio is a command-line tool that simplifies the management of environment variables across multiple profiles. It allows users to easily switch between different configurations and apply them to their current environment. Envio also encrypts sensitive environment variable values to ensure secure storage and transmission"
edition = "2021"
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

[![CICD](https://github.com/humblepenguinn/envio/actions/workflows/CICD.yml/badge.svg)](https://github.com/humblepenguinn/envio/workflows/CICD.yml)
[![Version info](https://img.shields.io/crates/v/envio.svg)](https://crates.io/crates/envio)

</div>

<p align="center">
Expand All @@ -20,13 +21,14 @@

---

<img alt="Demo" src="https://vhs.charm.sh/vhs-4CTjlTKDNnQqgXYMPFe4ot.gif" width="600" />
<img alt="Demo" src="https://vhs.charm.sh/vhs-3hIrmMhWXj614OI9okDEHt.gif" width="600" />

## About

`envio` is an open source CLI tool that helps make managing environment variables a breeze. With `envio`, users can create encrypted profiles that contain a collection of environment variables associated with a specific project or use case. `envio` ensures security and simplifies the development process by allowing users to easily switch between profiles as needed and load them in their current terminal session for immediate use.

Some key features of `envio` include:

- `Encrypted` profiles which can only be decrypted using a `key`
- Load profiles into your `terminal sessions`
- `Persistent` environment variables that are available in `future sessions`
Expand All @@ -36,35 +38,41 @@ Some key features of `envio` include:
- `Exporting` profiles to a file

## Profiles

In `envio`, a profile is a collection of environment variables that are associated with a specific project, application, or use case. Users can create multiple profiles, each with their own set of environment variables, and easily switch between them as needed.

For example, a developer might create a profile for a web development project that includes environment variables for the database connection, API keys, and other project-specific settings. They could then switch to a different profile for a mobile app project that requires a different set of environment variables.

The benefit of using profiles is that users can easily manage and switch between different sets of environment variables without having to manually set and unset them every time they switch tasks. Additionally, in `envio`, profiles are encrypted, so users can rest assured that their sensitive environment variables are secure and require a key to access them.

## Installation

You can install `envio` through a few methods

### Releases

You can head over to the [releases page](https://github.com/humblepenguinn/envio/releases/latest) and download the official `envio` binaries from there for your target operating system. `Windows MSI installers` are also available

### Cargo Repository

You can install `envio` through the Cargo repository using the following command:

```sh
$ cargo install envio
```

### Source
Go [here](./docs/build_from_source.md) to see how

Go [here](./docs/build_from_source.md) to see how

More methods of installation will be added in the future!

## Usage

Go [here](./docs/usage.md) to see how to use the tool

## Development

In addition to the command-line tool, `envio` can also be used as a library in Rust programs to manage environment variables. To use `envio` in your program, add it as a dependency in your Cargo.toml file:

Please note that the envio library is not stable right now and can be subjected to many changes!
Expand All @@ -76,6 +84,7 @@ envio = "0.1.0"

Then, in your Rust code, you can use the `envio` crate to read and write environment variables
Here's a simple example:

```rust
// In this example we get the profile passed as an argument to the program
// and then print the environment variables in that profile
Expand Down Expand Up @@ -130,7 +139,9 @@ Currently, `envio` is only available as a Rust library
<!-- ALL-CONTRIBUTORS-LIST:END -->

## Contributing

Contributions to `envio` are always welcome! Please see the [Contributing Guidelines](CONTRIBUTING.md) for more information.

## License

This project is licensed under the [MIT](LICENSE-MIT) License and the [Apache](LICENSE-APACHE) License
1 change: 0 additions & 1 deletion completions/_envio
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ _arguments "${_arguments_options[@]}" \
_arguments "${_arguments_options[@]}" \
'-h[Print help]' \
'--help[Print help]' \
'*::args:' \
&& ret=0
;;
(launch)
Expand Down
2 changes: 1 addition & 1 deletion completions/envio.bash
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ _envio() {
return 0
;;
envio__unload)
opts="-h --help [ARGS]..."
opts="-h --help"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand Down
27 changes: 27 additions & 0 deletions docs/envio-profile-loading-update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Important Security Notice for Envio Users

### Security Vulnerability in Versions Before 0.4.0

A security vulnerability has recently been discovered in versions prior to `0.4.0`. Before version `0.4.0`, users could load their profile using the `envio load <profile_name>` command and load the environment variables from their profile persistently. However, the way this was achieved was risky and insecure.

Specifically, the `envio load` command would take in the user key, decrypt the contents in the profile, take the environment variables, and then write them to the `setenv.sh` file, which would be sourced from the user's shell. Since the environment variables were written in plain text in `setenv.sh`, it became vulnerable to potential security breaches.

While it was not completely unsecure because users still had to pass in their key before loading in the environment variables from their profile and only then would the environment variables get written to and exported from the `setenv.sh` script, Nevertheless it was not a good approach and is not recommended.

### Updated Approach in Versions After 0.4.0

Now, with the update in version `0.4.0`, `envio` has implemented an updated approach. Whenever users use the `envio load` command, it creates a `setenv.sh` script (as before) that, whenever the users load their shell, asks for the user's key. If the key is correct, it decrypts the envs in the profile, writes them to a temporary file, sources the temporary file, and then deletes it. This approach ensures that the user's environment variables remain secure and are not exposed in plain text.

With this new approach, users can still load their profiles as before using the `envio load` command, but now whenever they open their shell, they need to enter their key to access their environment variables.

Users can also still use the `envio unload` command to unload the profile from their terminal sessions, but they do not need to pass the `profile name` as a argument anymore

### Future Improvements in Envio

`envio` is committed to continuously improving the way it loads environment variables until it reaches a certain level of satisfaction. Until version 1.0.0, `envio` will keep working on improving the way it loads environment variables, and users can expect changes in the approach. However, after version 1.0.0, `envio` will stabilize the approach, and users can expect fewer changes that won't be breaking.

We strongly encourage all users to upgrade to version 0.4.0 and take necessary measures to ensure their environment variables are secure.

### User Feedback

We would love to hear your thoughts and feedback on the new approach we use to load environment variables. If you have any suggestions or improvements, please don't hesitate to reach out to us at `humblepenguinofficial@gmail.com`
22 changes: 21 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,25 @@ You can use the `envio load <profile_name>` command to load the profile and make
$ envio load myprofile
```

On `Windows`, users just need to reload their shell and they can start using their environment variables as before. However, on `Unix-based` operating systems, a new approach has been implemented to load the environment variables securely. Whenever users open their shell, envio now asks users for the key used for the profile that was loaded. They have to type in the key to access their environment variables, which are then stored in a temporary file and sourced in the current session. This ensures that the environment variables are loaded securely and are not accessible to anyone without the correct key.

Now,
```sh
$ echo $DATABASE_URL
```

## Unloading a Profile

To unload a profile from the current session, run the command:
On `Windows`, to unload a profile from the current session, run the command:
```sh
$ envio unload myprofile
```

On `Unix-based` operating systems, to unload a profile from the current session, run the `envio unload` command without any arguments:
```sh
$ envio unload
```

## Launching a Program with a Profile

The `envio launch` command allows you to run a program using a specific profile. This is useful when you need to switch between different sets of environment variables for different projects or environments.
Expand Down Expand Up @@ -152,3 +160,15 @@ To list all the environment variables in a profile, users can run the following
```sh
$ envio list <profile_name>
```

Users can also view their profiles and environment variables in a profile without any visual formatting using the `-no-pretty-print` argument

To list all existing profiles:
```sh
$ envio list profiles -- --no-pretty-print
```

To list all the environment variables in a profile:
```sh
$ envio list <profile_name> -- --no-pretty-print
```
5 changes: 4 additions & 1 deletion src/bin/envio/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use clap::Args;
use clap::Parser;
//use crate::commands::Command;

#[derive(Parser)]
/*
Expand Down Expand Up @@ -35,6 +34,10 @@ pub enum Command {
Add(CommandArgs),
#[clap(name = "load", about = "Load a profile in the current session")]
Load(CommandArgs),
#[cfg(target_family = "unix")]
#[clap(name = "unload", about = "Unload a profile from the current session")]
Unload,
#[cfg(target_family = "windows")]
#[clap(name = "unload", about = "Unload a profile from the current session")]
Unload(CommandArgs),
#[clap(name = "launch", about = "Launch a program with a profile")]
Expand Down
73 changes: 61 additions & 12 deletions src/bin/envio/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use envio::crypto::encrypt;
use envio::utils::get_configdir;
use envio::{
self, check_profile, create_profile, delete_profile, download_profile, get_profile,
import_profile, list_profiles,
import_profile, list_profiles, load_profile, unload_profile,
};

use crate::cli::Command;
Expand Down Expand Up @@ -49,6 +49,22 @@ impl Command {
return;
}

let profile_name;

if command_args.args.len() == 1 {
profile_name = command_args.args[0].clone();
} else if command_args.args.len() == 2 {
profile_name = command_args.args[1].clone();
} else {
println!("{}: Invalid number of arguments", "Error".red());
return;
}

if check_profile(profile_name.to_string()) {
println!("{}: Profile already exists", "Error".red());
return;
}

let prompt = Password::new("Enter your encryption key:")
.with_display_toggle_enabled()
.with_display_mode(PasswordDisplayMode::Masked)
Expand All @@ -68,13 +84,12 @@ impl Command {
};

if command_args.args.len() == 1 {
create_profile(command_args.args[0].clone(), None, &user_key);
create_profile(profile_name, None, &user_key);
} else if command_args.args.len() == 2 {
if !Path::new(&command_args.args[0]).exists() {
println!("{}: File does not exist", "Error".red());
return;
}
let profile_name = command_args.args[1].clone();

let mut file = std::fs::OpenOptions::new()
.read(true)
Expand Down Expand Up @@ -191,29 +206,45 @@ impl Command {
}

let profile_name = command_args.args[0].clone();
let profile = if let Some(p) = get_profile(profile_name, &get_userkey()) {
p
} else {
return;
};

profile.load_profile();
#[cfg(target_family = "unix")]
{
load_profile(&profile_name);
}

#[cfg(target_family = "windows")]
{
let profile = if let Some(p) = get_profile(profile_name, &get_userkey()) {
p
} else {
return;
};

load_profile(profile);
}
}

#[cfg(target_family = "unix")]
Command::Unload => {
unload_profile();
}

#[cfg(target_family = "windows")]
Command::Unload(command_args) => {
if command_args.args.is_empty() {
println!("{}: Invalid number of arguments", "Error".red());
return;
}

let profile_name = command_args.args[0].clone();

let profile = if let Some(p) = get_profile(profile_name, &get_userkey()) {
p
} else {
return;
};

profile.unload_profile();
unload_profile(profile);
}

Command::Launch(command_args) => {
Expand Down Expand Up @@ -275,9 +306,27 @@ impl Command {
}

let profile_name = command_args.args[0].clone();

if command_args.args.len() == 2 {
if command_args.args[1] == "--no-pretty-print" {
if profile_name == "profiles" {
list_profiles(true)
} else {
let profile = if let Some(p) = get_profile(profile_name, &get_userkey())
{
p
} else {
return;
};

for (key, value) in profile.envs.iter() {
println!("{}={}", key, value);
}
}
}
return;
}
if profile_name == "profiles" {
list_profiles()
list_profiles(false)
} else {
let profile = if let Some(p) = get_profile(profile_name, &get_userkey()) {
p
Expand Down
Loading

0 comments on commit ed18848

Please sign in to comment.