SkillTotal

Is Django safe?

Some risk - review before installing
Notable — review in context (capabilities are not malware):
  • Unsafe deserialization
  • Python dynamic code execution
  • Python shell/command execution

Django is an AI python_package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 7 risky constructs are reported for review. It can: dynamic code execution, filesystem read, filesystem write, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 30/100 (medium).

Django 6.0.6

python_package · pypi:django
MEDIUM
30
/ 100 risk score
Snapshot · scanned Jul 5, 2026 · Django@6.0.6 · engine 0.30.0 / ruleset 28

Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of Django's authors. Report a false positive.

Capabilities — what this component can do (not a risk score):
dynamic code executionfilesystem readfilesystem writenetwork egressshell execution

Findings (7)

HIGHUnsafe deserializationST-DESERIALIZE-PY

It loads data with a format that can rebuild arbitrary objects (e.g. pickle, or unsafe YAML).

value = pickle.loads(base64.b64decode(value.encode()))
return pickle.loads(zlib.decompress(f.read()))
previous_value = pickle.loads(zlib.decompress(f.read()))

Why it matters: Feeding such a loader untrusted data can execute code hidden inside that data.

Fix: Deserialize untrusted data with a safe format/loader: JSON, or yaml.safe_load / Loader=SafeLoader. Reserve pickle/marshal for data you fully control.

HIGHPython dynamic code executionST-DYN-PY

The code turns strings into live code at runtime (eval / new Function / exec).

exec(compile(pythonrc_code, pythonrc, "exec"), imported_objects)
exec(options["command"], {**globals(), **self.get_namespace(**options)})
exec(sys.stdin.read(), {**globals(), **self.get_namespace(**options)})
return eval(code, {}, {"datetime": datetime, "timezone": timezone})

Why it matters: If those strings aren't fixed and trusted, they become a way to run arbitrary code.

Fix: Avoid evaluating dynamically constructed code; if unavoidable, ensure the input is a trusted constant and never derived from external data.

HIGHPython shell/command executionST-SHELL-PY

The component can run operating-system commands or spawn processes.

p = run(args, capture_output=True, close_fds=os.name != "nt")
subprocess.run(
                [black_path, "--fast", "--", *written_files],
                capture_output=True,
            )
subprocess.run(args, env=env, check=True)
subprocess.Popen(
                dump_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=dump_env
            ) as dump_proc,
subprocess.Popen(
                load_cmd,
                stdin=dump_proc.stdout,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.PIPE,
                env=load_env,
            ) as load_proc,
p = subprocess.run(args, env=new_environ, close_fds=False)
git_log = subprocess.run(
        "git log --pretty=format:%ct --quiet -1 HEAD",
        capture_output=True,
        shell=True,
        cwd=repo_dir,
        text=True,
    )

Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.

Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; avoid shell=True.

MEDIUMServer bound to all network interfacesST-EXPOSE-BIND

A server is bound to all network interfaces (0.0.0.0), not just your own machine.

Why it matters: Without authentication, other hosts on the network can reach it.

Fix: Bind to 127.0.0.1 for local-only use, or require authentication and restrict access if remote exposure is intended.

MEDIUMPython filesystem readST-FS-PY-READ

The component reads files from disk.

template_contents = template_file.read_text()
with open(file_h, "rb") as file_h:
with open(self._key_to_file(), encoding="ascii") as session_file:
self.file = open(self.name, mode or self.mode, *args, **kwargs)
file = open(file_or_path, "rb")
with open(old_file_name, "rb") as old_file:
return File(open(self.path(name), mode))
with open(self.path, encoding="utf-8") as fp:
msgs = Path(pofile).read_text(encoding="utf-8")
with open(potfile, encoding="utf-8") as fp:
with open(django_po, encoding="utf-8") as fp:
with open(old_path, encoding="utf-8") as template_file:
with open(template_file, encoding="utf-8") as fp:
source = exception_file.read_text()
with open(origin.name, encoding=self.engine.file_charset) as fp:
with builtin_template_path("csrf_403.html").open(encoding="utf-8") as fh:
with open(filename, "rb") as fp:
with builtin_template_path("technical_404.html").open(encoding="utf-8") as fh:
with builtin_template_path("default_urlconf.html").open(encoding="utf-8") as fh:

Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.

Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.

MEDIUMPython filesystem write/deleteST-FS-PY-WRITE

The component writes or deletes files on disk.

shutil.move(output_file_name, session_file_name)
os.unlink(self._key_to_file(session_key))
with open(self._key_to_file(key, version), "r+b") as f:
copystat(old_file_name, new_file_name)
copymode(old_file_name, new_file_name)
os.remove(old_file_name)
os.remove(content.temporary_file_path())
self.stream = open(self._get_filename(), "ab")
with open(self.work_path, "w", encoding="utf-8") as fp:
with open(potfile, "a", encoding="utf-8", newline="\n") as fp:
with open(potfile, "w", encoding="utf-8") as fp:
with open(pofile, "w", encoding="utf-8") as fp:
with open(writer.path, "w", encoding="utf-8") as fh:

Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.

Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.

MEDIUMPython network egressST-NET-PY

The component makes outbound network requests.

from urllib.parse import urlsplit
elif not settings.DEBUG or urlsplit(prefix).netloc:
from urllib.parse import parse_qsl
from urllib.parse import quote as urlquote
from urllib.parse import urlsplit
query_string = urlsplit(request.build_absolute_uri()).query
return parse_qsl(query_string.replace(preserved_filters, ""))
obj_repr = format_html('<a href="{}">{}</a>', urlquote(obj_url), obj)
"obj": format_html('<a href="{}">{}</a>', urlquote(request.path), obj),
from urllib.parse import parse_qsl, unquote, urlsplit, urlunsplit
parsed_qs = dict(parse_qsl(parsed_url[3]))
preserved_filters = dict(parse_qsl(preserved_filters))
match_url = "/%s" % unquote(url).partition(get_script_prefix())[2]
parse_qsl(preserved_filters["_changelist_filters"])
from urllib.parse import urlsplit
login_scheme, login_netloc = urlsplit(resolved_login_url)[:2]
current_scheme, current_netloc = urlsplit(path)[:2]
from urllib.parse import urlsplit
login_scheme, login_netloc = urlsplit(resolved_login_url)[:2]
current_scheme, current_netloc = urlsplit(path)[:2]
from urllib.parse import urlsplit
login_scheme, login_netloc = urlsplit(resolved_login_url)[:2]
current_scheme, current_netloc = urlsplit(path)[:2]

Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.

Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.

Check your own component

Run the same evidence-backed scan on any MCP server, agent skill, or package.

Scan your own component

Or get notified if this component's risk changes:

How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →