Here is the combined list of vulnerabilities, formatted in markdown, with duplicates removed and sections merged for similar vulnerabilities:
-
Description:
- The
waffle
application exposes endpoints/waffle_status
(named URLwaffle_status
) and/wafflejs
(named URLwafflejs
) that return information about all defined flags, switches, and samples./waffle_status
returns a JSON response, while/wafflejs
returns JavaScript code embedding the same data. - An external attacker can access these endpoints without any authentication.
- By accessing these endpoints, the attacker can enumerate all feature flags, switches, and samples configured in the application, including their names, active status, and last modified timestamps.
- This information can reveal details about upcoming features, internal application logic, and potentially sensitive configuration if flag, switch, or sample names are chosen descriptively or contain sensitive keywords.
- Step-by-step trigger:
- Identify the publicly accessible URLs for the
waffle_status
andwafflejs
endpoints (typically/waffle_status
and/wafflejs
). - Send an HTTP GET request to either of these URLs using a web browser or a tool like
curl
. - Observe the JSON response from
/waffle_status
or the JavaScript code from/wafflejs
, which lists all flags, switches, and samples with their active status and last modified timestamps.
- Identify the publicly accessible URLs for the
- The
-
Impact:
- Information Leakage: Exposure of feature flag, switch, and sample names, their status, and last modified timestamps.
- Security Misconfiguration: Revealing internal application configuration details through feature toggle names, potentially aiding further attacks.
-
Vulnerability Rank: High
-
Currently Implemented Mitigations:
- None. The
/waffle_status
and/wafflejs
endpoints are publicly accessible without any authentication or authorization checks. They are only decorated with@never_cache
to prevent caching.
- None. The
-
Missing Mitigations:
- Access Control: Implement access control to the
/waffle_status
and/wafflejs
endpoints. Restrict access to authenticated administrators or internal services only. This can be achieved by:- Requiring authentication for access to these endpoints.
- Implementing IP-based whitelisting to allow access only from trusted networks.
- Using Django's permission system to restrict access based on user roles.
- Consider removing or limiting the exposure of this data in production environments.
- Access Control: Implement access control to the
-
Preconditions:
- The
waffle.urls
are included in the project'surlpatterns
. - The application is deployed and publicly accessible.
- The
-
Source Code Analysis:
- File:
/code/waffle/urls.py
from django.urls import path from waffle.views import wafflejs, waffle_json urlpatterns = [ path('wafflejs', wafflejs, name='wafflejs'), # Vulnerable endpoint path('waffle_status', waffle_json, name='waffle_status'), # Vulnerable endpoint ]
- File:
/code/waffle/views.py
@never_cache def waffle_json(request): # Function handling the /waffle_status endpoint return JsonResponse(_generate_waffle_json(request)) @never_cache def wafflejs(request): # Function handling the /wafflejs endpoint data = _generate_waffle_json(request) js = 'var waffle = %s;' % json.dumps(data) return HttpResponse(js, 'application/javascript') def _generate_waffle_json(request: HttpRequest) -> dict[str, dict[str, Any]]: flags = get_waffle_flag_model().get_all() # Retrieves all flags flag_values = { f.name: { 'is_active': f.is_active(request), 'last_modified': f.modified, } for f in flags } switches = get_waffle_switch_model().get_all() # Retrieves all switches switch_values = { s.name: { 'is_active': s.is_active(), 'last_modified': s.modified, } for s in switches } samples = get_waffle_sample_model().get_all() # Retrieves all samples sample_values = { s.name: { 'is_active': s.is_active(), 'last_modified': s.modified, } for s in samples } return { 'flags': flag_values, 'switches': switch_values, 'samples': sample_values, }
- The code shows that both
waffle_json
andwafflejs
views, mapped to/waffle_status
and/wafflejs
URLs respectively, retrieve all flags, switches, and samples and return their names, active status, and last modified timestamps in a JSON response (or embedded in Javascript) without any access control.
- File:
-
Security Test Case:
- Deploy the django-waffle example application or an application using django-waffle with at least one Flag, Switch, and Sample defined.
- Access the
/waffle_status
endpoint using a web browser orcurl
from outside the application's network (as an external attacker). For example:curl http://<your-application-url>/waffle_status
- Verify that the response is a JSON object with HTTP response code 200 and
Content-Type
header indicates JSON. - Examine the JSON response and confirm that it contains keys such as
"flags"
,"switches"
, and"samples"
. - For each item in the lists, verify that the
name
,is_active
status andlast_modified
timestamp are exposed. - Repeat steps 2-5 for
/wafflejs
endpoint and check that the returned JavaScript embeds the same information. - Expected Result: The test should confirm that an unauthenticated external attacker can successfully retrieve a list of all flags, switches, and samples along with their status and last modified timestamps by accessing the
/waffle_status
and/wafflejs
endpoints. Conclude that internal configuration details have been exposed without access control.
-
Description:
- When a Flag is created with
testing=True
, thewaffle
middleware sets a cookie nameddwft_%s
(where%s
is the flag name) to enable testing override of the flag's behavior. - This cookie, used for testing purposes, is set without the
HttpOnly
andSecure
flags by default. - If an attacker can perform a Cross-Site Scripting (XSS) attack on the application, they can potentially access this cookie via JavaScript.
- If an attacker performs a Man-in-the-Middle (MITM) attack over an insecure HTTP connection, they could potentially intercept this cookie.
- By obtaining or manipulating this cookie, an attacker could potentially enable or disable features for a victim user in a testing scenario, potentially leading to unexpected application behavior or bypassing intended feature restrictions.
- Step-by-step trigger:
- Create a Flag in the Django admin panel with
testing
option enabled. - Access a page in the application where this flag might be evaluated. Observe the
dwft_<flag_name>
cookie being set in the browser's developer tools (if the flag logic is executed). - Using browser developer tools or a network intercepting proxy, inspect the attributes of the
dwft_<flag_name>
cookie. - Verify that the
HttpOnly
andSecure
flags are not set for this cookie.
- Create a Flag in the Django admin panel with
- When a Flag is created with
-
Impact:
- Session Hijacking/Manipulation: Potential for an attacker to manipulate the testing cookie if XSS or MITM is possible.
- Feature Bypass: Attacker might be able to bypass intended feature restrictions or enable features not meant for them in a testing context.
-
Vulnerability Rank: High
-
Currently Implemented Mitigations:
- The
WaffleMiddleware
sets cookies for both regular flags (dwf_%s
) and testing flags (dwft_%s
). However, only the regular flag cookies respect theSECURE
setting. The testing cookies do not explicitly setHttpOnly
orSecure
flags.
- The
-
Missing Mitigations:
- Set
HttpOnly
flag: Set theHttpOnly
flag toTrue
for thedwft_%s
cookies to prevent client-side JavaScript from accessing the cookie, mitigating XSS-based attacks targeting this cookie. - Set
Secure
flag: Set theSecure
flag toTrue
for thedwft_%s
cookies to ensure they are only transmitted over HTTPS, mitigating MITM attacks if the application uses HTTPS.
- Set
-
Preconditions:
- A Flag is created with
testing=True
. - The application evaluates this flag, causing the
dwft_%s
cookie to be set. - The application is vulnerable to XSS or is used over insecure HTTP connections, making MITM attacks possible.
- A Flag is created with
-
Source Code Analysis:
- File:
/code/waffle/middleware.py
class WaffleMiddleware(MiddlewareMixin): def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse: secure = get_setting('SECURE') # SECURE setting is retrieved max_age = get_setting('MAX_AGE') if hasattr(request, 'waffles'): for k in request.waffles: name = smart_str(get_setting('COOKIE') % k) active, rollout = request.waffles[k] if rollout and not active: age = None else: age = max_age response.set_cookie(name, value=active, max_age=age, secure=secure, httponly=True) # secure and httponly flag used here for dwf_%s cookies if hasattr(request, 'waffle_tests'): for k in request.waffle_tests: name = smart_str(get_setting('TEST_COOKIE') % k) value = request.waffle_tests[k] response.set_cookie(name, value=value) # secure and httponly flags are NOT used for dwft_%s cookies return response
- The code shows that
response.set_cookie
for testing cookies (inwaffle_tests
block) does not includesecure=secure
orhttponly=True
arguments, making them insecure by default, while regular waffle cookies (dwf_%s
) correctly usesecure=secure
andhttponly=True
.
- File:
-
Security Test Case:
- Deploy the django-waffle example application or an application using django-waffle.
- Create a Flag named
test_flag_xss
in the Django admin panel and setTesting
toYes
. - Access any page in the application.
- Using browser's developer tools, inspect the cookies set for the domain. Look for the
dwft_test_flag_xss
cookie. - Check the attributes of the
dwft_test_flag_xss
cookie. - Verify that the
HttpOnly
flag isFalse
or not present. - Verify that the
Secure
flag isFalse
or not present. - Expected Result: The test should confirm that the
dwft_test_flag_xss
cookie is set withoutHttpOnly
andSecure
flags, making it potentially vulnerable to XSS and MITM attacks.
-
Description:
- The project includes a settings file (
test_settings.py
) that is used by the provided startup script (run.sh
) and CI/CD workflows. This file sets critical values insecurely for a production environment—it enables debugging (DEBUG = True
) and uses a weak, hardcoded secret key (SECRET_KEY = 'foobar'
). - An attacker could exploit the exposed debug information and easily guess or forge cryptographic tokens if this configuration is deployed in a public production environment.
- Step-by-step trigger:
- Deploy the application using the provided
run.sh
script (which exportsDJANGO_SETTINGS_MODULE="test_settings"
). - As an unauthenticated user, trigger an error (for example, by accessing a non-existent route) so that Django’s debug error page is displayed.
- Examine the error page for sensitive internal details (such as stack traces and settings).
- Optionally, attempt to tamper with or forge session cookies knowing that they are signed using the weak secret key.
- Deploy the application using the provided
- The project includes a settings file (
-
Impact:
- Running with
DEBUG = True
in production can lead to detailed error messages being displayed to attackers; these messages may reveal sensitive information (such as file paths, configuration details, and even portions of source code). - A weak secret key undermines security measures including session signing and cryptographic tokens, making session hijacking or other forgery attacks feasible.
- Running with
-
Vulnerability Rank: High
-
Currently Implemented Mitigations:
- None. The repository’s default configuration in
test_settings.py
is intended only for testing but is used by default in the startup script and workflows.
- None. The repository’s default configuration in
-
Missing Mitigations:
- Provide a production-ready settings file: Create a separate settings file (e.g.,
production_settings.py
) that setsDEBUG = False
and uses a strong, unpredictableSECRET_KEY
. - Modify startup script: Update
run.sh
to use the production settings file by default or provide clear instructions and mechanisms to switch to production settings for deployment. - Prevent accidental use of test settings: Ensure that the production deployment environment cannot accidentally use the insecure
test_settings.py
, possibly by checking environment variables or using different deployment scripts for different environments.
- Provide a production-ready settings file: Create a separate settings file (e.g.,
-
Preconditions:
- The deployed instance must be using
test_settings.py
(or otherwise misconfigured with debugging enabled and a weak secret key) and be publicly accessible.
- The deployed instance must be using
-
Source Code Analysis:
- File:
/code/test_settings.py
DEBUG = True SECRET_KEY = 'foobar'
- The shell script
run.sh
unconditionally setsDJANGO_SETTINGS_MODULE="test_settings"
, meaning that even in a production environment, the insecure settings might be used by default.
- File:
-
Security Test Case:
- Deploy the application using the provided
run.sh
script without modifications. - As an external user, trigger an error (for example, by browsing to a non-existent URL) and verify that a detailed Django debug error page is shown with a stack trace and internal configuration details.
- Check that the session cookies (or any cryptographically signed cookies) are being generated with a known value (i.e., that the secret key is the weak string “foobar”). This might require inspecting cookie values or application logs depending on how session management is implemented.
- Confirm that sensitive debugging information is visible and that cryptographic protections may be easily bypassed.
- Expected Result: The test should confirm that deploying with default settings exposes sensitive debugging information and uses a weak secret key, making the application highly vulnerable in a production environment. Recommend that production deployments never use these settings.
- Deploy the application using the provided
-
Description:
- The application uses django-waffle for feature flagging and has the
WAFFLE_OVERRIDE
setting enabled (set toTrue
). - When
WAFFLE_OVERRIDE
isTrue
, theis_active
method of a Flag model checks for URL parameters matching the flag name. - An attacker can craft a URL with a parameter like
flag_name=1
to activate the flag orflag_name=0
to deactivate it for their session. - By manipulating these URL parameters, an attacker can bypass the intended feature flag logic and potentially access features that should be disabled for them or disable features that should be enabled.
- Step-by-step trigger:
- Ensure
WAFFLE_OVERRIDE = True
is set in the application's settings. - Identify a feature flag in the application (e.g.,
test_flag
). - Access a URL in the application, and observe the default behavior related to the feature flag.
- Modify the URL by appending a query parameter with the flag name and value '1' to activate it (e.g.,
?test_flag=1
) or '0' to deactivate it (e.g.,?test_flag=0
). - Access the modified URL and observe the change in application behavior, reflecting the overridden flag state.
- Ensure
- The application uses django-waffle for feature flagging and has the
-
Impact:
- Unauthorized access to features: Attackers can enable flags intended for specific user groups (e.g., staff, superusers) or future features not yet meant for public access.
- Unauthorized disabling of features: Attackers can disable flags that are essential for normal application functionality for their session.
- Security bypass: Depending on the features controlled by flags, this could lead to significant security bypasses, such as accessing admin functionalities, bypassing payment checks, or viewing sensitive data.
-
Vulnerability Rank: High
-
Currently Implemented Mitigations:
- None. The project provides the
WAFFLE_OVERRIDE
setting but relies on developers to disable it in production environments.
- None. The project provides the
-
Missing Mitigations:
- Strongly discourage
WAFFLE_OVERRIDE
in production: Document clearly against enablingWAFFLE_OVERRIDE
in production, emphasizing it's for development/testing only and must be disabled to prevent unauthorized feature access control. - Restrict
WAFFLE_OVERRIDE
functionality: Consider removingWAFFLE_OVERRIDE
from production code paths or restrict its usage to authenticated superusers only, even when enabled. - Implement a warning for production use: The application could check
WAFFLE_OVERRIDE
at startup and log a warning or refuse to start if enabled in a non-development environment.
- Strongly discourage
-
Preconditions:
- The
WAFFLE_OVERRIDE
setting insettings.py
is set toTrue
. - The application is deployed in a publicly accessible environment.
- At least one Flag is defined in the waffle system.
- The
-
Source Code Analysis:
- File:
waffle/models.py
- Class:
AbstractBaseFlag
- Method:
is_active(self, request: HttpRequest, read_only: bool = False) -> bool | None
def is_active(self, request: HttpRequest, read_only: bool = False) -> bool | None: # ... other checks ... if get_setting('OVERRIDE'): # [POINT OF VULNERABILITY] if self.name in request.GET: return request.GET[self.name] == '1' # ... rest of the logic ...
- The
is_active
method checks if theOVERRIDE
setting is enabled usingget_setting('OVERRIDE')
. - If
OVERRIDE
isTrue
, it directly checks if the flag's name exists as a key inrequest.GET
. - If the flag name is in
request.GET
, it returnsTrue
if the value is'1'
andFalse
otherwise. - This logic allows controlling the flag's active state directly through URL parameters if
WAFFLE_OVERRIDE
is enabled, bypassing all other intended flag activation logic.
- File:
-
Security Test Case:
- Pre-setup:
- Ensure
WAFFLE_OVERRIDE = True
is set intest_settings.py
or a similar settings file used for testing the vulnerability. - Start the Django development server or deploy the application to a test instance.
- Create a Flag named
test_flag
in the Django admin panel, with default settings (e.g., Everyone: Unknown).
- Ensure
- Test Steps:
- Access a URL that is protected by the
test_flag
(e.g.,/flag-on
view decorated with@waffle_flag('test_flag')
). Observe the expected behavior when the flag is not active by default (e.g., 404 response). - Modify the URL to include the query parameter
test_flag=1
(e.g.,/flag-on?test_flag=1
). - Access the modified URL in the browser.
- Access a URL that is protected by the
- Expected Result:
- The application should now exhibit the behavior associated with the flag being active (e.g., return a 200 OK response with specific content), demonstrating that the
test_flag
has been activated by the URL parameter, bypassing the default flag logic.
- The application should now exhibit the behavior associated with the flag being active (e.g., return a 200 OK response with specific content), demonstrating that the
- Cleanup:
- Revert
WAFFLE_OVERRIDE
setting toFalse
after testing.
- Revert
- Pre-setup: