Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
fb8afb7
add swarm and nomad parse exceptions
sfondev Apr 11, 2025
0098d97
add Nomad parser
sfondev Apr 11, 2025
ae6b6b1
add Swarm parser
sfondev Apr 11, 2025
5a1e69b
fix incorrect exception name
sfondev Apr 11, 2025
4c0178c
format swarm.py
sfondev Apr 25, 2025
10dd8e6
change Terraform parser get_element_info method visibility to protect…
sfondev Apr 25, 2025
602191f
changed the Transformer for Nomad to be a new class that extends the …
sfondev Apr 25, 2025
0adf8ae
format nomad.py
sfondev Apr 25, 2025
f5089f4
format nomad.py and fix missing abs file path issue
sfondev Jun 27, 2025
759ff54
fixes swarm.py adds support for 'extends' and 'includes' extension
sfondev Jun 27, 2025
899368c
fix docker_images_scraper.py: add support for the new search api and …
sfondev Jun 27, 2025
9680335
remove forgotten print 😅
sfondev Jul 17, 2025
7031412
fix boolean parsing
sfondev Jul 17, 2025
c32defa
swarm and nomad tech introduction
sfondev Aug 23, 2025
4cd8574
fix add more common tar compressed with bzip2 extension, also for zst…
sfondev Aug 23, 2025
602602b
adding the smells error messages
sfondev Aug 23, 2025
808439c
swarm and nomad parsers fixes
sfondev Aug 23, 2025
4d2fbd9
correctly escape the '%' character for copying into latex
sfondev Aug 23, 2025
5c67131
adding implementation of Log Collection and some of container image s…
sfondev Aug 23, 2025
253dc45
add deprecated official images smell
sfondev Aug 23, 2025
e178edc
added files needed for log collector/log aggregator smell
sfondev Aug 23, 2025
7ad6a8d
currently useless file, will remove later
sfondev Aug 23, 2025
d7177d4
currently useless file, will remove later
sfondev Aug 23, 2025
1e19b3d
added file needed for the unstable image smell
sfondev Aug 23, 2025
3a49007
adding no api gateway smell files
sfondev Aug 23, 2025
94712dc
adding priveleged containers, mounted docker socket and Nomad integri…
sfondev Aug 23, 2025
8a24153
adding multiple services per deployment unit and missing healthcheck …
sfondev Aug 23, 2025
f804267
fix wrong version of non official image smell detection
sfondev Aug 23, 2025
757d8c8
fix alternative ways of referring to an official image from Docker Hub
sfondev Aug 24, 2025
f8765a3
fix logic error and always make tag lower, since it can be mixed or c…
sfondev Aug 24, 2025
e9671bd
fix small parser mistake
sfondev Aug 24, 2025
85ee60a
try to fix self include when extending from another file
sfondev Aug 24, 2025
3f57889
format + extends should be fixed now...
sfondev Aug 24, 2025
27b9f6b
remove unused stuff
sfondev Aug 25, 2025
9a683b9
fix uniformize use of container image string element instead of whole…
sfondev Aug 25, 2025
060f0e5
fix rename error key
sfondev Aug 25, 2025
e7cfd7c
removed unused commented code
sfondev Aug 25, 2025
948e374
removed parsing of included files, was not a correct approach for sca…
sfondev Aug 25, 2025
663556d
rename class and add missing detection
sfondev Aug 25, 2025
52d225c
rename file
sfondev Aug 25, 2025
85a8fe9
fix typo in class name
sfondev Aug 25, 2025
ded778e
moved the detection of nomad no integrity check together with the others
sfondev Aug 25, 2025
0594562
fix: move smell checkers out of visitor.py
sfondev Aug 25, 2025
5b6cd01
move container image name parser util to separated file
sfondev Aug 25, 2025
8bd9632
format swarm.py
sfondev Aug 25, 2025
ae48806
fix matching invalid ip bindings in complete command invocations, by …
sfondev Aug 28, 2025
0ade21c
fix typo
sfondev Aug 28, 2025
f8ce625
remove hack, fix variable parsing in swarm
sfondev Aug 28, 2025
7ceb362
change to use StringChecker and add handling of arrays and hashes
sfondev Aug 28, 2025
365c906
fix empty strings in env var value
sfondev Aug 28, 2025
aef512b
fix some of SecurityVisitor global constants not being initialized fo…
sfondev Aug 28, 2025
f1ccef5
add: swarm tests
sfondev Aug 28, 2025
578bb59
add missing __init__.py needed for test detection
sfondev Aug 28, 2025
9d12ba3
fix filenames of official docker images and deprecated official image…
sfondev Aug 29, 2025
e6150d4
update official docker images list
sfondev Aug 29, 2025
cb0e621
remove unnecessary method
sfondev Aug 29, 2025
200e9dc
remove unused import
sfondev Aug 29, 2025
febd84d
fix: consider both list of official images. The newer list of officia…
sfondev Aug 30, 2025
374c39f
format visitor.py
sfondev Aug 30, 2025
8a5f19e
fix missing healthchecks smell and wobbly service interaction on nomad
sfondev Aug 30, 2025
fa902f5
add nomad tests
sfondev Aug 30, 2025
4ee287e
fix: add hack to avoid using detection function tailored to nomad for…
sfondev Aug 30, 2025
9835c68
fix name of nomad task config logging driver attribute
sfondev Sep 4, 2025
7caa60e
hack: support multiple artifacts missing integrity checks in Nomad
sfondev Sep 9, 2025
6d9cad3
fix: pinned container image without digest smell
sfondev Sep 9, 2025
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
6 changes: 6 additions & 0 deletions glitch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from glitch.parsers.puppet import PuppetParser
from glitch.parsers.terraform import TerraformParser
from glitch.parsers.gha import GithubActionsParser
from glitch.parsers.swarm import SwarmParser
from glitch.parsers.nomad import NomadParser
from glitch.exceptions import throw_exception
from glitch.repair.interactive.main import run_infrafix
from pkg_resources import resource_filename
Expand Down Expand Up @@ -90,6 +92,10 @@ def __get_parser(tech: Tech) -> Parser:
return TerraformParser()
elif tech == Tech.gha:
return GithubActionsParser()
elif tech == Tech.swarm:
return SwarmParser()
elif tech == Tech.nomad:
return NomadParser()
else:
raise ValueError(f"Invalid tech: {tech}")

Expand Down
28 changes: 27 additions & 1 deletion glitch/analysis/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,40 @@ class Error:
"sec_hard_secr": "Hard-coded secret - Developers should not reveal sensitive information in the source code. (CWE-798)",
"sec_hard_pass": "Hard-coded password - Developers should not reveal sensitive information in the source code. (CWE-259)",
"sec_hard_user": "Hard-coded user - Developers should not reveal sensitive information in the source code. (CWE-798)",
"sec_invalid_bind": "Invalid IP address binding - Binding to the address 0.0.0.0 allows connections from every possible network which might be a security issues. (CWE-284)",
"sec_invalid_bind": "Invalid IP address binding - Binding to the address 0.0.0.0 allows connections from every possible network which might be a security issue. (CWE-284)",
"sec_no_int_check": "No integrity check - The content of files downloaded from the internet should be checked. (CWE-353)",
"sec_no_default_switch": "Missing default case statement - Not handling every possible input combination might allow an attacker to trigger an error for an unhandled value. (CWE-478)",
"sec_full_permission_filesystem": "Full permission to the filesystem - Files should not have full permissions to every user. (CWE-732)",
"sec_obsolete_command": "Use of obsolete command or function - Avoid using obsolete or deprecated commands and functions. (CWE-477)",
Tech.docker: {
"sec_non_official_image": "Use of non-official Docker image - Use of non-official images should be avoided or taken into careful consideration. (CWE-829)",
},
Tech.swarm: {
"sec_non_official_image": "Use of non-official Docker image - Use of non-official images should be avoided or taken into careful consideration. (CWE-829)",
"sec_image_integrity": "Use of image is not tagged with digest - The images downloaded from the internet should be checked. (CWE-353)",
"sec_unstable_tag": "Unstable version/release image tag - Prefer specifying a release version or better yet a specific version digest (CWE-353)",
"sec_no_image_tag": "The image is not tagged - Prefer specifying a release version or better yet specific version digest",
"arc_no_apig": "No API Gateway - If following a microservices architecture you should use in front of your services an API Gateway instead of directly exposing them.",
"arc_no_logging": "Log Collection not found - It is advised to setup logging for your services.",
"arc_missing_healthchecks": "Missing Healthchecks - You should setup healthchecks for your services, or check if the images used already have default ones.",
"sec_mounted_docker_socket": "Docker socket mounted to a container - Avoid mounting the Docker socket to container, if the container is compromised its access to the Docker socket allows control of all other containers, and even acquiring control of the host machine.",
"sec_privileged_containers": "Use of Privileged Containers - Developers should always try to give and use the least privileges possible. Use of privileged containers severely thins out the security and isolation provided by container runtimes, its use should be avoided as much as possible (CWE-250)",
"sec_depr_off_imgs" : "Use of deprecated official Docker images - Use of official deprecated images should be avoided as it makes you open to vulnerabilities, quality issues and unfixed bugs (CWE-1104)",
},
Tech.nomad: {
"sec_non_official_image": "Use of non-official Docker image - Use of non-official images should be avoided or taken into careful consideration. (CWE-829)",
"sec_image_integrity": "Container image is not tagged with digest - The images downloaded from the internet should be checked. (CWE-353)",
"sec_unstable_tag": "Unstable version/release image tag - Prefer specifying a release version or better yet a specific version digest (CWE-353)",
"sec_no_image_tag": "Container Image is not tagged - Prefer specifying a release version or better yet specific version digest (CWE-353)",
"arc_no_apig": "No API Gateway - If following a microservices architecture you should use in front of your services an API Gateway instead of directly exposing them.",
"arc_no_logging": "Log Collection not found - It is advised to setup logging for your services.",
"arc_missing_healthchecks": "Missing Healthchecks - You should setup healthchecks for your services, or check if the images used already have default ones.",
"sec_mounted_docker_socket": "Docker socket mounted to a container - Avoid mounting the Docker socket to container, if the container is compromised its access to the Docker socket allows control of all other containers, and even acquiring control of the host machine.",
"sec_privileged_containers": "Use of Privileged Containers - Developers should always try to give and use the least privileges possible. Use of privileged containers severely thins out the security and isolation provided by container runtimes, its use should be avoided as much as possible (CWE-250)",
"arc_multiple_services": "Multiple Services per Deployment Unit - If you are following a Microservices architecture you are violating the idependent deployability rule by deploying multiple microservices in the same group.",
"sec_depr_off_imgs" : "Use of deprecated official Docker images - Use of official deprecated images should be avoided as it makes you open to vulnerabilities, quality issues and unfixed bugs (CWE-1104)",
"arc_wobbly_service_interaction" : "Wobbly Service Interaction - If you are following a Microservices you are likely compromising the principle of isolation of failures of microservice. Using a Consul sidecar proxy allows having Circuit Breakers and Timeouts that avoid cascading failures due to wobbly interactions between microservices",
},
Tech.terraform: {
"sec_integrity_policy": "Integrity Policy - Image tag is prone to be mutable or integrity monitoring is disabled. (CWE-471)",
"sec_ssl_tls_policy": "SSL/TLS/mTLS Policy - Developers should use SSL/TLS/mTLS protocols and their secure versions. (CWE-326)",
Expand Down
84 changes: 84 additions & 0 deletions glitch/analysis/security/container_image_tags_smells.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.analysis.security.visitor import SecurityVisitor
from glitch.repr.inter import CodeElement, KeyValue, Hash, String
from glitch.analysis.utils import parse_container_image_name
from typing import List


class ContainerImageTagsSmells(SecuritySmellChecker):
# NOTE: This is the implementation for Nomad and Swarm
def check(self, element: CodeElement, file: str) -> List[Error]:
errors: List[Error] = []
bad_element = element
if isinstance(element, KeyValue) and (
(element.name == "image" and isinstance(element.value, String))
or (isinstance(element.value, Hash) and element.name == "config")
):
image = ""

if isinstance(element.value, String):
image = element.value.value
else:
for k, v in element.value.value.items():
if isinstance(k, String) and k.value == "image":
image = v.value
bad_element = v
break

has_digest, has_tag = False, False
_, tag, digest = parse_container_image_name(image)

if tag != "":
has_tag = True
if digest != "":
has_digest = True

if image != "" and has_digest: # image tagged with digest
checksum_s = digest.split(":")
checksum = checksum_s[-1]
if (
checksum_s[0] == "sha256" and len(checksum) != 64
): # sha256 256 digest -> 64 hexadecimal digits
errors.append(
Error(
"sec_image_integrity",
bad_element,
file,
repr(bad_element),
)
)

if image != "" and not has_digest:
errors.append(
Error(
"sec_image_integrity",
bad_element,
file,
repr(bad_element),
)
)
if image != "" and has_tag:
tag = tag.lower()

dangerous_tags: List[str] = SecurityVisitor.DANGEROUS_IMAGE_TAGS

for dt in dangerous_tags:
if dt in tag:
errors.append(
Error(
"sec_unstable_tag",
bad_element,
file,
repr(bad_element),
)
)
break
if (
image != "" and not has_digest and not has_tag
): # Image not tagged, avoids mistakenely nomad tasks without images (non-docker or non-podman)
errors.append(
Error("sec_no_image_tag", bad_element, file, repr(bad_element))
)

return errors
47 changes: 47 additions & 0 deletions glitch/analysis/security/deprecated_official_images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.analysis.security.visitor import SecurityVisitor
from glitch.repr.inter import CodeElement, KeyValue, Hash, String
from glitch.analysis.utils import parse_container_image_name
from typing import List


class DeprecatedOfficialDockerImages(SecuritySmellChecker):
def check(self, element: CodeElement, file: str) -> List[Error]:
errors: List[Error] = []
image = ""
bad_element = element
if isinstance(element, KeyValue) and element.name == "image":
if isinstance(element.value, String):
image = element.value.value
elif (
isinstance(element, KeyValue)
and element.name == "config"
and isinstance(element.value, Hash)
):
for k, v in element.value.value.items():
if isinstance(k, String) and k.value == "image":
image = v.value
bad_element = v
break
if image != "":
img_name, _, _ = parse_container_image_name(image)
for obsolete_img in SecurityVisitor.DEPRECATED_OFFICIAL_DOCKER_IMAGES:
obsolete_img_dockerio = f"docker.io/library/{obsolete_img}"
obsolete_img_library = f"library/{obsolete_img}"
obsolete_img_complete_link = (
f"registry.hub.docker.com/library/{obsolete_img}"
)

if (
img_name == obsolete_img
or img_name == obsolete_img_dockerio
or img_name == obsolete_img_library
or img_name == obsolete_img_complete_link
):
errors.append(
Error("sec_depr_off_imgs", bad_element, file, repr(bad_element))
)
break

return errors
46 changes: 46 additions & 0 deletions glitch/analysis/security/docker_socket_mounted.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from glitch.analysis.rules import Error
from glitch.analysis.security.smell_checker import SecuritySmellChecker
from glitch.repr.inter import CodeElement, KeyValue, Hash, String,Array
from typing import List


class DockerSocketMountedInsideContainer(SecuritySmellChecker):
def check(self, element: CodeElement, file: str) -> List[Error]:
errors: List[Error] = []
if isinstance(element, KeyValue):
if element.name == "volumes" and isinstance(element.value, Array):
for volume in element.value.value:
if isinstance(volume, String) and volume.value.split(":")[
0
].startswith("/var/run/docker.sock"):
errors.append(
Error(
"sec_mounted_docker_socket", volume, file, repr(volume)
)
)
break
elif element.name == "config" and isinstance(element.value, Hash):
found_socket_exposed = False
for k, v in element.value.value.items():
if (
isinstance(k, String)
and k.value == "volumes"
and isinstance(v, Array)
):
for volume in v.value:
if isinstance(volume, String) and volume.value.split(":")[
0
].startswith("/var/run/docker.sock"):
errors.append(
Error(
"sec_mounted_docker_socket",
volume,
file,
repr(volume),
)
)
found_socket_exposed = True
break
if found_socket_exposed:
break
return errors
12 changes: 9 additions & 3 deletions glitch/analysis/security/hard_secr.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,16 @@ def __check_pair(
SecurityVisitor.PASSWORDS + SecurityVisitor.SECRETS + SecurityVisitor.USERS
):
secr_checker = StringChecker(
lambda s: re.match(
r"[_A-Za-z0-9$\/\.\[\]-]*{text}\b".format(text=item), s
lambda s: (
re.match(r"[_A-Za-z0-9$\/\.\[\]-]*{text}\b".format(text=item), s)
is not None
)
or (
re.match(
r"[_A-Za-z0-9$\/\.\[\]-]*{text}\b".format(text=item.upper()), s
)
is not None
)
is not None
)
if secr_checker.check(name) and not whitelist_checker.check(name):
if not var_checker.check(value):
Expand Down
7 changes: 7 additions & 0 deletions glitch/analysis/security/invalid_bind.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from glitch.repr.inter import *
from glitch.analysis.expr_checkers.string_checker import StringChecker
from typing import List
from shlex import split as shsplit


class InvalidBind(SecuritySmellChecker):
Expand All @@ -27,4 +28,10 @@ def check(self, element: CodeElement, file: str) -> List[Error]:
):
return [Error("sec_invalid_bind", element, file, repr(element))]

if isinstance(element, KeyValue) and isinstance(element.value, String):
#HACK: splits a string in command parts as for complete commmands invocations the regex wasn't
# matching on full command invocations that included a reference to "0.0.0.0" or the its http/s variants
for part in shsplit(element.value.value):
if check_invalid.str_check(part):
return [Error("sec_invalid_bind", element, file, repr(element))]
return []
Loading