I found some FN in the BindToAllInterfaces rule. While the rule successfully catches direct literal bindings like s.bind(('0.0.0.0', 8080)), but I identified that it sometimes doesn't match some common real-world usage patterns.
First, the rule relies on tracking string literals directly into the bind() call. However, it misses cases where the address is stored in an object attribute or derived from environment variable defaults. The DataFlow::TypeTrackingNode configuration doesn't seem to propagate the taint through instance attributes or library default arguments. I am not sure whether this can be solved by configuring the dataflow tracking.
For example, storing the address in self.bind_addr bypasses the check:
class Server:
def __init__(self):
self.bind_addr = '0.0.0.0' # Taint lost here
self.port = 31137
def start(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((self.bind_addr, self.port)) # Rule misses this
Similarly, using os.environ.get with an insecure default value isn't flagged:
import os
import socket
# The default '0.0.0.0' is not tracked as a taint source
host = os.environ.get('APP_HOST', '0.0.0.0')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, 8080)) # Rule misses this
Second,
The rule is hardcoded to check API::moduleImport("socket"). This excludes widely used alternatives like gevent.socket and high-level framework runners that perform binding internally.
For instance, using gevent.socket evades the module check:
from gevent import socket # Not 'import socket.'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 31137)) # Rule misses this
High-level framework methods like Sanic.run or aiohttp.web.run_app also bind sockets but aren't modeled as sinks:
from sanic import Sanic
app = Sanic(__name__)
if __name__ == "__main__":
# Internally binds to 0.0.0.0, but doesn't call socket.bind() directly
app.run(host='0.0.0.0', port=31137) # Rule misses this
But I am not sure if there is another rule to handle these framework cases.
I found some FN in the BindToAllInterfaces rule. While the rule successfully catches direct literal bindings like s.bind(('0.0.0.0', 8080)), but I identified that it sometimes doesn't match some common real-world usage patterns.
First, the rule relies on tracking string literals directly into the bind() call. However, it misses cases where the address is stored in an object attribute or derived from environment variable defaults. The DataFlow::TypeTrackingNode configuration doesn't seem to propagate the taint through instance attributes or library default arguments. I am not sure whether this can be solved by configuring the dataflow tracking.
For example, storing the address in self.bind_addr bypasses the check:
Similarly, using os.environ.get with an insecure default value isn't flagged:
Second,
The rule is hardcoded to check API::moduleImport("socket"). This excludes widely used alternatives like
gevent.socketand high-level framework runners that perform binding internally.For instance, using gevent.socket evades the module check:
High-level framework methods like Sanic.run or aiohttp.web.run_app also bind sockets but aren't modeled as sinks:
But I am not sure if there is another rule to handle these framework cases.