Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Tuan Dang committed Nov 26, 2022
2 parents 802f367 + a18e04a commit a027b77
Show file tree
Hide file tree
Showing 25 changed files with 290 additions and 105 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Not sure where to get started? [Book a free, non-pressure pairing sessions with

- [GitHub Discussions](https://github.com/Infisical/infisical/discussions) for help with building and discussion.
- [GitHub Issues](https://github.com/Infisical/infisical-cli/issues) for any bugs and errors you encounter using Infisical.
- [Community Slack](https://join.slack.com/t/infisical/shared_invite/zt-1dgg63ln8-G7PCNJdCymAT9YF3j1ewVA) for hanging out with the community and quick communication with the team.
- [Community Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g) for hanging out with the community and quick communication with the team.

## Status

Expand Down
16 changes: 15 additions & 1 deletion cli/packages/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ var runCmd = &cobra.Command{
return
}

substitute, err := cmd.Flags().GetBool("substitute")
if err != nil {
log.Errorln("Unable to parse the substitute flag")
log.Debugln(err)
return
}

projectId, err := cmd.Flags().GetString("projectId")
if err != nil {
log.Errorln("Unable to parse the project id flag")
Expand Down Expand Up @@ -82,14 +89,21 @@ var runCmd = &cobra.Command{
}
}

execCmd(args[0], args[1:], envsFromApi)
if substitute {
substitutions := util.SubstituteSecrets(envsFromApi)
execCmd(args[0], args[1:], substitutions)
} else {
execCmd(args[0], args[1:], envsFromApi)
}

},
}

func init() {
rootCmd.AddCommand(runCmd)
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
runCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from")
runCmd.Flags().Bool("substitute", true, "Parse shell variable substitutions in your secrets")
}

// Credit: inspired by AWS Valut
Expand Down
14 changes: 14 additions & 0 deletions cli/packages/models/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package models

import log "github.com/sirupsen/logrus"

// Custom error type so that we can give helpful messages in CLI
type Error struct {
Err error
DebugMessage string
FriendlyMessage string
}

func (e *Error) printFriendlyMessage() {
log.Infoln(e.FriendlyMessage)
}
71 changes: 71 additions & 0 deletions cli/packages/util/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"regexp"
"strings"

"github.com/Infisical/infisical-merge/packages/models"
Expand Down Expand Up @@ -205,3 +206,73 @@ func GetWorkSpacesFromAPI(userCreds models.UserCredentials) (workspaces []models

return getWorkSpacesResponse.Workspaces, nil
}

func getExpandedEnvVariable(secrets []models.SingleEnvironmentVariable, variableWeAreLookingFor string, hashMapOfCompleteVariables map[string]string, hashMapOfSelfRefs map[string]string) string {
if value, found := hashMapOfCompleteVariables[variableWeAreLookingFor]; found {
return value
}

for _, secret := range secrets {
if secret.Key == variableWeAreLookingFor {
regex := regexp.MustCompile(`\${([^\}]*)}`)
variablesToPopulate := regex.FindAllString(secret.Value, -1)

// case: variable is a constant so return its value
if len(variablesToPopulate) == 0 {
return secret.Value
}

valueToEdit := secret.Value
for _, variableWithSign := range variablesToPopulate {
variableWithoutSign := strings.Trim(variableWithSign, "}")
variableWithoutSign = strings.Trim(variableWithoutSign, "${")

// case: reference to self
if variableWithoutSign == secret.Key {
hashMapOfSelfRefs[variableWithoutSign] = variableWithoutSign
continue
} else {
var expandedVariableValue string

if preComputedVariable, found := hashMapOfCompleteVariables[variableWithoutSign]; found {
expandedVariableValue = preComputedVariable
} else {
expandedVariableValue = getExpandedEnvVariable(secrets, variableWithoutSign, hashMapOfCompleteVariables, hashMapOfSelfRefs)
hashMapOfCompleteVariables[variableWithoutSign] = expandedVariableValue
}

// If after expanding all the vars above, is the current var a self ref? if so no replacement needed for it
if _, found := hashMapOfSelfRefs[variableWithoutSign]; found {
continue
} else {
valueToEdit = strings.ReplaceAll(valueToEdit, variableWithSign, expandedVariableValue)
}
}
}

return valueToEdit

} else {
continue
}
}

return "${" + variableWeAreLookingFor + "}"
}

func SubstituteSecrets(secrets []models.SingleEnvironmentVariable) []models.SingleEnvironmentVariable {
hashMapOfCompleteVariables := make(map[string]string)
hashMapOfSelfRefs := make(map[string]string)
expandedSecrets := []models.SingleEnvironmentVariable{}

for _, secret := range secrets {
expandedVariable := getExpandedEnvVariable(secrets, secret.Key, hashMapOfCompleteVariables, hashMapOfSelfRefs)
expandedSecrets = append(expandedSecrets, models.SingleEnvironmentVariable{
Key: secret.Key,
Value: expandedVariable,
})

}

return expandedSecrets
}
160 changes: 160 additions & 0 deletions cli/packages/util/secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package util

import (
"testing"

"github.com/Infisical/infisical-merge/packages/models"
)

// References to self should return the value unaltered
func Test_SubstituteSecrets_When_ReferenceToSelf(t *testing.T) {

var tests = []struct {
Key string
Value string
ExpectedValue string
}{
{Key: "A", Value: "${A}", ExpectedValue: "${A}"},
{Key: "A", Value: "${A} ${A}", ExpectedValue: "${A} ${A}"},
{Key: "A", Value: "${A}${A}", ExpectedValue: "${A}${A}"},
}

for _, test := range tests {
secret := models.SingleEnvironmentVariable{
Key: test.Key,
Value: test.Value,
}

secrets := []models.SingleEnvironmentVariable{secret}
result := SubstituteSecrets(secrets)

if result[0].Value != test.ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected %s but got %s for input %s", test.ExpectedValue, result[0].Value, test.Value)
}

}
}

func Test_SubstituteSecrets_When_ReferenceDoesNotExist(t *testing.T) {

var tests = []struct {
Key string
Value string
ExpectedValue string
}{
{Key: "A", Value: "${X}", ExpectedValue: "${X}"},
{Key: "A", Value: "${H}HELLO", ExpectedValue: "${H}HELLO"},
{Key: "A", Value: "${L}${S}", ExpectedValue: "${L}${S}"},
}

for _, test := range tests {
secret := models.SingleEnvironmentVariable{
Key: test.Key,
Value: test.Value,
}

secrets := []models.SingleEnvironmentVariable{secret}
result := SubstituteSecrets(secrets)

if result[0].Value != test.ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected %s but got %s for input %s", test.ExpectedValue, result[0].Value, test.Value)
}

}
}

func Test_SubstituteSecrets_When_ReferenceDoesNotExist_And_Self_Referencing(t *testing.T) {

tests := []struct {
Key string
Value string
ExpectedValue string
}{
{
Key: "O",
Value: "${P} ==$$ ${X} ${UNKNOWN} ${A}",
ExpectedValue: "DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A}",
},
{
Key: "X",
Value: "DOMAIN",
ExpectedValue: "DOMAIN",
},
{
Key: "A",
Value: "*${A}* ${X}",
ExpectedValue: "*${A}* DOMAIN",
},
{
Key: "H",
Value: "${X} >>>",
ExpectedValue: "DOMAIN >>>",
},
{
Key: "P",
Value: "DOMAIN === ${A} ${H}",
ExpectedValue: "DOMAIN === ${A} DOMAIN >>>",
},
{
Key: "T",
Value: "${P} ==$$ ${X} ${UNKNOWN} ${A} ${P} ==$$ ${X} ${UNKNOWN} ${A}",
ExpectedValue: "DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A} DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A}",
},
{
Key: "S",
Value: "${ SSS$$ ${HEY}",
ExpectedValue: "${ SSS$$ ${HEY}",
},
}

secrets := []models.SingleEnvironmentVariable{}
for _, test := range tests {
secrets = append(secrets, models.SingleEnvironmentVariable{Key: test.Key, Value: test.Value})
}

results := SubstituteSecrets(secrets)

for index, expanded := range results {
if expanded.Value != tests[index].ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected [%s] but got [%s] for input [%s]", tests[index].ExpectedValue, expanded.Value, tests[index].Value)
}
}
}

func Test_SubstituteSecrets_When_No_SubstituteNeeded(t *testing.T) {

tests := []struct {
Key string
Value string
ExpectedValue string
}{
{
Key: "DOMAIN",
Value: "infisical.com",
ExpectedValue: "infisical.com",
},
{
Key: "API_KEY",
Value: "hdgsvjshcgkdckhevdkd",
ExpectedValue: "hdgsvjshcgkdckhevdkd",
},
{
Key: "ENV",
Value: "PROD",
ExpectedValue: "PROD",
},
}

secrets := []models.SingleEnvironmentVariable{}
for _, test := range tests {
secrets = append(secrets, models.SingleEnvironmentVariable{Key: test.Key, Value: test.Value})
}

results := SubstituteSecrets(secrets)

for index, expanded := range results {
if expanded.Value != tests[index].ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected [%s] but got [%s] for input [%s]", tests[index].ExpectedValue, expanded.Value, tests[index].Value)
}
}
}
2 changes: 1 addition & 1 deletion frontend/components/basic/dialog/AddServiceTokenDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import nacl from "tweetnacl";
import addServiceToken from "~/pages/api/serviceToken/addServiceToken";
import getLatestFileKey from "~/pages/api/workspace/getLatestFileKey";

import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/crypto";
import { decryptAssymmetric, encryptAssymmetric } from "../../utilities/cryptography/crypto";
import Button from "../buttons/Button";
import InputField from "../InputField";
import ListBox from "../Listbox";
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/basic/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import createWorkspace from "~/pages/api/workspace/createWorkspace";
import getWorkspaces from "~/pages/api/workspace/getWorkspaces";

import NavBarDashboard from "../navigation/NavBarDashboard";
import { decryptAssymmetric, encryptAssymmetric } from "../utilities/crypto";
import { decryptAssymmetric, encryptAssymmetric } from "../utilities/cryptography/crypto";
import Button from "./buttons/Button";
import AddWorkspaceDialog from "./dialog/AddWorkspaceDialog";
import Listbox from "./Listbox";
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/basic/table/UserTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Listbox from "../Listbox";
const {
decryptAssymmetric,
encryptAssymmetric,
} = require("../../utilities/crypto");
} = require("../../utilities/cryptography/crypto");
const nacl = require("tweetnacl");
nacl.util = require("tweetnacl-util");

Expand Down
Loading

0 comments on commit a027b77

Please sign in to comment.