Access Control: Tenant isolation¶
Identifier:
tenant_isolation
Scanner(s) Support¶
GraphQL Scanner | REST Scanner | WebApp Scanner |
---|---|---|
Description¶
When a system isn't properly isolating tenant data, it can mistakenly flag the same object as belonging to more than one user, breaching strict separation rules. This type of vulnerability is dangerous because it can lead to unauthorized data access where one tenant might see or even change another tenant's data. It often happens due to misconfigured rules or flawed logic in how object instances are tracked and associated with users, making it easier to unintentionally combine or share sensitive information across tenants. Developers should keep a close eye on how their application defines and enforces tenant boundaries to avoid these kinds of security pitfalls.
By default, when no configuration is provided, the check will cover all paths and keys matching. Response similarity will be used to detect isolation violations.
References:
Configuration¶
Example¶
Example configuration:
---
security_tests:
tenant_isolation:
assets_allowed:
- REST
- GRAPHQL
- WEBAPP
keys_matching:
- card_number
main_user: ''
natural_language_rule: Ensure that a user's notes cannot be accessed by other
users.
other_users:
detect:
- if_: request.is_authenticated
is_: true
is_not: null
- if_: helpers.fingerprints.same
is_: true
is_not: null
paths:
- /users/{id}
skip: false
specific_users: {}
Reference¶
assets_allowed
¶
Type : List[AssetType]
*
List of assets that this check will cover.
keys_matching
¶
Type : List[string]
*
List of keys in a response body that will be compared between different users, to detect an isolation violation.
If the key values are the exact same between these users, an alert will be raised.
For example if you want to control the key card_number
, you can use the following:
main_user
¶
Type : string
The main user to use for the check. It will be used as source of truth to detect isolation violations with all other users.
natural_language_rule
¶
Type : string
A natural language prompt to describe what should be checked for tenant isolation. This will be used to generate the rules to detect tenant isolation issues when analyzing the responses. You can review the generated rules in the alert details, or the scan logs with the prefix [Agentic - Tenant Isolation]
other_users
¶
Type : TenantIsolationRule
*
The conditions to trigger the alert when comparing the original and switched responses. The list of conditions are combined with AND logic by default. By default, the conditions check if the request is authenticated and if the responses of both users have the same fingerprint.
paths
¶
Type : List[string]
*
List of paths that this check will cover. Add *
to cover all paths.
For example if you want to control the path /users/{id}
, you can use the following:
To cover all paths, you can use the following:
skip
¶
Type : boolean
Skip the test if true.
specific_users
¶
Type : Dict[string, TenantIsolationRule]
The conditions to trigger the alert when analyzing the responses between a given user and specific users. The list of conditions are combined with AND logic by default.
APILogicalAndDetector¶
and
¶
Type : List[APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector]
*
Logical and on a list of detectors
if
¶
Type : Const[and]
*
Use this to apply a logical and on a list of detectors.
Example¶
detect:
- if: and
and:
- if: helpers.request.crud
in:
- CREATE
- UPDATE
- if: response.status_code
is: 200
APILogicalNotDetector¶
if
¶
Type : Const[not]
*
Use this to apply a logical not on a detector.
Example¶
not
¶
Type : APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector
Logical not of a detector
APILogicalOrDetector¶
if
¶
Type : Const[or]
*
Use this to apply a logical or on a list of detectors.
Example¶
detect:
- if: or
or:
- if: helpers.request.crud
in:
- CREATE
- UPDATE
- if: response.status_code
is: 200
or
¶
Type : List[APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector]
*
Logical or on a list of detectors
FingerprintCountDetector¶
gt
¶
Type : integer
Condition is greater than this integer
if
¶
Type : Const[helpers.fingerprints.count]
*
Use this to select and compare the count of unique fingerprints of the current and original response.
in
¶
Type : List[integer]
Condition is in this list of integers (exact match)
is
¶
Type : integer
Condition is this exact integer
is_not
¶
Type : integer
Condition is not this exact integer
lt
¶
Type : integer
Condition is less than this integer
FingerprintsSameDetector¶
if
¶
Type : Const[helpers.fingerprints.same]
*
Use this to determine whether the current and original responses have the same fingerprint.
is
¶
Type : boolean
Condition is true
is_not
¶
Type : boolean
Condition is false
HelpersRequestCrudDetector¶
if
¶
Type : Const[helpers.request.crud]
*
Use this to select against the detected CRUD operation of the request.
Example¶
in
¶
Type : List[CustomRuleCrud]
Condition is the request is in this list of CRUD operations (exact match)
is
¶
Type : CustomRuleCrud
Condition is the request is this CRUD operation
is_not
¶
Type : CustomRuleCrud
Condition is the request is not this CRUD operation
HelpersResponseIsSuccessfulDetector¶
if
¶
Type : Const[helpers.response.is_successful]
*
Use this to check whether the response is successful.
Example¶
is
¶
Type : boolean
Condition is true
is_not
¶
Type : boolean
Condition is false
JSONMatchesAllDetector¶
if
¶
Type : Const[helpers.json_matches.all]
*
Use this to determine whether every the current and original responses contain the same JSON fragment.
is
¶
Type : boolean
Condition is true
is_not
¶
Type : boolean
Condition is false
jq
¶
Type : string
Use this to select the exact JSON you want to compare between the current and original response.
JSONMatchesCountDetector¶
gt
¶
Type : integer
Condition is greater than this integer
if
¶
Type : Const[helpers.json_matches.count]
*
Use this to count the number of times a JSON match is in the current and original response.
in
¶
Type : List[integer]
Condition is in this list of integers (exact match)
is
¶
Type : integer
Condition is this exact integer
is_not
¶
Type : integer
Condition is not this exact integer
jq
¶
Type : string
Use this to select the exact JSON you want to compare between the current and original response.
lt
¶
Type : integer
Condition is less than this integer
ObjectTypeMatcher¶
in
¶
Type : List[OBJECT_TYPE]
Object type is in the following list
is
¶
Type : OBJECT_TYPE
Object type is exactly this type
is_not
¶
Type : OBJECT_TYPE
Object type is any this type except this one
RegexMatchesAllDetector¶
if
¶
Type : Const[helpers.regex_matches.all]
*
Use this to determine whether every the current and original responses match the same regular expression.
is
¶
Type : boolean
Condition is true
is_not
¶
Type : boolean
Condition is false
regex
¶
Type : string
Condition is matched on this regex with fullmatch
RegexMatchesCountDetector¶
gt
¶
Type : integer
Condition is greater than this integer
if
¶
Type : Const[helpers.regex_matches.count]
*
Use this to count the number of times a regex match is in the current and original response.
in
¶
Type : List[integer]
Condition is in this list of integers (exact match)
is
¶
Type : integer
Condition is this exact integer
is_not
¶
Type : integer
Condition is not this exact integer
lt
¶
Type : integer
Condition is less than this integer
regex
¶
Type : string
Condition is matched on this regex with fullmatch
RequestBodyJSONDetector¶
if
¶
Type : Const[request.body.json]
*
Use this to select and compare the request body when detected as JSON, using jq-like syntax.
Example 1¶
Example 2¶
in
¶
Type : List[Union[Dict[string, object], List[object]]]
Condition is in this list of JSON
is
¶
Type : Union[Dict[string, object], List[object]]
Condition is this exact JSON
is_not
¶
Type : Union[Dict[string, object], List[object]]
Condition is not this exact JSON
jq
¶
Type : string
JQ query to match and use as boolean
RequestBodyTextDetector¶
contains
¶
Type : string
Contains this string
if
¶
Type : Const[request.body.text]
*
Use this to select and compare the request body as text, using string compare.
Example¶
in
¶
Type : List[string]
Condition is in this list (exact match)
is
¶
Type : string
Condition is this exact string
is_not
¶
Type : string
Condition is not this exact string
regex
¶
Type : string
Condition is matched on this regex with fullmatch
RequestHeadersDetector¶
if
¶
Type : Const[request.headers]
*
Use that to select and compare the request headers in a key value dictionary.
Example¶
key
¶
Type : StringMatcher
Key to match
value
¶
Type : StringMatcher
Value to match
RequestIsAuthenticatedDetector¶
if
¶
Type : Const[request.is_authenticated]
*
Use this to select whether or not whether the request is authenticated.
Example¶
is
¶
Type : boolean
Condition is true
is_not
¶
Type : boolean
Condition is false
RequestMethodDetector¶
if
¶
Type : Const[request.method]
*
Use this to select against the request HTTP Method.
Example¶
in
¶
Type : List[HTTPMethod]
Condition is the request is in this list of CRUD operations (exact match)
is
¶
Type : HTTPMethod
Condition is the request is this CRUD operation
is_not
¶
Type : HTTPMethod
Condition is the request is not this CRUD operation
RequestObjectDetector¶
if
¶
Type : Const[request.object]
*
Use this to select and compare the detected object scalars (including custom scalars) in the request, with their kind, name and value.
Example¶
name
¶
Type : StringMatcher
Object scalar name to match
type
¶
Type : ObjectTypeMatcher
Object scalar type to match
value
¶
Type : StringMatcher
Object scalar value to match
RequestUserDetector¶
contains
¶
Type : string
Contains this string
if
¶
Type : Const[request.user]
*
Use this to string compare the configured user for the request.
Example¶
in
¶
Type : List[string]
Condition is in this list (exact match)
is
¶
Type : string
Condition is this exact string
is_not
¶
Type : string
Condition is not this exact string
regex
¶
Type : string
Condition is matched on this regex with fullmatch
ResponseBodyJSONDetector¶
if
¶
Type : Const[response.body.json]
*
Use this to select and compare the response body when detected as JSON, using jq-like syntax.
Example 1¶
Example 2¶
in
¶
Type : List[Union[Dict[string, object], List[object]]]
Condition is in this list of JSON
is
¶
Type : Union[Dict[string, object], List[object]]
Condition is this exact JSON
is_not
¶
Type : Union[Dict[string, object], List[object]]
Condition is not this exact JSON
jq
¶
Type : string
JQ query to match and use as boolean
ResponseBodyTextDetector¶
contains
¶
Type : string
Contains this string
if
¶
Type : Const[response.body.text]
*
Use this to select and compare the response body as text, using string compare.
Example¶
in
¶
Type : List[string]
Condition is in this list (exact match)
is
¶
Type : string
Condition is this exact string
is_not
¶
Type : string
Condition is not this exact string
regex
¶
Type : string
Condition is matched on this regex with fullmatch
ResponseDurationDetector¶
gt
¶
Type : integer
Condition is greater than this integer
if
¶
Type : Const[response.duration_ms]
*
Use this to compare the duration of the request in milliseconds.
Example¶
in
¶
Type : List[integer]
Condition is in this list of integers (exact match)
is
¶
Type : integer
Condition is this exact integer
is_not
¶
Type : integer
Condition is not this exact integer
lt
¶
Type : integer
Condition is less than this integer
ResponseHeadersDetector¶
if
¶
Type : Const[response.headers]
*
Use that to select and compare the response headers in a key value dictionary.
Example¶
key
¶
Type : StringMatcher
Key to match
value
¶
Type : StringMatcher
Value to match
ResponseObjectDetector¶
if
¶
Type : Const[response.object]
*
Use this to select and compare the detected object scalars (including custom scalars) in the response, with their kind, name and value.
Example¶
name
¶
Type : StringMatcher
Object scalar name to match
type
¶
Type : ObjectTypeMatcher
Object scalar type to match
value
¶
Type : StringMatcher
Object scalar value to match
ResponseStatusCodeDetector¶
gt
¶
Type : integer
Condition is greater than this integer
if
¶
Type : Const[response.status_code]
*
Use this to compare the HTTP status code as an integer.
Example¶
in
¶
Type : List[integer]
Condition is in this list of integers (exact match)
is
¶
Type : integer
Condition is this exact integer
is_not
¶
Type : integer
Condition is not this exact integer
lt
¶
Type : integer
Condition is less than this integer
ScanTypeDetector¶
if
¶
Type : Const[scan.type]
*
Use this to select against the type of the scan.
Example¶
in
¶
Type : List[CustomRuleScanType]
The scan type is in this list
is
¶
Type : CustomRuleScanType
The scan type is exactly this
is_not
¶
Type : CustomRuleScanType
The scan type is not this type
SchemaNeedAuthenticationDetector¶
if
¶
Type : Const[schema.need_authentication]
*
Use this to select whether or not the schema requires authentication.
Example¶
is
¶
Type : boolean
Condition is true
is_not
¶
Type : boolean
Condition is false
SchemaPathRefDetector¶
contains
¶
Type : string
Contains this string
if
¶
Type : Const[schema.path_ref]
*
Use this to string compare the operation name in GraphQL or the path in REST.
Example¶
in
¶
Type : List[string]
Condition is in this list (exact match)
is
¶
Type : string
Condition is this exact string
is_not
¶
Type : string
Condition is not this exact string
regex
¶
Type : string
Condition is matched on this regex with fullmatch
SchemaUrlDetector¶
contains
¶
Type : string
Contains this string
if
¶
Type : Const[schema.url]
*
Use this to string compare the URL of the request.
Example¶
in
¶
Type : List[string]
Condition is in this list (exact match)
is
¶
Type : string
Condition is this exact string
is_not
¶
Type : string
Condition is not this exact string
regex
¶
Type : string
Condition is matched on this regex with fullmatch
StringMatcher¶
contains
¶
Type : string
Contains this string
in
¶
Type : List[string]
Condition is in this list (exact match)
is
¶
Type : string
Condition is this exact string
is_not
¶
Type : string
Condition is not this exact string
regex
¶
Type : string
Condition is matched on this regex with fullmatch
TenantIsolationRule¶
detect
¶
Type : List[APILogicalAndDetector|APILogicalNotDetector|APILogicalOrDetector|FingerprintCountDetector|FingerprintsSameDetector|HelpersRequestCrudDetector|HelpersResponseIsSuccessfulDetector|JSONMatchesAllDetector|JSONMatchesCountDetector|RegexMatchesAllDetector|RegexMatchesCountDetector|RequestBodyJSONDetector|RequestBodyTextDetector|RequestHeadersDetector|RequestIsAuthenticatedDetector|RequestMethodDetector|RequestObjectDetector|RequestUserDetector|ResponseBodyJSONDetector|ResponseBodyTextDetector|ResponseDurationDetector|ResponseHeadersDetector|ResponseObjectDetector|ResponseStatusCodeDetector|ScanTypeDetector|SchemaNeedAuthenticationDetector|SchemaPathRefDetector|SchemaUrlDetector]
*
The detectors to trigger the alert when analyzing the responses between the main user and other users.
Enums¶
CustomRuleCrud¶
Value |
---|
CREATE |
READ |
UPDATE |
DELETE |
CustomRuleScanType¶
Value |
---|
GRAPHQL |
REST |
HTTPMethod¶
Value |
---|
CONNECT |
DELETE |
GET |
HEAD |
OPTIONS |
PATCH |
POST |
PUT |
TRACE |
OBJECT_TYPE¶
Value |
---|
adobe_client_id |
adobe_client_secret |
age_secret_key |
algolia_api_key |
alibaba_access_key_id |
alibaba_secret_key |
amount |
application |
area_code |
asana_client_id |
asana_client_secret |
atlassian_api_token |
authentication |
author |
authorization_code |
aws_access_token |
aws_mws_id |
bank |
bank_account |
bank_card |
base64 |
bcrypt |
beamer_api_token |
bearer |
bearer_uuid |
bitbucket_client_id |
bitbucket_client_secret |
bitcoin |
body_type |
boolean |
boolean_wannabe |
building |
card_type |
category |
city |
clojars_api_token |
command |
commit_hash |
confirmation_code |
content_type |
contentful_delivery_api_token |
country |
country_code |
county |
coupon_code |
cuid |
currency_code |
cvv |
dash |
databricks_api_token |
datadog_access_token |
date |
datetime |
delivery_method |
device_name |
device_type |
did |
digitalocean_pat |
directory |
discount |
document_type |
doppler_api_token |
driving_license |
dropbox_api_token |
dropbox_long_lived_api_token |
dropbox_short_lived_api_token |
duffel_api_token |
duration |
dynatrace_api_token |
e_commerce_indicator |
easypost_api_token |
easypost_test_api_token |
email |
environment |
ethereum |
etsy_access_token |
event_type |
facebook |
fastly_api_token |
fee |
file |
finicity_api_token |
finicity_client_secret |
flickr_access_token |
float |
flutterwave_encryption_key |
flutterwave_secret_key |
form |
frameio_api_token |
french_phone |
func |
gcp_api_key |
gender |
geocodio |
github_app_token |
github_fine_grained_pat |
github_oauth |
github_pat |
github_refresh_token |
gitlab_pat |
gitlab_rrt |
grafana_api_key |
grafana_service_account_token |
graphcms |
hash |
hashicorp_tf_api_token |
health_insurance_number |
heroku_api_key |
hex_color_code |
hexadecimal |
host |
house_number |
hsl |
hsla |
html_body |
http_method |
hubspot_api_key |
huggingface_token |
id |
identity_number |
injection |
instagram_oauth |
integer |
intercom_api_key |
ipc_patent |
ipstack_token |
ipv4 |
ipv6 |
isbn |
item |
jfrog_api_key |
jiratoken |
join |
json |
jwt |
language_iso_639_1 |
language_iso_639_2 |
latitude |
launchdarkly_access_token |
legal_name |
limit |
linear_api_key |
linear_client_secret |
linkedin_client_secret |
llm_input |
lob_api_key |
locale |
location |
longitude |
mac |
mailchimp_api_key |
mailgun_private_api_token |
mailgun_signing_key |
mask |
md5 |
medical_record_number |
merchant |
messagebird_api_token |
microsoft_teams_webhook |
monero |
mongo_db_object_id |
month |
navigation |
netlify_access_token |
new_relic_user_api_id |
npm_access_token |
offset |
okta_access_token |
openai_api_key |
pagination |
pagination_limit |
pagination_wannabe |
passport |
password |
paypaloauth |
permission |
phone |
pin_code |
plan |
planetscale_api_token |
planetscale_password |
policy |
port |
postman_api_token |
prescription_number |
price |
private_key |
protocol |
pubnubpublishkey |
pulumi_api_token |
pypi_upload_token |
reason_code |
reference |
region |
return_type |
rgb |
rgba |
role |
rubygems_api_token |
search |
secret |
sendgrid_api_token |
sendinblue_api_token |
sentry_access_token |
serial_number |
sha1 |
sha256 |
shipping_method |
shippo_api_token |
shopify_access_token |
shopify_custom_access_token |
shopify_private_app_access_token |
shopify_shared_secret |
sidekiq_secret |
slack_bot_token |
slack_legacy_workspace_token |
slack_user_token |
slack_webhook_url |
slug |
social_security_number |
software_component |
ssh_url |
status |
status_code |
status_message |
street_address |
string |
stripe_access_token |
stripe_public_access_token |
sumologic_access_token |
thinkific |
time |
timestamp |
title |
twilio_api_key |
twitch_api_token |
twitter_api_key |
typeform_api_token |
unsanitized_payload |
upload |
uri |
url |
us_bank_account_number |
us_bank_routing_number |
us_zip_code |
user_agent |
username |
uuid |
vault_service_token |
vehicle_type |
version |
view |
year |
youtubeapikey |
zendesk_secret_key |
zip_code |