@@ -274,7 +274,7 @@ func (k *kclRunner) writeOutput(
274274
275275func pathLooksLikeFile (path string ) bool {
276276 ext := filepath .Ext (path )
277- return ext == ".yaml" || ext == ".yml"
277+ return ext == ".yaml" || ext == ".yml" //nolint:goconst
278278}
279279
280280func writeManifestDirectory (
@@ -314,7 +314,13 @@ func writeManifestDirectory(
314314 fallbackIndex ++
315315 }
316316
317- if err = os .WriteFile (filepath .Join (outPath , fileName ), resource , 0o600 ); err != nil {
317+ outputPath , err := safeOutputResourcePath (outPath , fileName )
318+ if err != nil {
319+ return err
320+ }
321+
322+ //nolint:gosec // outputPath is constrained to outPath by basename validation and securejoin
323+ if err = os .WriteFile (outputPath , resource , 0o600 ); err != nil {
318324 return fmt .Errorf ("failed to write resource to file %q: %w" , fileName , err )
319325 }
320326 }
@@ -323,6 +329,26 @@ func writeManifestDirectory(
323329 return nil
324330}
325331
332+ func safeOutputResourcePath (outPath string , fileName string ) (string , error ) {
333+ cleanName := filepath .Clean (fileName )
334+ if cleanName == "." || cleanName == ".." {
335+ return "" , fmt .Errorf ("invalid resource file name %q" , fileName )
336+ }
337+ if filepath .IsAbs (cleanName ) {
338+ return "" , fmt .Errorf ("resource file name %q must be relative" , fileName )
339+ }
340+ if cleanName != filepath .Base (cleanName ) {
341+ return "" , fmt .Errorf ("resource file name %q must not contain path separators" , fileName )
342+ }
343+
344+ joinedPath , err := securejoin .SecureJoin (outPath , cleanName )
345+ if err != nil {
346+ return "" , fmt .Errorf ("failed to build output path for %q: %w" , fileName , err )
347+ }
348+
349+ return joinedPath , nil
350+ }
351+
326352func splitManifestResources (document []byte ) ([][]byte , error ) {
327353 var value any
328354 if err := libyaml .Unmarshal (document , & value ); err != nil {
0 commit comments