From a6180ba53be143fb19c5860c01c83c7bccba9790 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Tue, 17 Feb 2026 18:28:11 +0530 Subject: [PATCH 01/14] feat: add support for stdin for input --- common/common.go | 22 ++++++++++++++++++++++ lib/encrypt/encrypt.go | 36 +++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/common/common.go b/common/common.go index bdb677c..bee92c6 100644 --- a/common/common.go +++ b/common/common.go @@ -49,6 +49,28 @@ func ReadDataFromFile(filePath string) (string, error) { return string(content), nil } +// ReadDataFromStdin - function to read data from stdin +func ReadDataFromStdin() (string, error) { + content, err := io.ReadAll(os.Stdin) + if err != nil { + return "", fmt.Errorf("failed to read from stdin: %w", err) + } + return string(content), nil +} + +// IsStdinAvailable - function to check if stdin has data available (piped or redirected) +func IsStdinAvailable() bool { + stat, err := os.Stdin.Stat() + if err != nil { + return false + } + // Check if stdin is a pipe or file (not a character device like terminal) + // This returns true when data is piped: echo "data" | command + // This returns false when running interactively in terminal + mode := stat.Mode() + return (mode&os.ModeCharDevice) == 0 && stat.Size() > 0 +} + // WriteDataToFile - function to write data to file (create file if doesn't exists) func WriteDataToFile(filePath, data string) error { DataFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) diff --git a/lib/encrypt/encrypt.go b/lib/encrypt/encrypt.go index 7ef6055..55476cf 100644 --- a/lib/encrypt/encrypt.go +++ b/lib/encrypt/encrypt.go @@ -31,7 +31,7 @@ const ( Creates a cryptographically signed contract using your private key and encrypts it with the platform's encryption certificate. Supports optional contract expiry for enhanced security.` - InputFlagDescription = "Path to unencrypted contract YAML file" + InputFlagDescription = "Path to unencrypted contract YAML file (use '-' for stdin)" OutputFlagDescription = "Path to save signed and encrypted contract" ContractExpiryFlag = "contract-expiry" DefaultContractExpiryFlag = false @@ -68,6 +68,18 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, string, common.SetMandatoryFlagError(cmd, err) } + // Check if "-" is specified but no stdin is available + if inputData == "-" && !common.IsStdinAvailable() { + err := fmt.Errorf("Error: '--in -' specified but no stdin data detected. Pipe data to stdin or use a file path instead") + common.SetMandatoryFlagError(cmd, err) + } + + // Check if stdin has data when a file path (not "-") is specified + if inputData != "-" && common.IsStdinAvailable() { + err := fmt.Errorf("Error: stdin data detected but --in specifies a file path '%s'. Use '--in -' to read from stdin or remove piped input to read from file", inputData) + common.SetMandatoryFlagError(cmd, err) + } + osVersion, err := cmd.Flags().GetString(OsVersionFlagName) if err != nil { return "", "", "", "", "", err @@ -179,13 +191,23 @@ func GenerateSignedEncryptContractExpiry(inputDataPath, osVersion, certPath, pri // commonParameters - function to fetch common details func commonParameters(inputDataPath, certPath, privateKeyPath string) (string, string, string, error) { - if !common.CheckFileFolderExists(inputDataPath) { - return "", "", "", fmt.Errorf("the contract path doesn't exist") - } + var inputData string + var err error - inputData, err := common.ReadDataFromFile(inputDataPath) - if err != nil { - return "", "", "", err + if inputDataPath == "-" { + inputData, err = common.ReadDataFromStdin() + if err != nil { + return "", "", "", fmt.Errorf("failed to read from stdin: %w", err) + } + } else { + if !common.CheckFileFolderExists(inputDataPath) { + return "", "", "", fmt.Errorf("the contract path doesn't exist") + } + + inputData, err = common.ReadDataFromFile(inputDataPath) + if err != nil { + return "", "", "", err + } } cert, err := common.GetDataFromFile(certPath) From 33651f0fa2eddc59a2016259907096c3b597805a Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Tue, 17 Feb 2026 20:57:51 +0530 Subject: [PATCH 02/14] feat: resolving review comments and adding stdin functionality to base64 command --- common/common.go | 18 +++++++++++++++++- lib/base64/base64.go | 20 +++++++++++++++++--- lib/encrypt/encrypt.go | 17 ++++------------- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/common/common.go b/common/common.go index bee92c6..2d0d25c 100644 --- a/common/common.go +++ b/common/common.go @@ -53,7 +53,7 @@ func ReadDataFromFile(filePath string) (string, error) { func ReadDataFromStdin() (string, error) { content, err := io.ReadAll(os.Stdin) if err != nil { - return "", fmt.Errorf("failed to read from stdin: %w", err) + return "", fmt.Errorf("failed to read from Standard Input: %w", err) } return string(content), nil } @@ -71,6 +71,22 @@ func IsStdinAvailable() bool { return (mode&os.ModeCharDevice) == 0 && stat.Size() > 0 } +// ValidateStdinInput - function to validate stdin input conflicts +// Returns error if there's a conflict between stdin and input parameter +func ValidateStdinInput(cmd *cobra.Command, inputData string) { + // Check if "-" is specified but no stdin is available + if inputData == "-" && !IsStdinAvailable() { + err := fmt.Errorf("Error: '--in -' specified but no Standard Input data detected. Pipe data to Standard Input or use a file path instead") + SetMandatoryFlagError(cmd, err) + } + + // Check if stdin has data when input data (not "-") is specified + if inputData != "-" && IsStdinAvailable() { + err := fmt.Errorf("Error: Standard Input data detected but --in specifies a file path '%s'. Use '--in -' to read from Standard Input or remove piped input to read from file", inputData) + SetMandatoryFlagError(cmd, err) + } +} + // WriteDataToFile - function to write data to file (create file if doesn't exists) func WriteDataToFile(filePath, data string) error { DataFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) diff --git a/lib/base64/base64.go b/lib/base64/base64.go index 2058ddf..fb3319f 100644 --- a/lib/base64/base64.go +++ b/lib/base64/base64.go @@ -31,7 +31,7 @@ const ( Useful for encoding data that needs to be included in contracts or configurations.` InputFlagName = "in" - InputFlagDescription = "Input data to encode (text or JSON)" + InputFlagDescription = "Input data to encode (text or JSON, use '-' for Standard Input)" FormatFlagName = "format" FormatFlagDescription = "Input data format (text or json)" @@ -59,6 +59,9 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, error) { common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input conflicts + common.ValidateStdinInput(cmd, inputData) + formatType, err := cmd.Flags().GetString(FormatFlagName) if err != nil { return "", "", "", err @@ -76,14 +79,25 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, error) { func Process(inputData, formatType string) (string, error) { var base64String string var err error + var data string + + // Check if input is from stdin + if inputData == "-" { + data, err = common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("failed to read from Standard Input: %w", err) + } + } else { + data = inputData + } if formatType == TextFormat { - base64String, _, _, err = contract.HpcrText(inputData) + base64String, _, _, err = contract.HpcrText(data) if err != nil { return "", err } } else if formatType == JsonFormat { - base64String, _, _, err = contract.HpcrJson(inputData) + base64String, _, _, err = contract.HpcrJson(data) if err != nil { return "", err } diff --git a/lib/encrypt/encrypt.go b/lib/encrypt/encrypt.go index 55476cf..c7b8780 100644 --- a/lib/encrypt/encrypt.go +++ b/lib/encrypt/encrypt.go @@ -31,7 +31,7 @@ const ( Creates a cryptographically signed contract using your private key and encrypts it with the platform's encryption certificate. Supports optional contract expiry for enhanced security.` - InputFlagDescription = "Path to unencrypted contract YAML file (use '-' for stdin)" + InputFlagDescription = "Path to unencrypted contract YAML file (use '-' for Standard Input)" OutputFlagDescription = "Path to save signed and encrypted contract" ContractExpiryFlag = "contract-expiry" DefaultContractExpiryFlag = false @@ -68,17 +68,8 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, string, common.SetMandatoryFlagError(cmd, err) } - // Check if "-" is specified but no stdin is available - if inputData == "-" && !common.IsStdinAvailable() { - err := fmt.Errorf("Error: '--in -' specified but no stdin data detected. Pipe data to stdin or use a file path instead") - common.SetMandatoryFlagError(cmd, err) - } - - // Check if stdin has data when a file path (not "-") is specified - if inputData != "-" && common.IsStdinAvailable() { - err := fmt.Errorf("Error: stdin data detected but --in specifies a file path '%s'. Use '--in -' to read from stdin or remove piped input to read from file", inputData) - common.SetMandatoryFlagError(cmd, err) - } + // Validate stdin input conflicts + common.ValidateStdinInput(cmd, inputData) osVersion, err := cmd.Flags().GetString(OsVersionFlagName) if err != nil { @@ -197,7 +188,7 @@ func commonParameters(inputDataPath, certPath, privateKeyPath string) (string, s if inputDataPath == "-" { inputData, err = common.ReadDataFromStdin() if err != nil { - return "", "", "", fmt.Errorf("failed to read from stdin: %w", err) + return "", "", "", fmt.Errorf("failed to read from Standard Input: %w", err) } } else { if !common.CheckFileFolderExists(inputDataPath) { From c5183f1bb75e71590d2a44d689a8a6070ea8a510 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Tue, 17 Feb 2026 21:03:03 +0530 Subject: [PATCH 03/14] feat: resolving review comments --- common/common.go | 6 +++--- lib/base64/base64.go | 4 ++-- lib/encrypt/encrypt.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/common.go b/common/common.go index 2d0d25c..c6f73dc 100644 --- a/common/common.go +++ b/common/common.go @@ -53,7 +53,7 @@ func ReadDataFromFile(filePath string) (string, error) { func ReadDataFromStdin() (string, error) { content, err := io.ReadAll(os.Stdin) if err != nil { - return "", fmt.Errorf("failed to read from Standard Input: %w", err) + return "", fmt.Errorf("unable to read input from standard input: %w", err) } return string(content), nil } @@ -76,13 +76,13 @@ func IsStdinAvailable() bool { func ValidateStdinInput(cmd *cobra.Command, inputData string) { // Check if "-" is specified but no stdin is available if inputData == "-" && !IsStdinAvailable() { - err := fmt.Errorf("Error: '--in -' specified but no Standard Input data detected. Pipe data to Standard Input or use a file path instead") + err := fmt.Errorf("Error: '--in -' specified but no standard input data detected. Pipe data to standard input or use a file path instead") SetMandatoryFlagError(cmd, err) } // Check if stdin has data when input data (not "-") is specified if inputData != "-" && IsStdinAvailable() { - err := fmt.Errorf("Error: Standard Input data detected but --in specifies a file path '%s'. Use '--in -' to read from Standard Input or remove piped input to read from file", inputData) + err := fmt.Errorf("Error: standard input data detected but --in specifies a file path '%s'. Use '--in -' to read from standard input or remove piped input to read from file", inputData) SetMandatoryFlagError(cmd, err) } } diff --git a/lib/base64/base64.go b/lib/base64/base64.go index fb3319f..2d11280 100644 --- a/lib/base64/base64.go +++ b/lib/base64/base64.go @@ -31,7 +31,7 @@ const ( Useful for encoding data that needs to be included in contracts or configurations.` InputFlagName = "in" - InputFlagDescription = "Input data to encode (text or JSON, use '-' for Standard Input)" + InputFlagDescription = "Input data to encode (text or JSON, use '-' for standard input)" FormatFlagName = "format" FormatFlagDescription = "Input data format (text or json)" @@ -85,7 +85,7 @@ func Process(inputData, formatType string) (string, error) { if inputData == "-" { data, err = common.ReadDataFromStdin() if err != nil { - return "", fmt.Errorf("failed to read from Standard Input: %w", err) + return "", fmt.Errorf("unable to read input from standard input: %w", err) } } else { data = inputData diff --git a/lib/encrypt/encrypt.go b/lib/encrypt/encrypt.go index c7b8780..ff465dc 100644 --- a/lib/encrypt/encrypt.go +++ b/lib/encrypt/encrypt.go @@ -31,7 +31,7 @@ const ( Creates a cryptographically signed contract using your private key and encrypts it with the platform's encryption certificate. Supports optional contract expiry for enhanced security.` - InputFlagDescription = "Path to unencrypted contract YAML file (use '-' for Standard Input)" + InputFlagDescription = "Path to unencrypted contract YAML file (use '-' for standard input)" OutputFlagDescription = "Path to save signed and encrypted contract" ContractExpiryFlag = "contract-expiry" DefaultContractExpiryFlag = false @@ -188,7 +188,7 @@ func commonParameters(inputDataPath, certPath, privateKeyPath string) (string, s if inputDataPath == "-" { inputData, err = common.ReadDataFromStdin() if err != nil { - return "", "", "", fmt.Errorf("failed to read from Standard Input: %w", err) + return "", "", "", fmt.Errorf("unable to read input from standard input: %w", err) } } else { if !common.CheckFileFolderExists(inputDataPath) { From 77bc739b227ce4b04419d5755c36146a90a7f411 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 11:23:45 +0530 Subject: [PATCH 04/14] fix: fixing OutputFlagDescription of base64 tgz command: --- lib/base64Tgz/base64Tgz.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base64Tgz/base64Tgz.go b/lib/base64Tgz/base64Tgz.go index c1965fb..7102eee 100644 --- a/lib/base64Tgz/base64Tgz.go +++ b/lib/base64Tgz/base64Tgz.go @@ -35,7 +35,7 @@ for inclusion in Hyper Protect contracts. Supports both plain and encrypted outp InputFlagDescription = "Path to folder containing docker-compose.yaml or pods.yaml" OutputFlagName = "out" OutputFormatFlag = "output" - OutputFlagDescription = "Output format (plain or encrypted)" + OutputFlagDescription = "Output format (plain or encrypt)" OutputFormatUnencrypted = "plain" OutputFormatEncrypted = "encrypt" DefaultOutput = OutputFormatUnencrypted From 2bc1619a4c59ca11772050b92bf47c64e7ff80ee Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 15:55:13 +0530 Subject: [PATCH 05/14] feat: Added stdin support in base64-tgz --- lib/base64Tgz/base64Tgz.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/base64Tgz/base64Tgz.go b/lib/base64Tgz/base64Tgz.go index 7102eee..8bcd91b 100644 --- a/lib/base64Tgz/base64Tgz.go +++ b/lib/base64Tgz/base64Tgz.go @@ -17,6 +17,7 @@ package base64Tgz import ( "fmt" + "strings" "github.com/ibm-hyper-protect/contract-cli/common" "github.com/ibm-hyper-protect/contract-go/v2/contract" @@ -32,7 +33,7 @@ const ( Creates a compressed archive of your container configuration files, encoded as Base64 for inclusion in Hyper Protect contracts. Supports both plain and encrypted output.` InputFlagName = "in" - InputFlagDescription = "Path to folder containing docker-compose.yaml or pods.yaml" + InputFlagDescription = "Path to folder containing docker-compose.yaml or pods.yaml (use '-' for standard input)" OutputFlagName = "out" OutputFormatFlag = "output" OutputFlagDescription = "Output format (plain or encrypt)" @@ -58,6 +59,9 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, string, common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input + common.ValidateStdinInput(cmd, inputData) + outputFormat, err := cmd.Flags().GetString(OutputFormatFlag) if err != nil { return "", "", "", "", "", err @@ -83,12 +87,25 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, string, // Process - function to process base64-tgz inputs func Process(inputData, outputFormat, hyperProtectVersion, encCertPath string) (string, error) { + var folderPath string + + // Handle stdin input + if inputData == "-" { + path, err := common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("unable to read input from standard input: %w", err) + } + folderPath = strings.TrimSpace(path) + } else { + folderPath = inputData + } + if outputFormat == OutputFormatUnencrypted { - if !common.CheckFileFolderExists(inputData) { + if !common.CheckFileFolderExists(folderPath) { return "", fmt.Errorf("the path to docker-compose.yaml or pods.yaml is not accessible") } - base64Data, _, _, err := contract.HpcrTgz(inputData) + base64Data, _, _, err := contract.HpcrTgz(folderPath) if err != nil { return "", fmt.Errorf("failed to generate base64 tar - %v", err) } @@ -100,7 +117,7 @@ func Process(inputData, outputFormat, hyperProtectVersion, encCertPath string) ( return "", err } - encryptedBase64Data, _, _, err := contract.HpcrTgzEncrypted(inputData, hyperProtectVersion, encCert) + encryptedBase64Data, _, _, err := contract.HpcrTgzEncrypted(folderPath, hyperProtectVersion, encCert) if err != nil { return "", fmt.Errorf("failed to generate encrypted base64 tar - %v", err) } From f3ecbe885f1155b22c564d529c19a39bd77fa78a Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:15:42 +0530 Subject: [PATCH 06/14] feat: Added stdin support in decryptAttestation --- lib/decryptAttestation/decryptAttestation.go | 30 ++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/decryptAttestation/decryptAttestation.go b/lib/decryptAttestation/decryptAttestation.go index ee9157c..43afa6b 100644 --- a/lib/decryptAttestation/decryptAttestation.go +++ b/lib/decryptAttestation/decryptAttestation.go @@ -33,7 +33,7 @@ const ( Attestation records are typically found at /var/hyperprotect/se-checksums.txt.enc and contain cryptographic hashes for verifying workload integrity.` - DecryptAttestFileInDescription = "Path to encrypted attestation file (se-checksums.txt.enc)" + DecryptAttestFileInDescription = "Path to encrypted attestation file (se-checksums.txt.enc, use '-' for standard input)" DecryptAttestFlagDescription = "Path to save decrypted attestation records" successMessageDecryptAttestation = "Successfully decrypted attestation records" InputFlagName = "in" @@ -83,23 +83,37 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, error) { } } + // Validate stdin input + common.ValidateStdinInput(cmd, encAttestPath) + return encAttestPath, privateKeyPath, decryptedAttestPath, nil } // DecryptAttestationRecords - function to decrypt attestation records func DecryptAttestationRecords(encryptedAttestationRecordsPath, privateKeyPath string) (string, error) { - if !common.CheckFileFolderExists(encryptedAttestationRecordsPath) { - log.Fatal("The path to encrypted attestation records file doesn't exists") + var encryptedChecksum string + var err error + + // Handle stdin input + if encryptedAttestationRecordsPath == "-" { + encryptedChecksum, err = common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("unable to read input from standard input: %w", err) + } + } else { + if !common.CheckFileFolderExists(encryptedAttestationRecordsPath) { + log.Fatal("The path to encrypted attestation records file doesn't exists") + } + encryptedChecksum, err = common.ReadDataFromFile(encryptedAttestationRecordsPath) + if err != nil { + return "", err + } } + if !common.CheckFileFolderExists(privateKeyPath) { log.Fatal("The path to private key doesn't exists") } - encryptedChecksum, err := common.ReadDataFromFile(encryptedAttestationRecordsPath) - if err != nil { - return "", err - } - privateKey, err := common.ReadDataFromFile(privateKeyPath) if err != nil { return "", err From 9f965d9bbdc6eb0c8de1477f2d9c1406f51cda90 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:22:42 +0530 Subject: [PATCH 07/14] feat: Added stdin support in encrypt string --- lib/encryptString/encryptString.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/encryptString/encryptString.go b/lib/encryptString/encryptString.go index 8d5fdce..3a9b21e 100644 --- a/lib/encryptString/encryptString.go +++ b/lib/encryptString/encryptString.go @@ -30,7 +30,7 @@ const ( Output format: hyper-protect-basic.. Use this to encrypt sensitive data like passwords or API keys for contracts.` - InputFlagDescription = "String data to encrypt (text or JSON)" + InputFlagDescription = "String data to encrypt (text or JSON, use '-' for standard input)" FormatFlagDescription = "Input data format (text or json)" OutputFlagDescription = "Path to save encrypted output" successMessageEncryptString = "Successfully stored encrypted text" @@ -56,6 +56,9 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, string, common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input + common.ValidateStdinInput(cmd, inputData) + inputFormat, err := cmd.Flags().GetString(FormatFlag) if err != nil { return "", "", "", "", "", err @@ -81,6 +84,19 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, string, // Process - function to generate encrypted string of plain or JSON text func Process(inputData, inputFormat, hyperProtectVersion, encCertPath string) (string, error) { + var data string + var err error + + // Handle stdin input + if inputData == "-" { + data, err = common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("unable to read input from standard input: %w", err) + } + } else { + data = inputData + } + encCert, err := common.GetDataFromFile(encCertPath) if err != nil { return "", err @@ -88,12 +104,12 @@ func Process(inputData, inputFormat, hyperProtectVersion, encCertPath string) (s var encryptedString string if inputFormat == TextFormat { - encryptedString, _, _, err = contract.HpcrTextEncrypted(inputData, hyperProtectVersion, encCert) + encryptedString, _, _, err = contract.HpcrTextEncrypted(data, hyperProtectVersion, encCert) if err != nil { return "", err } } else if inputFormat == JsonFormat { - encryptedString, _, _, err = contract.HpcrJsonEncrypted(inputData, hyperProtectVersion, encCert) + encryptedString, _, _, err = contract.HpcrJsonEncrypted(data, hyperProtectVersion, encCert) if err != nil { return "", err } From e937e32c7cf5e1e3ad63c5a673f8e2dde5c75371 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:26:55 +0530 Subject: [PATCH 08/14] feat: Added stdin support in get certificate --- lib/getCertificate/getCertificate.go | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/getCertificate/getCertificate.go b/lib/getCertificate/getCertificate.go index 0d05c7d..0fe1148 100644 --- a/lib/getCertificate/getCertificate.go +++ b/lib/getCertificate/getCertificate.go @@ -31,7 +31,7 @@ const ( Parses the JSON output from download-certificate and extracts the certificate for the specified version.` - FileInFlagDescription = "Path to download-certificate JSON output" + FileInFlagDescription = "Path to download-certificate JSON output (use '-' for standard input)" VersionFlagDescription = "Certificate version to extract (e.g., 1.0.23)" FileOutFlagDescription = "Path to save extracted encryption certificate" InputFlagName = "in" @@ -80,18 +80,31 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, error) { } } + // Validate stdin input + common.ValidateStdinInput(cmd, encryptionCertsPath) + return encryptionCertsPath, version, encryptionCertificatePath, nil } // Process - function to get encryption certificate func Process(encryptionCertsPath, version string) (string, error) { - if !common.CheckFileFolderExists(encryptionCertsPath) { - return "", fmt.Errorf("the path to encryption certificates doesn't exist") - } + var encryptionCertsJson string + var err error - encryptionCertsJson, err := common.ReadDataFromFile(encryptionCertsPath) - if err != nil { - return "", err + // Handle stdin input + if encryptionCertsPath == "-" { + encryptionCertsJson, err = common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("unable to read input from standard input: %w", err) + } + } else { + if !common.CheckFileFolderExists(encryptionCertsPath) { + return "", fmt.Errorf("the path to encryption certificates doesn't exist") + } + encryptionCertsJson, err = common.ReadDataFromFile(encryptionCertsPath) + if err != nil { + return "", err + } } _, outputCertificate, _, _, _, err := certificate.HpcrGetEncryptionCertificateFromJson(encryptionCertsJson, version) From 55325aba4f0f13245b6bc26186f8e6a40a4eb835 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:29:52 +0530 Subject: [PATCH 09/14] feat: Added stdin support in image --- lib/image/image.go | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/image/image.go b/lib/image/image.go index 6a5949b..575349b 100644 --- a/lib/image/image.go +++ b/lib/image/image.go @@ -40,7 +40,7 @@ const ( Parses image information from IBM Cloud API, CLI, or Terraform output to extract image ID, name, checksum, and version. Supports filtering by specific HPCR version.` - IbmCloudJsonInputDescription = "Path to IBM Cloud images JSON (from API, CLI, or Terraform)" + IbmCloudJsonInputDescription = "Path to IBM Cloud images JSON (from API, CLI, or Terraform, use '-' for standard input)" HpcrVersionFlagDescription = "Specific HPCR version to retrieve (returns latest if not specified)" OutputFlagDescription = "Path to save HPCR image details" invalidImagePathMessage = "The Image details path doesn't exists" @@ -65,6 +65,9 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, error) { common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input + common.ValidateStdinInput(cmd, imageListJsonPath) + versionName, err := cmd.Flags().GetString(VersionFlagName) if err != nil { return "", "", "", "", err @@ -83,13 +86,23 @@ func ValidateInput(cmd *cobra.Command) (string, string, string, string, error) { // Process - function to get HPCR image details from JSON input func Process(imageDetailsJsonPath, versionName string) (ImageDetails, error) { - if !common.CheckFileFolderExists(imageDetailsJsonPath) { - log.Fatal(invalidImagePathMessage) - } + var imageDataJson string + var err error - imageDataJson, err := common.ReadDataFromFile(imageDetailsJsonPath) - if err != nil { - log.Fatal(err) + // Handle stdin input + if imageDetailsJsonPath == "-" { + imageDataJson, err = common.ReadDataFromStdin() + if err != nil { + return ImageDetails{}, fmt.Errorf("unable to read input from standard input: %w", err) + } + } else { + if !common.CheckFileFolderExists(imageDetailsJsonPath) { + log.Fatal(invalidImagePathMessage) + } + imageDataJson, err = common.ReadDataFromFile(imageDetailsJsonPath) + if err != nil { + log.Fatal(err) + } } imageId, imageName, imageChecksum, ImageVersion, err := image.HpcrSelectImage(imageDataJson, versionName) From af1cdfa4a965bfda47422a207392c4930d363189 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:32:39 +0530 Subject: [PATCH 10/14] feat: Added stdin support in initdata --- lib/initdata/initdata.go | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/initdata/initdata.go b/lib/initdata/initdata.go index 7109b40..ab2d856 100644 --- a/lib/initdata/initdata.go +++ b/lib/initdata/initdata.go @@ -29,7 +29,7 @@ const ( ParameterLongDescription = `Gzip and Encoded initdata annotation` InputFlagName = "in" - InputFlagDescription = "Path of Signed and Encrypted contract" + InputFlagDescription = "Path of Signed and Encrypted contract (use '-' for standard input)" OutputFlagName = "out" OutputFlagDescription = "Path to save Gzipped and encoded initdata value" @@ -47,6 +47,9 @@ func ValidateInput(cmd *cobra.Command) (string, string, error) { common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input + common.ValidateStdinInput(cmd, inputData) + outputPath, err := cmd.Flags().GetString(OutputFlagName) if err != nil { return "", "", err @@ -56,13 +59,25 @@ func ValidateInput(cmd *cobra.Command) (string, string, error) { // GenerateInitdata - function to generate gzipped initdata func GenerateInitdata(inputDataPath string) (string, error) { - if !common.CheckFileFolderExists(inputDataPath) { - return "", fmt.Errorf("the contract path doesn't exist") - } - inputData, err := common.ReadDataFromFile(inputDataPath) - if err != nil { - return "", err + var inputData string + var err error + + // Handle stdin input + if inputDataPath == "-" { + inputData, err = common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("unable to read input from standard input: %w", err) + } + } else { + if !common.CheckFileFolderExists(inputDataPath) { + return "", fmt.Errorf("the contract path doesn't exist") + } + inputData, err = common.ReadDataFromFile(inputDataPath) + if err != nil { + return "", err + } } + gzipInitdata, _, _, err := contract.HpccInitdata(inputData) if err != nil { return "", err From ff01be2b865537ceba91f5ecb1b547222048fd80 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:38:37 +0530 Subject: [PATCH 11/14] feat: Added stdin support in validate contract --- cmd/validateContract.go | 22 +++++++++++++++------- lib/validateContract/validateContract.go | 5 ++++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/cmd/validateContract.go b/cmd/validateContract.go index 789096d..31199b5 100644 --- a/cmd/validateContract.go +++ b/cmd/validateContract.go @@ -36,13 +36,21 @@ var validateContractCmd = &cobra.Command{ log.Fatal(err) } - contractData, err := common.ReadDataFromFile(contractPath) - if err != nil { - log.Fatal(err) - } - - if !common.CheckFileFolderExists(contractPath) { - log.Fatal("The path to contract doesn't exist") + var contractData string + // Handle stdin input + if contractPath == "-" { + contractData, err = common.ReadDataFromStdin() + if err != nil { + log.Fatalf("failed to read from standard input: %v", err) + } + } else { + if !common.CheckFileFolderExists(contractPath) { + log.Fatal("The path to contract doesn't exist") + } + contractData, err = common.ReadDataFromFile(contractPath) + if err != nil { + log.Fatal(err) + } } err = contract.HpcrVerifyContract(contractData, version) diff --git a/lib/validateContract/validateContract.go b/lib/validateContract/validateContract.go index 69eeab3..56e8dc4 100644 --- a/lib/validateContract/validateContract.go +++ b/lib/validateContract/validateContract.go @@ -29,7 +29,7 @@ const ( Checks contract structure, required fields, and data types before encryption. Helps catch errors early in the development process.` - InputFlagDescription = "Path to unencrypted Hyper Protect contract YAML file" + InputFlagDescription = "Path to unencrypted Hyper Protect contract YAML file (use '-' for standard input)" InputFlagName = "in" OsVersionFlagName = "os" OsVersionFlagDescription = "Target Hyper Protect platform (hpvs, hpcr-rhvs, or hpcc-peerpod)" @@ -46,6 +46,9 @@ func ValidateInput(cmd *cobra.Command) (string, string, error) { common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input + common.ValidateStdinInput(cmd, contract) + version, err := cmd.Flags().GetString(OsVersionFlagName) if err != nil { return "", "", err From a933a2b06ec1c24ce93041c1c64753fc78e680ce Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:44:28 +0530 Subject: [PATCH 12/14] feat: Added stdin support in validate encryption certificate --- cmd/validateEncryptionCertificate.go | 2 +- .../validateEncryptionCertificate.go | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/cmd/validateEncryptionCertificate.go b/cmd/validateEncryptionCertificate.go index fc7a7e2..b93c55a 100644 --- a/cmd/validateEncryptionCertificate.go +++ b/cmd/validateEncryptionCertificate.go @@ -55,7 +55,7 @@ func init() { requiredFlags := map[string]bool{ "in": true, } - validateEncryptionCertificateCmd.PersistentFlags().String(validateEncryptionCertificate.InputFlagName, "", validateEncryptionCertificate.CertVersionFlagDescription) + validateEncryptionCertificateCmd.PersistentFlags().String(validateEncryptionCertificate.InputFlagName, "", validateEncryptionCertificate.InputFlagDescription) common.SetCustomHelpTemplate(validateEncryptionCertificateCmd, requiredFlags) common.SetCustomErrorTemplate(validateEncryptionCertificateCmd) } diff --git a/lib/validateEncryptionCertificate/validateEncryptionCertificate.go b/lib/validateEncryptionCertificate/validateEncryptionCertificate.go index 9dc4b10..be0dfca 100644 --- a/lib/validateEncryptionCertificate/validateEncryptionCertificate.go +++ b/lib/validateEncryptionCertificate/validateEncryptionCertificate.go @@ -30,6 +30,7 @@ const ( Validates encryption certificate for on-premise, VPC deployment. It will check encryption certificate validity` + InputFlagDescription = "Path to encryption certificate file (use '-' for standard input)" CertVersionFlagDescription = "Versions of Encryption Certificates to validate, Seperated by coma(,)" InputFlagName = "in" ) @@ -45,18 +46,31 @@ func ValidateInput(cmd *cobra.Command) (string, error) { common.SetMandatoryFlagError(cmd, err) } + // Validate stdin input + common.ValidateStdinInput(cmd, encryptionCertsPath) + return encryptionCertsPath, nil } // GetEncryptionCertfile - function to get encryption certificate func GetEncryptionCertfile(encryptionCertsPath string) (string, error) { - if !common.CheckFileFolderExists(encryptionCertsPath) { - return "", fmt.Errorf("The specified path does not contain the encryption certificates.") - } + var encryptionCert string + var err error - encryptionCert, err := common.ReadDataFromFile(encryptionCertsPath) - if err != nil { - return "", err + // Handle stdin input + if encryptionCertsPath == "-" { + encryptionCert, err = common.ReadDataFromStdin() + if err != nil { + return "", fmt.Errorf("failed to read from standard input: %w", err) + } + } else { + if !common.CheckFileFolderExists(encryptionCertsPath) { + return "", fmt.Errorf("The specified path does not contain the encryption certificates.") + } + encryptionCert, err = common.ReadDataFromFile(encryptionCertsPath) + if err != nil { + return "", err + } } return encryptionCert, nil From 49a125e8f02c546eac11a324c6866b8264f901a7 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 17:48:02 +0530 Subject: [PATCH 13/14] feat: Added stdin support in validate network config --- cmd/validateContract.go | 2 +- cmd/validateNetwork.go | 20 +++++++++++++------ .../validateEncryptionCertificate.go | 2 +- lib/validateNetwork/validateNetwork.go | 4 +++- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cmd/validateContract.go b/cmd/validateContract.go index 31199b5..b7e6668 100644 --- a/cmd/validateContract.go +++ b/cmd/validateContract.go @@ -41,7 +41,7 @@ var validateContractCmd = &cobra.Command{ if contractPath == "-" { contractData, err = common.ReadDataFromStdin() if err != nil { - log.Fatalf("failed to read from standard input: %v", err) + log.Fatalf("unable to read input from standard input: %v", err) } } else { if !common.CheckFileFolderExists(contractPath) { diff --git a/cmd/validateNetwork.go b/cmd/validateNetwork.go index 01e452d..bc95f38 100644 --- a/cmd/validateNetwork.go +++ b/cmd/validateNetwork.go @@ -36,13 +36,21 @@ var validateNetworkConfigCmd = &cobra.Command{ log.Fatal(err) } - if !common.CheckFileFolderExists(networkConfigPath) { - log.Fatal("The path to network-config doesn't exist") - } + var networkConfigData string + if networkConfigPath == "-" { + networkConfigData, err = common.ReadDataFromStdin() + if err != nil { + log.Fatalf("unable to read input from standard input: %v", err) + } + } else { + if !common.CheckFileFolderExists(networkConfigPath) { + log.Fatal("The path to network-config doesn't exist") + } - networkConfigData, err := common.ReadDataFromFile(networkConfigPath) - if err != nil { - log.Fatal(err) + networkConfigData, err = common.ReadDataFromFile(networkConfigPath) + if err != nil { + log.Fatal(err) + } } err = network.HpcrVerifyNetworkConfig(networkConfigData) diff --git a/lib/validateEncryptionCertificate/validateEncryptionCertificate.go b/lib/validateEncryptionCertificate/validateEncryptionCertificate.go index be0dfca..596eeb1 100644 --- a/lib/validateEncryptionCertificate/validateEncryptionCertificate.go +++ b/lib/validateEncryptionCertificate/validateEncryptionCertificate.go @@ -61,7 +61,7 @@ func GetEncryptionCertfile(encryptionCertsPath string) (string, error) { if encryptionCertsPath == "-" { encryptionCert, err = common.ReadDataFromStdin() if err != nil { - return "", fmt.Errorf("failed to read from standard input: %w", err) + return "", fmt.Errorf("unable to read input from standard input: %w", err) } } else { if !common.CheckFileFolderExists(encryptionCertsPath) { diff --git a/lib/validateNetwork/validateNetwork.go b/lib/validateNetwork/validateNetwork.go index 6765567..104055b 100644 --- a/lib/validateNetwork/validateNetwork.go +++ b/lib/validateNetwork/validateNetwork.go @@ -29,7 +29,7 @@ const ( Validates network configuration for on-premise deployments, ensuring all required fields are present and properly formatted.` - InputFlagDescription = "Path to network-config YAML file" + InputFlagDescription = "Path to network-config YAML file (use '-' for standard input)" InputFlagName = "in" ) @@ -44,5 +44,7 @@ func ValidateInput(cmd *cobra.Command) (string, error) { common.SetMandatoryFlagError(cmd, err) } + common.ValidateStdinInput(cmd, networkConfig) + return networkConfig, nil } From 8dfa231622ac67a1be4b005efe124f46224fe4d0 Mon Sep 17 00:00:00 2001 From: Vikas Sharma Date: Wed, 18 Feb 2026 18:11:55 +0530 Subject: [PATCH 14/14] feat: updated README.md file --- docs/README.md | 86 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 1e0c97b..9be8715 100644 --- a/docs/README.md +++ b/docs/README.md @@ -126,7 +126,7 @@ contract-cli base64 [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Input data to encode (text or JSON) | +| `--in` | string | Yes | Input data to encode (text or JSON) (use '-' for standard input) | | `--format` | string | No | Input data format (text or json) | | `--out` | string | No | Path to save Base64 encoded output | | `-h, --help` | - | No | Display help information | @@ -148,6 +148,11 @@ contract-cli base64 --in '{"type": "workload"}' --format json contract-cli base64 --in "Hello World" --format text --out encoded.txt ``` +**Using standard input (pipe input):** +```bash +echo "Hello World" | contract-cli base64 --in - --format text +``` + --- ### base64-tgz @@ -164,7 +169,7 @@ contract-cli base64-tgz [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to folder containing `docker-compose.yaml` or `pods.yaml` | +| `--in` | string | Yes | Path to folder containing `docker-compose.yaml` or `pods.yaml` (use '-' for standard input) | | `--output` | string | No | Output type: `plain` or `encrypted` (default: `plain`) | | `--cert` | string | No | Path to encryption certificate (for encrypted output) | | `--os` | string | No | Target Hyper Protect platform: `hpvs`, `hpcr-rhvs`, or `hpcc-peerpod` (default: `hpvs`) | @@ -212,6 +217,11 @@ contract-cli base64-tgz \ contract-cli base64-tgz --in ./compose-folder --out archive.txt ``` +**Using standard input (pipe input):** +```bash +echo "pods-folder" | contract-cli base64-tgz --in - +``` + --- ### decrypt-attestation @@ -228,7 +238,7 @@ contract-cli decrypt-attestation [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to encrypted attestation file | +| `--in` | string | Yes | Path to encrypted attestation file (use '-' for standard input) | | `--priv` | string | Yes | Path to private key used for decryption | | `--out` | string | No | Path to save decrypted attestation records | | `-h, --help` | - | No | Display help information | @@ -250,6 +260,13 @@ contract-cli decrypt-attestation \ --out decrypted-attestation.txt ``` +**Using standard input:** +```bash +cat se-checksums.txt.enc | contract-cli decrypt-attestation \ + --in - \ + --priv private.pem +``` + --- ### download-certificate @@ -312,7 +329,7 @@ contract-cli encrypt [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to unencrypted Hyper Protect contract YAML file | +| `--in` | string | Yes | Path to unencrypted Hyper Protect contract YAML file (use '-' for standard input) | | `--priv` | string | No* | Path to private key for signing | | `--cert` | string | No | Path to encryption certificate (uses latest if not specified) | | `--os` | string | No | Target Hyper Protect platform: `hpvs`, `hpcr-rhvs`, or `hpcc-peerpod` (default: `hpvs`) | @@ -379,6 +396,13 @@ contract-cli encrypt \ --os hpcc-peerpod ``` +**Using standard input:** +```bash +echo "test-string" | contract-cli encrypt \ + --in - \ + --priv private.pem +``` + --- ### encrypt-string @@ -395,7 +419,7 @@ contract-cli encrypt-string [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | String data to encrypt | +| `--in` | string | Yes | String data to encrypt (use '-' for standard input) | | `--format` | string | No | Input data format (text or json) | | `--cert` | string | No | Path to encryption certificate (uses latest if not specified) | | `--os` | string | No | Target Hyper Protect platform: `hpvs`, `hpcr-rhvs`, or `hpcc-peerpod` (default: `hpvs`) | @@ -430,6 +454,11 @@ contract-cli encrypt-string \ --out encrypted-secret.txt ``` +**Using standard input:** +```bash +echo "my-secret-password" | contract-cli encrypt-string --in - +``` + --- ### get-certificate @@ -446,7 +475,7 @@ contract-cli get-certificate [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to download-certificate JSON output | +| `--in` | string | Yes | Path to download-certificate JSON output (use '-' for standard input) | | `--version` | string | Yes | Certificate version to extract (e.g., 1.0.23) | | `--out` | string | No | Path to save extracted encryption certificate | | `-h, --help` | - | No | Display help information | @@ -468,6 +497,11 @@ contract-cli get-certificate \ --out cert-1.0.23.crt ``` +**Using standard input:** +```bash +cat "cert.json" | contract-cli get-certificate --in - --version 1.0.23 +``` + --- ### image @@ -484,7 +518,7 @@ contract-cli image [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to IBM Cloud images JSON (from API, CLI, or Terraform) | +| `--in` | string | Yes | Path to IBM Cloud images JSON (from API, CLI, or Terraform) (use '-' for standard input) | | `--version` | string | No | Specific HPCR version to retrieve (returns latest if not specified) | | `--format` | string | No | Output format for data (json, yaml, or text) | | `--out` | string | No | Path to save HPCR image details | @@ -518,6 +552,12 @@ contract-cli image \ --out hpcr-image.json ``` +**Using standard input:** +```bash +cat "ibm-cloud-images.json" | contract-cli image --in - +``` + + --- ### validate-contract @@ -534,7 +574,7 @@ contract-cli validate-contract [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to unencrypted Hyper Protect contract YAML file | +| `--in` | string | Yes | Path to unencrypted Hyper Protect contract YAML file (use '-' for standard input) | | `--os` | string | No | Target Hyper Protect platform: `hpvs`, `hpcr-rhvs`, or `hpcc-peerpod` (default: `hpvs`) | | `-h, --help` | - | No | Display help information | @@ -555,6 +595,11 @@ contract-cli validate-contract --in contract.yaml --os hpcr-rhvs contract-cli validate-contract --in contract.yaml --os hpcc-peerpod ``` +**Using standard input:** +```bash +cat contract.yaml | contract-cli validate-contract --in - --os hpvs +``` + --- ### validate-network @@ -571,7 +616,7 @@ contract-cli validate-network [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to network-config YAML file | +| `--in` | string | Yes | Path to network-config YAML file (use '-' for standard input) | | `-h, --help` | - | No | Display help information | #### Examples @@ -581,6 +626,11 @@ contract-cli validate-network [flags] contract-cli validate-network --in network-config.yaml ``` +**Using standard input:** +```bash +cat network-config.yaml | contract-cli validate-network --in - +``` + --- @@ -598,16 +648,21 @@ contract-cli validate-encryption-certificate [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to encryption certificate file | +| `--in` | string | Yes | Path to encryption certificate file (use '-' for standard input) | | `-h, --help` | - | No | Display help information | #### Examples -**Validate encryption certifacte configuration:** +**Validate encryption certificate configuration:** ```bash contract-cli validate-encryption-certificate --in encryption-cert.crt ``` +**Using standard input:** +```bash +cat encryption-cert.crt | contract-cli validate-encryption-certificate --in - +``` + --- ### initdata @@ -623,17 +678,22 @@ contract-cli initdata [flags] | Flag | Type | Required | Description | |------|------|----------|-------------| -| `--in` | string | Yes | Path to signed & encrypted contract YAML file | +| `--in` | string | Yes | Path to signed & encrypted contract YAML file (use '-' for standard input) | | `--out` | string | No | Path to store gzipped & encoded initdata value | | `-h, --help` | - | No | Display help information | #### Examples -**Create Hpcc Initdata from signed & encrypted contract** +**Create Hpcc Initdata from signed & encrypted contract:** ```bash contract-cli initdata --in signed_encrypted_contract.yaml ``` +**Using standard input:** +```bash +cat signed_encrypted_contract.yaml | contract-cli initdata --in - +``` + --- ## Common Workflows