in commands/credential_scan.go [64:352]
func (c CredentialScanCommand) Execute() int {
wd, err := os.Getwd()
if err != nil {
logrus.Errorf("failed to get working directory: %+v", err)
return 1
}
if c.workingDir != "" {
wd, err = filepath.Abs(c.workingDir)
if err != nil {
logrus.Errorf("working directory is invalid: %+v", err)
return 1
}
}
if c.swaggerRepoPath != "" {
c.swaggerRepoPath, err = filepath.Abs(c.swaggerRepoPath)
if err != nil {
logrus.Errorf("swagger repo path %q is invalid: %+v", c.swaggerRepoPath, err)
return 1
}
if _, err := os.Stat(c.swaggerRepoPath); os.IsNotExist(err) {
logrus.Errorf("swagger repo path %q is invalid: path does not exist", c.swaggerRepoPath)
return 1
}
c.swaggerRepoPath = strings.TrimSuffix(c.swaggerRepoPath, "/")
if !strings.HasSuffix(c.swaggerRepoPath, "specification") {
logrus.Errorf("swagger repo path %q is invalid: must point to \"specification\", e.g., /home/projects/azure-rest-api-specs/specification", c.swaggerRepoPath)
return 1
}
c.swaggerRepoPath += "/"
}
if c.swaggerIndexFile != "" {
c.swaggerIndexFile, err = filepath.Abs(c.swaggerIndexFile)
if err != nil {
logrus.Errorf("swagger index file path %q is invalid: %+v", c.swaggerIndexFile, err)
return 1
}
if _, err := os.Stat(c.swaggerIndexFile); os.IsNotExist(err) {
logrus.Infof("swagger index file %q does not exist, will try to build or download index", c.swaggerIndexFile)
}
}
outputDir := wd
if c.outputDir != "" {
outputDir, err = filepath.Abs(c.outputDir)
if err != nil {
logrus.Errorf("output directory is invalid: %+v", err)
return 1
}
}
tfFiles, err := hcl.FindTfFiles(wd)
if err != nil {
logrus.Errorf("failed to find tf files for %q: %+v", wd, err)
return 1
}
if len(*tfFiles) == 0 {
logrus.Warnf("no tf file found in %q", wd)
}
logrus.Infof("find %v tf file(s) under %s", len(*tfFiles), wd)
azapiResources := make([]hcl.AzapiResource, 0)
vars := make(map[string]hcl.Variable, 0)
azureProviders := make([]hcl.AzureProvider, 0)
for _, tfFile := range *tfFiles {
f, err := hcl.ParseHclFile(tfFile)
if err != nil {
logrus.Errorf("failed to parse hcl file %q: %+v", tfFile, err)
return 1
}
azapiResourceInFile, err := hcl.ParseAzapiResource(*f)
if err != nil {
logrus.Errorf("failed to parse azapi resource for %q: %+v", tfFile, err)
return 1
}
azapiResources = append(azapiResources, *azapiResourceInFile...)
varsInFile, err := hcl.ParseVariables(*f)
if err != nil {
logrus.Errorf("failed to parse variables for %q: %+v", tfFile, err)
return 1
}
for k, v := range *varsInFile {
vars[k] = v
}
azureProvidersInFile, err := hcl.ParseAzureProvider(*f)
if err != nil {
logrus.Errorf("failed to parse azure provider for %q: %+v", tfFile, err)
return 1
}
azureProviders = append(azureProviders, *azureProvidersInFile...)
}
credScanErrors := make([]CredScanError, 0)
for _, azureProvider := range azureProviders {
if v := azureProvider.SubscriptionId; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "subscription_id", v, vars)...)
}
if v := azureProvider.TenantId; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "tenant_id", v, vars)...)
}
if v := azureProvider.AuxiliaryTenantIds; len(v) > 0 {
for i, tenant_id := range v {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, fmt.Sprintf("auxiliary_tenant_ids[%v]", i), tenant_id, vars)...)
}
}
if v := azureProvider.AuxiliaryTenantIdsString; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "auxiliary_tenant_ids", v, vars)...)
}
if v := azureProvider.ClientId; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "client_id", v, vars)...)
}
if v := azureProvider.ClientCertificate; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "client_certificate", v, vars)...)
}
if v := azureProvider.ClientCertificatePassword; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "client_certificate_password", v, vars)...)
}
if v := azureProvider.ClientSecret; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "client_secret", v, vars)...)
}
if v := azureProvider.OidcRequestToken; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "oidc_request_token", v, vars)...)
}
if v := azureProvider.OidcToken; v != "" {
credScanErrors = append(credScanErrors, checkAzureProviderSecret(azureProvider, "oidc_token", v, vars)...)
}
}
for _, azapiResource := range azapiResources {
logrus.Infof("scaning azapi_resource.%s(%s)", azapiResource.Name, azapiResource.Type)
if azapiResource.Body == "" {
continue
}
var body interface{}
err = json.Unmarshal([]byte(azapiResource.Body), &body)
if err != nil {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("failed to unmarshal body: %+v", err),
"",
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
mockedResourceId, apiVersion := coverage.MockResourceIDFromType(azapiResource.Type)
logrus.Infof("azapi_resource.%s(%s): mocked possible resource ID: %s, API version: %s", azapiResource.Name, azapiResource.Type, mockedResourceId, apiVersion)
var swaggerModel *coverage.SwaggerModel
if c.swaggerRepoPath != "" {
logrus.Infof("scan based on local swagger repo: %s", c.swaggerRepoPath)
swaggerModel, err = coverage.GetModelInfoFromLocalIndex(mockedResourceId, apiVersion, "PUT", c.swaggerRepoPath, c.swaggerIndexFile)
if err != nil {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("fail to find swagger model from local swagger with possible resource ID(%s) API version(%s): %+v", mockedResourceId, apiVersion, err),
"",
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
} else {
swaggerModel, err = coverage.GetModelInfoFromIndex(mockedResourceId, apiVersion, "PUT", c.swaggerIndexFile)
if err != nil {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("fail to find swagger model with possible resource ID(%s) API version(%s): %+v", mockedResourceId, apiVersion, err),
"",
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
}
if swaggerModel == nil {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("unable to find swagger model with possible resource ID(%s) API version(%s)", mockedResourceId, apiVersion),
"",
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
logrus.Infof("find swagger model for azapi_resource.%s(%s): %+v", azapiResource.Name, azapiResource.Type, *swaggerModel)
model, err := coverage.Expand(swaggerModel.ModelName, swaggerModel.SwaggerPath)
if err != nil {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("failed to expand model: %+v", err),
"",
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
secrets := make(map[string]string)
model.CredScan(body, secrets)
logrus.Infof("find secrets for azapi_resource.%s(%s): %+v", azapiResource.Name, azapiResource.Type, secrets)
for k, v := range secrets {
if !strings.HasPrefix(v, "$") || strings.HasPrefix(v, "$local.") {
credScanErr := makeCredScanError(
azapiResource,
"cannot use plain text or 'local' for secret, please follow https://github.com/Azure/armstrong/blob/main/docs/guidance-for-api-test.md#4-q-i-have-some-sensitive-information-in-the-test-case-how-to-hide-it to hide the secret values",
k,
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
if strings.HasPrefix(v, "$var.") {
varName := strings.TrimPrefix(v, "$var.")
varName = strings.Split(varName, ".")[0]
theVar, ok := vars[varName]
if !ok {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("variable %q was not found, please follow https://github.com/Azure/armstrong/blob/main/docs/guidance-for-api-test.md#4-q-i-have-some-sensitive-information-in-the-test-case-how-to-hide-it to set the variable for secret values", varName),
k,
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
continue
}
if theVar.HasDefault {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("variable %q (%v:%v) used in secret field but has a default value, please follow https://github.com/Azure/armstrong/blob/main/docs/guidance-for-api-test.md#4-q-i-have-some-sensitive-information-in-the-test-case-how-to-hide-it to set the variable for secret values", varName, theVar.FileName, theVar.LineNumber),
k,
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
}
if !theVar.IsSensitive {
credScanErr := makeCredScanError(
azapiResource,
fmt.Sprintf("variable %q (%v:%v) used in secret field but is not marked as sensitive, please follow https://github.com/Azure/armstrong/blob/main/docs/guidance-for-api-test.md#4-q-i-have-some-sensitive-information-in-the-test-case-how-to-hide-it to set the variable for secret values", varName, theVar.FileName, theVar.LineNumber),
k,
)
credScanErrors = append(credScanErrors, credScanErr)
logrus.Error(credScanErr)
}
}
}
}
storeCredScanErrors(outputDir, credScanErrors)
return 0
}