summaryrefslogtreecommitdiff
path: root/content/development/update_rust_subprocess.rst
blob: 595f72a23935314c7293527fce2b8978f5aba19c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
Update: - Shell pipelines with subprocess crate and use of Exec::shell function
###############################################################################

:date: 2017-06-19 20:48 +0530
:slug: update-shell-pipelines
:tags: rust, subprocess, shell inejctions
:author: copyninja
:summary: Update to my previous post on using Exec::shell from subprocess crates
          to create shell pipeline.

In my `previous post <https://copyninja.info/blog/shell-pipelines-rust.html>`_ I
used `Exec::shell` function from subprocess crate and passed it string generated
by interpolating *--author* argument. This string was then run by the shell via
`Exec::shell`. After publishing post I got ping on IRC by `Jonas Smedegaard
<https://wiki.debian.org/JonasSmedegaard>`_ and `Paul Wise
<https://wiki.debian.org/PaulWise>`_ that I should replace `Exec::shell`, as it
might be prone to errors or vulnerabilities of shell injection attack. Indeed
they were right, in hurry I did not completely read the function documentation
which clearly mentions this fact.

    When invoking this function, be careful not to interpolate arguments into
    the string run by the shell, such as Exec::shell(format!("sort {}",
    filename)). Such code is prone to errors and, if filename comes from an
    untrusted source, to shell injection attacks. Instead, use
    Exec::cmd("sort").arg(filename).

Though I'm not directly taking input from untrusted source, its still possible
that the string I got back from git log command might contain some oddly
formatted string with characters of different encoding which could possibly
break the `Exec::shell` , as I'm not sanitizing the shell command. When we use
`Exec::cmd` and pass argument using *.args* chaining, the library takes care of
creating safe command line. So I went in and modified the function to use
`Exec::cmd` instead of `Exec::shell`.

Below is updated function.

.. code-block:: rust

   fn copyright_fromgit(repo: &str) -> Result<Vec<String>> {
       let tempdir = TempDir::new_in(".", "debcargo")?;
       Exec::shell(OsStr::new(format!("git clone --bare {} {}",
                                   repo,
                                   tempdir.path().to_str().unwrap())
                               .as_str()))
        .stdout(subprocess::NullFile)
        .stderr(subprocess::NullFile)
        .popen()?;

       let author_process = {
           Exec::shell(OsStr::new("git log --format=\"%an <%ae>\"")).cwd(tempdir.path()) |
           Exec::shell(OsStr::new("sort -u"))
       }.capture()?;
       let authors = author_process.stdout_str().trim().to_string();
       let authors: Vec<&str> = authors.split('\n').collect();
       let mut notices: Vec<String> = Vec::new();
       for author in &authors {
           let author_string = format!("--author={}", author);
           let first = {
               Exec::cmd("/usr/bin/git")
                .args(&["log", "--format=%ad",
                       "--date=format:%Y",
                       "--reverse",
                       &author_string])
                .cwd(tempdir.path()) | Exec::shell(OsStr::new("head -n1"))
           }.capture()?;

           let latest = {
               Exec::cmd("/usr/bin/git")
                .args(&["log", "--format=%ad", "--date=format:%Y", &author_string])
                .cwd(tempdir.path()) | Exec::shell("head -n1")
           }.capture()?;

           let start = i32::from_str(first.stdout_str().trim())?;
           let end = i32::from_str(latest.stdout_str().trim())?;
           let cnotice = match start.cmp(&end) {
               Ordering::Equal => format!("{}, {}", start, author),
               _ => format!("{}-{}, {}", start, end, author),
           };

           notices.push(cnotice);
       }

       Ok(notices)
   }

I still use `Exec::shell` for generating author list, this is not problematic as
I'm not interpolating arguments to create command string.