fix: add involves edge from task to agent:nomos at creation
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
ci / web (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled

Plus sync vendor directory for Docker build compatibility.
This commit is contained in:
2026-08-11 22:03:12 +02:00
parent 7d6a3320d4
commit febc153b7f
3384 changed files with 945212 additions and 2 deletions

View File

@@ -0,0 +1,244 @@
package util
import (
"context"
"encoding/json"
"fmt"
"net/url"
"os"
"time"
credentials "cloud.google.com/go/iam/credentials/apiv1"
"cloud.google.com/go/iam/credentials/apiv1/credentialspb"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/go-resty/resty/v2"
"google.golang.org/api/option"
)
func GetKubernetesServiceAccountToken(serviceAccountTokenPath string) (string, error) {
if serviceAccountTokenPath == "" {
serviceAccountTokenPath = DEFAULT_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH
}
token, err := os.ReadFile(serviceAccountTokenPath)
if err != nil {
return "", err
}
return string(token), nil
}
func buildAzureMetadataServiceURL(resource string, clientID string) string {
azureURL := AZURE_METADATA_SERVICE_URL + AZURE_DEFAULT_RESOURCE
if resource != "" {
azureURL = AZURE_METADATA_SERVICE_URL + url.QueryEscape(resource)
}
if clientID != "" {
azureURL += "&client_id=" + url.QueryEscape(clientID)
}
return azureURL
}
// GetAzureMetadataToken fetches a JWT from the Azure IMDS endpoint.
// The optional clientID parameter targets a specific User-Assigned Managed Identity;
// pass "" for System-Assigned Managed Identity.
func GetAzureMetadataToken(httpClient *resty.Client, customResource string, clientID string) (string, error) {
type AzureMetadataResponse struct {
AccessToken string `json:"access_token"`
}
metadataResponse := AzureMetadataResponse{}
response, err := httpClient.R().
SetResult(&metadataResponse).
SetHeader("Metadata", "true").
SetHeader("Accept", "application/json").
Get(buildAzureMetadataServiceURL(customResource, clientID))
if err != nil {
return "", err
}
if response.IsError() {
return "", fmt.Errorf("GetAzureMetadataToken: Unsuccessful response [%v %v] [status-code=%v] [Error: %s]", response.Request.Method, response.Request.URL, response.StatusCode(), TryParseErrorBody(response))
}
return metadataResponse.AccessToken, nil
}
func GetGCPMetadataToken(httpClient *resty.Client, identityID string) (string, error) {
res, err := httpClient.R().
SetHeader("Metadata-Flavor", "Google").
Get(fmt.Sprintf("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=%s&format=full", identityID))
if err != nil {
return "", err
}
if res.IsError() {
return "", fmt.Errorf("GetGCPMetadataToken: Unsuccessful response [%v %v] [status-code=%v] [Error: %s]", res.Request.Method, res.Request.URL, res.StatusCode(), TryParseErrorBody(res))
}
return res.String(), nil
}
func GetAwsEC2IdentityDocumentRegion(timeout int) (string, error) {
type AwsIdentityDocument struct {
Region string `json:"region"`
}
httpClient := resty.New().SetTimeout(time.Duration(timeout) * time.Millisecond)
res, err := httpClient.R().
SetHeader("X-aws-ec2-metadata-token-ttl-seconds", "21600").
Put(AWS_EC2_METADATA_TOKEN_URL)
if err != nil {
return "", err
}
if res.IsError() {
return "", fmt.Errorf("GetAwsEC2IdentityDocumentRegion: Unsuccessful response [%v %v] [status-code=%v] [Error: %s]", res.Request.Method, res.Request.URL, res.StatusCode(), TryParseErrorBody(res))
}
metadataToken := res.String()
res, err = httpClient.R().
SetHeader("X-aws-ec2-metadata-token", metadataToken).
SetHeader("Accept", "application/json").
Get(AWS_EC2_INSTANCE_IDENTITY_DOCUMENT_URL)
if err != nil {
return "", err
}
if res.IsError() {
return "", fmt.Errorf("GetAwsEC2IdentityDocumentRegion: Unsuccessful response [%v %v] [status-code=%v] [Error: %s]", res.Request.Method, res.Request.URL, res.StatusCode(), TryParseErrorBody(res))
}
// For some reason using .SetResult(&AwsIdentityDocument{}) doesn't work and just results in an empty object. This works though..
var identityDocument AwsIdentityDocument
err = json.Unmarshal(res.Body(), &identityDocument)
if err != nil {
return "", err
}
return identityDocument.Region, nil
}
func GetGCPIamServiceAccountToken(identityID string, serviceAccountKeyPath string) (string, error) {
type JwtPayload struct {
Sub string `json:"sub"`
Aud string `json:"aud"`
}
ctx := context.Background()
serviceAccountKey, err := os.ReadFile(serviceAccountKeyPath)
if err != nil {
return "", err
}
var creds map[string]string
if err := json.Unmarshal(serviceAccountKey, &creds); err != nil {
return "", fmt.Errorf("failed to unmarshal service account key: %v", err)
}
clientEmail := creds["client_email"]
if clientEmail == "" {
return "", fmt.Errorf("client email not found in service account key")
}
payload := JwtPayload{
Sub: clientEmail,
Aud: identityID,
}
payloadJSON, err := json.Marshal(payload)
if err != nil {
return "", fmt.Errorf("failed to marshal JWT payload: %v", err)
}
iamCredentialsClient, err := credentials.NewIamCredentialsClient(ctx, option.WithCredentialsFile(serviceAccountKeyPath)) //nolint:staticcheck // deprecated but no drop-in replacement available yet
if err != nil {
return "", fmt.Errorf("failed to create IAM credentials client: %v", err)
}
defer iamCredentialsClient.Close() //nolint:errcheck
signJwtRequest := &credentialspb.SignJwtRequest{
Name: fmt.Sprintf("projects/-/serviceAccounts/%s", clientEmail),
Payload: string(payloadJSON),
}
resp, err := iamCredentialsClient.SignJwt(ctx, signJwtRequest)
if err != nil {
return "", fmt.Errorf("failed to sign JWT: %v. Ensure the IAM Service Account Credentials API is enabled", err)
}
signedJwt := resp.SignedJwt
if signedJwt == "" {
return "", fmt.Errorf("failed to sign JWT: signedJwt is empty")
}
return signedJwt, nil
}
func GetAwsRegion() (string, error) {
// in Lambda environments, the region is available in the AWS_REGION environment variable
region := os.Getenv("AWS_REGION")
if region != "" {
return region, nil
}
// in EC2 environments, the region is available in the identity doc
region, err := GetAwsEC2IdentityDocumentRegion(5000)
if err != nil {
return "", err
}
return region, nil
}
func RetrieveAwsCredentials() (credentials aws.Credentials, region string, err error) {
presetAwsCfg, err := config.LoadDefaultConfig(context.TODO())
if err == nil && presetAwsCfg.Region != "" {
creds, err := presetAwsCfg.Credentials.Retrieve(context.TODO())
if err == nil {
return creds, presetAwsCfg.Region, nil
}
}
awsRegion, err := GetAwsRegion()
if err != nil {
return aws.Credentials{}, "", err
}
awsCfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(awsRegion))
if err != nil {
return aws.Credentials{}, "", fmt.Errorf("unable to load SDK config, %v", err)
}
creds, err := awsCfg.Credentials.Retrieve(context.TODO())
if err != nil {
return aws.Credentials{}, "", fmt.Errorf("error retrieving credentials: %v", err)
}
return creds, awsRegion, nil
}

View File

@@ -0,0 +1,92 @@
package util
import (
"context"
"errors"
)
// Auth related:
const (
INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME = "INFISICAL_AUTH_ORGANIZATION_SLUG"
// Universal auth:
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_ENV_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID"
INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_ENV_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET"
// GCP auth:
INFISICAL_GCP_AUTH_IDENTITY_ID_ENV_NAME = "INFISICAL_GCP_AUTH_IDENTITY_ID"
INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_ENV_NAME = "INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH"
// AWS auth:
INFISICAL_AWS_IAM_AUTH_IDENTITY_ID_ENV_NAME = "INFISICAL_AWS_IAM_AUTH_IDENTITY_ID"
// Azure auth:
INFISICAL_AZURE_AUTH_IDENTITY_ID_ENV_NAME = "INFISICAL_AZURE_AUTH_IDENTITY_ID"
INFISICAL_AZURE_AUTH_CLIENT_ID_ENV_NAME = "INFISICAL_AZURE_AUTH_CLIENT_ID"
// OCI auth:
INFISICAL_OCI_AUTH_IDENTITY_ID_ENV_NAME = "INFISICAL_OCI_AUTH_IDENTITY_ID"
// LDAP auth:
INFISICAL_LDAP_AUTH_IDENTITY_ID_ENV_NAME = "INFISICAL_LDAP_AUTH_IDENTITY_ID"
// Kubernetes auth:
INFISICAL_KUBERNETES_IDENTITY_ID_ENV_NAME = "INFISICAL_KUBERNETES_IDENTITY_ID"
INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH_ENV_NAME = "INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH"
// OIDC auth:
INFISICAL_OIDC_AUTH_IDENTITY_ID_ENV_NAME = "INFISICAL_OIDC_AUTH_IDENTITY_ID"
// Access token:
INFISICAL_ACCESS_TOKEN_ENV_NAME = "INFISICAL_ACCESS_TOKEN"
// AWS metadata service:
AWS_EC2_METADATA_TOKEN_URL = "http://169.254.169.254/latest/api/token"
AWS_EC2_INSTANCE_IDENTITY_DOCUMENT_URL = "http://169.254.169.254/latest/dynamic/instance-identity/document"
// Azure metadata service:
AZURE_METADATA_SERVICE_URL = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" // End of the URL needs to be appended with the resource
AZURE_DEFAULT_RESOURCE = "https%3A%2F%2Fmanagement.azure.com/"
)
type AuthMethod string
const (
ACCESS_TOKEN AuthMethod = "ACCESS_TOKEN"
UNIVERSAL_AUTH AuthMethod = "UNIVERSAL_AUTH"
GCP_ID_TOKEN AuthMethod = "GCP_ID_TOKEN"
GCP_IAM AuthMethod = "GCP_IAM"
AWS_IAM AuthMethod = "AWS_IAM"
KUBERNETES AuthMethod = "KUBERNETES"
AZURE AuthMethod = "AZURE"
OIDC_AUTH AuthMethod = "OIDC_AUTH"
JWT_AUTH AuthMethod = "JWT_AUTH"
LDAP_AUTH AuthMethod = "LDAP_AUTH"
OCI_AUTH AuthMethod = "OCI_AUTH"
)
// SSH related:
type CertKeyAlgorithm string
const (
RSA2048 CertKeyAlgorithm = "RSA_2048"
RSA4096 CertKeyAlgorithm = "RSA_4096"
ECDSAP256 CertKeyAlgorithm = "EC_prime256v1"
ECDSAP384 CertKeyAlgorithm = "EC_secp384r1"
)
type SshCertType string
const (
UserCert SshCertType = "user"
HostCert SshCertType = "host"
)
// General:
const (
DEFAULT_INFISICAL_API_URL = "https://app.infisical.com/api"
DEFAULT_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"
)
var ErrContextCanceled = errors.New("context canceled")
var ErrContextDeadlineExceeded error = context.DeadlineExceeded

View File

@@ -0,0 +1,146 @@
package util
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"sort"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/infisical/go-sdk/packages/models"
"github.com/rs/zerolog"
)
func AppendAPIEndpoint(siteUrl string) string {
// Ensure the address does not already end with "/api"
if strings.HasSuffix(siteUrl, "/api") {
return siteUrl
}
// Check if the address ends with a slash and append accordingly
if siteUrl[len(siteUrl)-1] == '/' {
return siteUrl + "api"
}
return siteUrl + "/api"
}
func PrintWarning(logger zerolog.Logger, message string) {
logger.Warn().Msgf("[Infisical] Warning: %v", message)
}
func EnsureUniqueSecretsByKey(secrets *[]models.Secret, skipUniqueKey bool) {
secretMap := make(map[string]models.Secret)
// Move secrets to a map to ensure uniqueness
for _, secret := range *secrets {
var key string
if skipUniqueKey {
// Create a composite key using both SecretPath and SecretKey
key = secret.SecretPath + ":" + secret.SecretKey
} else {
// Use only SecretKey for global uniqueness
key = secret.SecretKey
}
secretMap[key] = secret
}
// Clear the slice
*secrets = (*secrets)[:0]
// Refill the slice from the map
for _, secret := range secretMap {
*secrets = append(*secrets, secret)
}
}
// containsSecret checks if the given key exists in the slice of secrets
func ContainsSecret(secrets []models.Secret, key string) bool {
for _, secret := range secrets {
if secret.SecretKey == key {
return true
}
}
return false
}
// Helper function to sort the secrets by key so we can create a consistent output
func SortSecretsByKeys(secrets []models.Secret) []models.Secret {
sort.Slice(secrets, func(i, j int) bool {
return secrets[i].SecretKey < secrets[j].SecretKey
})
return secrets
}
/*
If the status code is 400, there will most likely always be a body.
The body is a json object with a message key. we need to try to parse it, but if it fails, we can just return an empty string.
But if the status code is 500, there may not be a body. if there is, it will be a json object with a message key. we need to try to parse it, but if it fails, we can just return an empty string
*/
func TryParseErrorBody(res *resty.Response) string {
if res == nil || !res.IsError() {
return ""
}
body := res.String()
if body == "" {
return ""
}
type ErrorResponse struct {
Message string `json:"message"`
ReqId string `json:"reqId"`
}
// stringify zod body entirely
if res.StatusCode() == 422 {
return body
}
// now we have a string, we need to try to parse it as json
var errorResponse ErrorResponse
err := json.Unmarshal([]byte(body), &errorResponse)
if err != nil {
return ""
}
return errorResponse.Message
}
func TryExtractReqId(res *resty.Response) string {
if res == nil || !res.IsError() {
return ""
}
type ErrorResponse struct {
ReqId string `json:"reqId"`
}
var errorResponse ErrorResponse
err := json.Unmarshal([]byte(res.String()), &errorResponse)
if err != nil {
return ""
}
return errorResponse.ReqId
}
func SleepWithContext(ctx context.Context, duration time.Duration) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(duration):
return nil
}
}
func ComputeCacheKeyFromBytes(bytes []byte, feature string) string {
key := sha256.Sum256(bytes)
return fmt.Sprintf("%s-%s", feature, hex.EncodeToString(key[:]))
}