Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid shell injection problems by using subprocess.run() #95

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## 3.0.0

- The anonymizer option is back

## 2.1.5

## 2.1.4 - 2019-04-04
Expand Down
19 changes: 9 additions & 10 deletions dcm2bids/dcm2bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import os
import platform
import sys
import subprocess
from datetime import datetime
from glob import glob
from .dcm2niix import Dcm2niix
from .logger import setup_logging
from .sidecar import Sidecar, SidecarPairing
from .structure import Participant
from .utils import DEFAULT, load_json, save_json, run_shell_command, splitext_
from .utils import DEFAULT, load_json, save_json, splitext_
from .version import __version__, check_latest, dcm2niix_version


Expand Down Expand Up @@ -166,17 +167,17 @@ def move(self, acquisition):

# it's an anat nifti file and the user using a deface script
if (
self.config.get("defaceTpl")
self.config.get("deface")
and acquisition.dataType == "anat"
and ".nii" in ext
):
try:
os.remove(dstFile)
except FileNotFoundError:
pass
defaceTpl = self.config.get("defaceTpl")
cmd = defaceTpl.format(srcFile=srcFile, dstFile=dstFile)
run_shell_command(cmd)
cmd = ["pydeface", "--outfile", dstFile, srcFile]
self.logger.info("Running %s" % (shlex.join(cmd),))
subprocess.run(cmd, check=True)

# use
elif ext == ".json":
Expand Down Expand Up @@ -260,7 +261,7 @@ def get_arguments():
action="store_true",
help="""
This option no longer exists from the script in this release.
See:https://github.com/unfmontreal/Dcm2Bids/blob/master/README.md#defaceTpl""",
See:https://github.com/unfmontreal/Dcm2Bids/blob/master/README.md#deface""",
)

args = parser.parse_args()
Expand All @@ -276,12 +277,10 @@ def main():
"""
The anonymizer option no longer exists from the script in this release
It is still possible to deface the anatomical nifti images
Please add "defaceTpl" key in the congifuration file
Please add "deface" key in the configuration file

For example, if you use the last version of pydeface, add:
"defaceTpl": "pydeface --outfile {dstFile} {srcFile}"
It is a template string and dcm2bids will replace {srcFile} and {dstFile}
by the source file (input) and the destination file (output)
"deface": true
"""
)
return 1
Expand Down
18 changes: 6 additions & 12 deletions dcm2bids/dcm2niix.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

import logging
import os
import shlex
import shutil
import subprocess
from glob import glob
from .utils import DEFAULT, run_shell_command
from .utils import DEFAULT


class Dcm2niix(object):
Expand Down Expand Up @@ -95,14 +97,6 @@ def execute(self):
""" Execute dcm2niix for each directory in dicomDirs
"""
for dicomDir in self.dicomDirs:
commandTpl = "dcm2niix {} -o {} {}"
cmd = commandTpl.format(self.options, self.outputDir, dicomDir)
output = run_shell_command(cmd)

try:
output = output.decode()
except:
pass

self.logger.debug("\n%s", output)
self.logger.info("Check log file for dcm2niix output")
cmd = ['dcm2niix', *shlex.split(self.options), '-o', self.outputDir, dicomDir]
self.logger.info("Running %s" % (shlex.join(cmd),))
subprocess.run(cmd, check=True)
14 changes: 1 addition & 13 deletions dcm2bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import os
import shlex
from collections import OrderedDict
from subprocess import check_output


class DEFAULT(object):
Expand All @@ -23,7 +22,7 @@ class DEFAULT(object):
session = cliSession # also Participant object
clobber = False
forceDcm2niix = False
defaceTpl = None
deface = False
logLevel = "WARNING"

# dcm2niix.py
Expand Down Expand Up @@ -99,14 +98,3 @@ def splitext_(path, extensions=None):
if path.endswith(ext):
return path[: -len(ext)], path[-len(ext) :]
return os.path.splitext(path)


def run_shell_command(commandLine):
""" Wrapper of subprocess.check_output

Returns:
Run command with arguments and return its output
"""
logger = logging.getLogger(__name__)
logger.info("Running %s", commandLine)
return check_output(shlex.split(commandLine))
8 changes: 3 additions & 5 deletions dcm2bids/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""This module take care of the versioning"""

# dcm2bids version
__version__ = "2.1.5"
__version__ = "3.0.0"


import logging
Expand All @@ -14,8 +14,6 @@
from shutil import which


REQUIRED_MODULE_METADATA = (("future", {"min_version": "0.17.1"}),)

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -64,7 +62,7 @@ def check_github_latest(githubRepo):
"""
url = "https://github.com/{}/releases/latest".format(githubRepo)
try:
output = check_output(shlex.split("curl --silent " + url))
output = check_output(["curl", "--silent", url])
except:
logger.debug(
"Checking latest version of %s was not possible", githubRepo,
Expand Down Expand Up @@ -153,7 +151,7 @@ def dcm2niix_version():
return

try:
output = check_output(shlex.split("dcm2niix"))
output = check_output(["dcm2niix"])
except:
logger.error("Running: dcm2niix", exc_info=True)
return
Expand Down
14 changes: 4 additions & 10 deletions docs/4-advance.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ These optional configurations could be insert in the configuration file at the s
```
{
"searchMethod": "fnmatch",
"defaceTpl": "pydeface --outfile {dstFile} {srcFile}",
"deface": true,
"description": [
...
]
Expand All @@ -18,17 +18,11 @@ default: `"searchMethod": "fnmatch"`

fnmatch is the behaviour (See criteria) by default and the fall back if this option is set incorrectly. `re` is the other choice if you want more flexibility to match criteria.

## defaceTpl
## deface

default: `"defaceTpl": None`
default: `"deface": False`

The anonymizer option no longer exists from `v2.0.0`. It is still possible to deface the anatomical nifti images.

For example, if you use the last version of pydeface, add:

`"defaceTpl": "pydeface --outfile {dstFile} {srcFile}"`

It is a template string and dcm2bids will replace {srcFile} and {dstFile} by the source file (input) and the destination file (output).
To anonymize images with `pydeface`, set this to `True`.

## dcm2niixOptions

Expand Down
2 changes: 1 addition & 1 deletion example/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"searchMethod": "fnmatch",
"defaceTpl": "pydeface --outfile {dstFile} {srcFile}",
"deface": true,
"descriptions": [
{
"dataType": "anat",
Expand Down
12 changes: 5 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ def load_version():
return global_dict


def install_requires():
"""Get list of required modules"""
required = []
for module, meta in _VERSION["REQUIRED_MODULE_METADATA"]:
required.append("{}>={}".format(module, meta["min_version"]))
return required
install_requires = [
"future>=0.17.1",
"pydeface",
]


_VERSION = load_version()
Expand Down Expand Up @@ -71,7 +69,7 @@ def install_requires():
packages=find_packages(),
entry_points=ENTRY_POINTS,
python_requires=">=3.5",
install_requires=install_requires(),
install_requires=install_requires,
package_data={"": ["README.md", "LICENSE.txt"]},
author=AUTHOR,
author_email=AUTHOR_EMAIL,
Expand Down