{"record":{"id":"cf6f5e90a7d8a619","repo":"crowdsecurity/crowdsec","slug":"unable-to-copy-appsec-rule-from-s-to-s-w","errorCode":null,"errorMessage":"unable to copy appsec-rule from '%s' to '%s': %w","messagePattern":"unable to copy appsec-rule from '(.+?)' to '(.+?)': %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/hubtest/appsecrule.go","lineNumber":66,"sourceCode":"\nfunc (t *HubTestItem) installAppsecRuleCustomFrom(appsecrule string, customPath string) (bool, error) {\n\t// we check if its a custom appsec-rule\n\tcustomAppsecRulePath := filepath.Join(customPath, appsecrule)\n\tif _, err := os.Stat(customAppsecRulePath); os.IsNotExist(err) {\n\t\treturn false, nil\n\t}\n\n\tcustomAppsecRulePathSplit := strings.Split(customAppsecRulePath, \"/\")\n\tcustomAppsecRuleName := customAppsecRulePathSplit[len(customAppsecRulePathSplit)-1]\n\n\titemTypeDirDest := fmt.Sprintf(\"%s/appsec-rules/\", t.RuntimePath)\n\tif err := os.MkdirAll(itemTypeDirDest, os.ModePerm); err != nil {\n\t\treturn false, fmt.Errorf(\"unable to create folder '%s': %w\", itemTypeDirDest, err)\n\t}\n\n\tcustomAppsecRuleDest := fmt.Sprintf(\"%s/appsec-rules/%s\", t.RuntimePath, customAppsecRuleName)\n\tif err := Copy(customAppsecRulePath, customAppsecRuleDest); err != nil {\n\t\treturn false, fmt.Errorf(\"unable to copy appsec-rule from '%s' to '%s': %w\", customAppsecRulePath, customAppsecRuleDest, err)\n\t}\n\n\treturn true, nil\n}\n\nfunc (t *HubTestItem) installAppsecRuleCustom(appsecrule string) error {\n\tfor _, customPath := range t.CustomItemsLocation {\n\t\tfound, err := t.installAppsecRuleCustomFrom(appsecrule, customPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif found {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"couldn't find custom appsec-rule '%s' in the following location: %+v\", appsecrule, t.CustomItemsLocation)","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/hubtest/appsecrule.go#L48-L84","documentation":"installAppsecRuleCustomFrom copies the located custom appsec-rule file into <RuntimePath>/appsec-rules/ and wraps Copy failures. The custom rule was found (os.Stat passed) but reading the source or writing the destination failed.","triggerScenarios":"installAppsecRuleCustom → installAppsecRuleCustomFrom when the Copy from customPath/appsecrule to the runtime destination fails: source unreadable, destination unwritable, source is a directory, or disk full.","commonSituations":"Custom rule file exists but with restrictive permissions; destination directory removed between MkdirAll and Copy by a concurrent process; the 'custom' entry is actually a directory; out-of-space CI runner.","solutions":["Verify the custom rule file is readable (permissions) and is a regular file, not a directory","Re-check write permissions on <RuntimePath>/appsec-rules/ after the mkdir step","Check disk space and re-run"],"exampleFix":"# before\n-rw------- 1 root root 512 my-rule.yaml   # unreadable by test user\n# after\nchmod 644 my-rule.yaml && re-run","handlingStrategy":"validation","validationCode":"src := filepath.Join(customPath, appsecrule)\nfi, err := os.Stat(src)\nif err != nil || fi.IsDir() || fi.Mode().Perm()&0o400 == 0 {\n\treturn fmt.Errorf(\"custom appsec-rule %s missing, unreadable or a directory\", src)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check custom rule files are regular files with read permission for the test user","Verify runtime directory is still writable right before the copy step","Avoid concurrent test runs sharing the same RuntimePath"],"tags":["filesystem","file-copy","permissions"],"backgroundTag":"file-copy-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}