diff --git a/cmd/run.go b/cmd/run.go
index 7599a4c2..1985be1e 100644
--- a/cmd/run.go
+++ b/cmd/run.go
@@ -13,6 +13,7 @@ var (
 	runDir         string
 	materialsPaths []string
 	productsPaths  []string
+	noCommand      bool
 )
 
 var runCmd = &cobra.Command{
@@ -23,7 +24,7 @@ files before command execution) and 'products' (i.e. files after command
 execution) and stores them together with other information (executed command,
 return value, stdout, stderr, ...) to a link metadata file, which is signed
 with the passed key.  Returns nonzero value on failure and zero otherwise.`,
-	Args:    cobra.MinimumNArgs(1),
+	Args:    cobra.MinimumNArgs(0),
 	PreRunE: getKeyCert,
 	RunE:    run,
 }
@@ -131,6 +132,14 @@ operating systems. It is done by replacing all line separators
 with a new line character.`,
 	)
 
+	runCmd.Flags().BoolVarP(
+		&noCommand,
+		"no-command",
+		"x",
+		false,
+		`Indicate that there is no command to be executed for the step.`,
+	)
+
 	runCmd.Flags().StringVar(
 		&spiffeUDS,
 		"spiffe-workload-api-path",
@@ -141,6 +150,14 @@ with a new line character.`,
 }
 
 func run(cmd *cobra.Command, args []string) error {
+	if noCommand && len(args) > 0 {
+		return fmt.Errorf("command arguments passed with --no-command/-x flag")
+	}
+
+	if !noCommand && len(args) == 0 {
+		return fmt.Errorf("no command arguments passed, please specify or use --no-command option")
+	}
+
 	block, err := intoto.InTotoRun(stepName, runDir, materialsPaths, productsPaths, args, key, []string{"sha256"}, exclude, lStripPaths, lineNormalization)
 	if err != nil {
 		return fmt.Errorf("failed to create link metadata: %w", err)
diff --git a/doc/in-toto_run.md b/doc/in-toto_run.md
index f54f3765..a5f5f656 100644
--- a/doc/in-toto_run.md
+++ b/doc/in-toto_run.md
@@ -37,6 +37,7 @@ in-toto run [flags]
   -d, --metadata-directory string         Directory to store link metadata (default "./")
   -n, --name string                       Name used to associate the resulting link metadata
                                           with the corresponding step defined in an in-toto layout.
+  -x, --no-command                        Indicate that there is no command to be executed for the step.
       --normalize-line-endings            Enable line normalization in order to support different
                                           operating systems. It is done by replacing all line separators
                                           with a new line character.
diff --git a/in_toto/runlib.go b/in_toto/runlib.go
index 80eef3d7..87e69050 100644
--- a/in_toto/runlib.go
+++ b/in_toto/runlib.go
@@ -21,6 +21,8 @@ var ErrSymCycle = errors.New("symlink cycle detected")
 // ErrUnsupportedHashAlgorithm signals a missing hash mapping in getHashMapping
 var ErrUnsupportedHashAlgorithm = errors.New("unsupported hash algorithm detected")
 
+var ErrEmptyCommandArgs = errors.New("the command args are empty")
+
 // visitedSymlinks is a hashset that contains all paths that we have visited.
 var visitedSymlinks Set
 
@@ -247,6 +249,9 @@ NOTE: Since stdout and stderr are captured, they cannot be seen during the
 command execution.
 */
 func RunCommand(cmdArgs []string, runDir string) (map[string]interface{}, error) {
+	if len(cmdArgs) == 0 {
+		return nil, ErrEmptyCommandArgs
+	}
 
 	cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
 
@@ -298,9 +303,13 @@ func InTotoRun(name string, runDir string, materialPaths []string, productPaths
 		return linkMb, err
 	}
 
-	byProducts, err := RunCommand(cmdArgs, runDir)
-	if err != nil {
-		return linkMb, err
+	// make sure that we only run RunCommand if cmdArgs is not nil or empty
+	byProducts := map[string]interface{}{}
+	if len(cmdArgs) != 0 {
+		byProducts, err = RunCommand(cmdArgs, runDir)
+		if err != nil {
+			return linkMb, err
+		}
 	}
 
 	products, err := RecordArtifacts(productPaths, hashAlgorithms, gitignorePatterns, lStripPaths, lineNormalization)
diff --git a/in_toto/runlib_test.go b/in_toto/runlib_test.go
index 7147762b..377d3115 100644
--- a/in_toto/runlib_test.go
+++ b/in_toto/runlib_test.go
@@ -377,6 +377,23 @@ func TestRunCommand(t *testing.T) {
 	}
 }
 
+func TestRunCommandErrors(t *testing.T) {
+	tables := []struct {
+		CmdArgs       []string
+		RunDir        string
+		ExpectedError error
+	}{
+		{nil, "", ErrEmptyCommandArgs},
+		{[]string{}, "", ErrEmptyCommandArgs},
+	}
+	for _, table := range tables {
+		_, err := RunCommand(table.CmdArgs, table.RunDir)
+		if !errors.Is(err, ErrEmptyCommandArgs) {
+			t.Errorf("RunCommand did not provoke expected error. Got: %s, want: %s", err, ErrEmptyCommandArgs)
+		}
+	}
+}
+
 func TestInTotoRun(t *testing.T) {
 	// Successfully run InTotoRun
 	linkName := "Name"
@@ -420,6 +437,30 @@ func TestInTotoRun(t *testing.T) {
 			}},
 		},
 		},
+		{[]string{"alice.pub"}, []string{"foo.tar.gz"}, []string{}, validKey, []string{"sha256"}, Metablock{
+			Signed: Link{
+				Name: linkName,
+				Type: "link",
+				Materials: map[string]interface{}{
+					"alice.pub": map[string]interface{}{
+						"sha256": "f051e8b561835b7b2aa7791db7bc72f2613411b0b7d428a0ac33d45b8c518039",
+					},
+				},
+				Products: map[string]interface{}{
+					"foo.tar.gz": map[string]interface{}{
+						"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
+					},
+				},
+				ByProducts:  map[string]interface{}{},
+				Command:     []string{},
+				Environment: map[string]interface{}{},
+			},
+			Signatures: []Signature{{
+				KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
+				Sig:   "f4a2d468965d595b4d29615fb2083ef7ac22a948e1530925612d73ba580ce9765d93db7b7ed1b9755d96f13a6a1e858c64693c2f7adcb311afb28cb57fbadc0c",
+			}},
+		},
+		},
 	}
 
 	for _, table := range tablesCorrect {