{"data":{"skill":{"slug":"blockrunai-nano-banana-blockrun","name":"nano-banana-blockrun","icon":"📦","repo":"https://github.com/BlockRunAI/nano-banana-blockrun/tree/main/","status":"approved","author":"BlockRunAI","authorVersion":null,"skillstoreRevision":null},"audit":{"id":"c9b0a066-2123-402c-9fa0-0e31be6bfe29","skill_id":"e42aa2bf-8b9d-4ce5-bff9-a88bbb17ea37","version":7,"content_hash":"82b65475af4cface41a8532ea7bdb72e","risk_level":"high","is_blocked":false,"safe_to_publish":false,"analysis_status":"ok","agent_auto_install_policy":"confirmation_required","manual_install_policy":"allowed","summary":"Confirmed findings center on wallet private-key handling, broad .env loading, external paid network generation, and local image file writes. Markdown code-fence findings were false positives, and the bundled PNG was manually reviewed as benign. No prompt injection attempt was found in the reviewed text files.","remediation":[{"issue":"Wallet private-key handling","severity":"high","suggestion":"Avoid storing raw private keys in .env files. Prefer a low-balance dedicated wallet, hardware-backed signing, or an explicit external wallet approval flow."},{"issue":"Broad .env loading order","severity":"high","suggestion":"Load configuration only from an explicit trusted path. Do not load .env from the output directory or arbitrary current working directories."},{"issue":"Unpinned SDK installation","severity":"medium","suggestion":"Pin blockrun-llm to a reviewed version and document package integrity checks before users run payment-related code."},{"issue":"Paid request transparency","severity":"medium","suggestion":"Show the model, estimated price, destination service, and wallet address before each generation. Require explicit user confirmation for paid calls."},{"issue":"Generated file output","severity":"medium","suggestion":"Validate the output directory, avoid overwriting existing files, and report the exact saved path after generation."}],"risk_factor_evidence":[{"factor":"network","evidence":[{"file":"LICENSE","line_end":3,"line_start":3},{"file":"LICENSE","line_end":184,"line_start":184},{"file":"README.md","line_end":20,"line_start":20},{"file":"SKILL.md","line_end":20,"line_start":20},{"file":"SKILL.md","line_end":67,"line_start":67},{"file":"SKILL.md","line_end":84,"line_start":84},{"file":"SKILL.md","line_end":85,"line_start":85},{"file":"SKILL.md","line_end":86,"line_start":86}]},{"factor":"filesystem","evidence":[{"file":"README.md","line_end":20,"line_start":20},{"file":"README.md","line_end":20,"line_start":20},{"file":"scripts/generate.py","line_end":61,"line_start":61}]},{"factor":"env_access","evidence":[{"file":"scripts/generate.py","line_end":21,"line_start":21},{"file":"scripts/generate.py","line_end":29,"line_start":29},{"file":"scripts/generate.py","line_end":8,"line_start":8},{"file":"scripts/generate.py","line_end":24,"line_start":24},{"file":"scripts/generate.py","line_end":25,"line_start":25},{"file":"scripts/generate.py","line_end":26,"line_start":26},{"file":"scripts/generate.py","line_end":8,"line_start":8},{"file":"scripts/generate.py","line_end":24,"line_start":24},{"file":"scripts/generate.py","line_end":25,"line_start":25},{"file":"scripts/generate.py","line_end":26,"line_start":26},{"file":"scripts/generate.py","line_end":48,"line_start":48}]},{"factor":"external_commands","evidence":[{"file":"SKILL.md","line_end":17,"line_start":15},{"file":"SKILL.md","line_end":25,"line_start":17},{"file":"SKILL.md","line_end":31,"line_start":25},{"file":"SKILL.md","line_end":38,"line_start":31},{"file":"SKILL.md","line_end":44,"line_start":38},{"file":"SKILL.md","line_end":47,"line_start":44},{"file":"SKILL.md","line_end":55,"line_start":47},{"file":"SKILL.md","line_end":61,"line_start":55},{"file":"SKILL.md","line_end":62,"line_start":61},{"file":"SKILL.md","line_end":63,"line_start":62}]}],"critical_findings":[],"high_findings":[{"title":"Environment file access","locations":[{"file":"README.md","line_end":38,"line_start":38}],"confidence":0.88,"description":"cp .env.example .env","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The README instructs users to create a .env file for configuration. The surrounding setup stores a wallet private key there, making this sensitive secret handling."},{"title":"Environment file access","locations":[{"file":"README.md","line_end":40,"line_start":40}],"confidence":0.95,"description":"# Edit .env and add your private key","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The README explicitly tells users to add a private key to .env. Plaintext environment files containing wallet keys are high-risk secret storage."},{"title":"Crypto seed/private key mention","locations":[{"file":"README.md","line_end":40,"line_start":40}],"confidence":0.96,"description":"# Edit .env and add your private key","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The setup explicitly asks for a wallet private key. Handling a crypto private key creates direct financial-loss risk if mishandled."},{"title":"Crypto seed/private key mention","locations":[{"file":"README.md","line_end":102,"line_start":102}],"confidence":0.82,"description":"**Your private key NEVER leaves your machine.**","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"This line acknowledges that the skill handles a wallet private key. Even with local signing claims, the key remains highly sensitive material used by the skill."},{"title":"Python environment access","locations":[{"file":"scripts/generate.py","line_end":29,"line_start":29}],"confidence":0.94,"description":"key = os.environ.get(\"BLOCKRUN_WALLET_KEY\")","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The script reads BLOCKRUN_WALLET_KEY from the environment. That variable is a crypto wallet private key used for paid requests."},{"title":"dotenv library","locations":[{"file":"scripts/generate.py","line_end":24,"line_start":24}],"confidence":0.9,"description":"load_dotenv(os.path.join(output_dir, '.env'))","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The script loads a .env file from output_dir before reading the wallet key. Because output_dir can be user-selected, this is sensitive configuration loading."},{"title":"dotenv library","locations":[{"file":"scripts/generate.py","line_end":25,"line_start":25}],"confidence":0.88,"description":"load_dotenv(os.path.join(os.getcwd(), '.env'))","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The script loads .env from the current working directory before reading the wallet key. This can pick up plaintext secrets from local project files."},{"title":"dotenv library","locations":[{"file":"scripts/generate.py","line_end":26,"line_start":26}],"confidence":0.86,"description":"load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The script loads .env from the skill directory before reading the wallet key. Storing wallet credentials beside the skill creates sensitive local secret exposure."},{"title":"Python dotenv loader","locations":[{"file":"scripts/generate.py","line_end":24,"line_start":24}],"confidence":0.9,"description":"load_dotenv(os.path.join(output_dir, '.env'))","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The loader reads .env from output_dir, which is controlled by an argument or environment variable. This can alter which wallet key the paid request uses."},{"title":"Python dotenv loader","locations":[{"file":"scripts/generate.py","line_end":25,"line_start":25}],"confidence":0.88,"description":"load_dotenv(os.path.join(os.getcwd(), '.env'))","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The loader reads a .env file from the current working directory. In this skill, those environment values include a wallet private key."},{"title":"Python dotenv loader","locations":[{"file":"scripts/generate.py","line_end":26,"line_start":26}],"confidence":0.86,"description":"load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The loader reads a .env file from the skill directory. This is sensitive because the documented .env value is a crypto wallet private key."},{"title":"Generic API/secret keys","locations":[{"file":"scripts/generate.py","line_end":48,"line_start":48}],"confidence":0.93,"description":"client = ImageClient(private_key=key)","review_kind":"capability","source_category":"env_access","source_severity":"high","confidence_reasoning":"The wallet key is passed into ImageClient as private_key. The skill relies on a third-party SDK to handle sensitive signing material for payments."},{"title":"Environment file access","locations":[{"file":"scripts/generate.py","line_end":12,"line_start":12}],"confidence":0.76,"description":"# Parse arguments first to get output_dir for .env loading","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The comment describes using output_dir for .env loading, which is implemented later in the script. In context, that .env can provide the wallet private key."},{"title":"Environment file access","locations":[{"file":"scripts/generate.py","line_end":23,"line_start":23}],"confidence":0.84,"description":"# Load .env files: output_dir first, then cwd, then skill directory as fallback","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The comment documents multiple .env lookup locations before wallet-key use. This confirms sensitive environment-file handling is intentional behavior."},{"title":"Environment file access","locations":[{"file":"scripts/generate.py","line_end":24,"line_start":24}],"confidence":0.92,"description":"load_dotenv(os.path.join(output_dir, '.env'))","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The script loads .env from output_dir before checking BLOCKRUN_WALLET_KEY. A user-selected directory controlling secret loading is a high-risk configuration pattern."},{"title":"Environment file access","locations":[{"file":"scripts/generate.py","line_end":25,"line_start":25}],"confidence":0.9,"description":"load_dotenv(os.path.join(os.getcwd(), '.env'))","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The script loads .env from the current working directory before reading the wallet key. This can unintentionally consume plaintext secrets from a project directory."},{"title":"Environment file access","locations":[{"file":"scripts/generate.py","line_end":26,"line_start":26}],"confidence":0.88,"description":"load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The script loads .env from the skill directory as a fallback. This encourages storing wallet credentials in the installed skill tree."},{"title":"Environment file access","locations":[{"file":"SKILL.md","line_end":26,"line_start":26}],"confidence":0.94,"description":"# Copy .env.example to .env and add your key","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The setup tells users to put their wallet key into a .env file. This is sensitive plaintext secret storage."},{"title":"Environment file access","locations":[{"file":"SKILL.md","line_end":27,"line_start":27}],"confidence":0.88,"description":"cp .env.example .env","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The setup copies an example environment file into .env for wallet configuration. The documented purpose is storing a private key."},{"title":"Crypto seed/private key mention","locations":[{"file":"SKILL.md","line_end":24,"line_start":24}],"confidence":0.96,"description":"3. Set your wallet private key:","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"The skill explicitly requires a wallet private key. Crypto private-key handling creates direct asset-loss risk if storage or signing is compromised."},{"title":"Crypto seed/private key mention","locations":[{"file":"SKILL.md","line_end":77,"line_start":77}],"confidence":0.82,"description":"**Your private key NEVER leaves your machine.**","review_kind":"security","source_category":"sensitive","source_severity":"high","confidence_reasoning":"This line acknowledges private-key handling by the skill. The local-signing claim does not remove the sensitivity of loading and using the key."},{"title":"User-Selected Directory Controls Wallet Configuration","locations":[{"file":"scripts/generate.py","line_end":29,"line_start":21}],"confidence":0.82,"description":"The script lets output_dir come from arguments or OUTPUT_DIR, then loads .env from that directory before reading BLOCKRUN_WALLET_KEY. A prompt or wrapper that changes the output path could change which wallet key is used.","review_kind":"security","source_category":"semantic","source_severity":"high","confidence_reasoning":"The code path is direct: output_dir is selected on line 21, .env is loaded from it on line 24, and the wallet key is read on line 29."}],"medium_findings":[{"title":"Python file write/append","locations":[{"file":"scripts/generate.py","line_end":61,"line_start":61}],"confidence":0.78,"description":"with open(filename, 'wb') as f:","review_kind":"capability","source_category":"filesystem","source_severity":"medium","confidence_reasoning":"The script writes generated PNG bytes to output_dir, which can come from an argument or environment variable. This intended behavior is still a filesystem side effect with overwrite and path-placement risk."},{"title":"Python environment access","locations":[{"file":"scripts/generate.py","line_end":21,"line_start":21}],"confidence":0.78,"description":"output_dir = sys.argv[3] if len(sys.argv) > 3 else os.environ.get(\"OUTPUT_DIR\", os.getcwd())","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The script reads OUTPUT_DIR from the environment, and that value controls both file output and the first .env lookup path. This can affect where secrets are loaded from."},{"title":"Hardcoded URL","locations":[{"file":"SKILL.md","line_end":67,"line_start":67}],"confidence":0.9,"description":"1. Your request goes to BlockRun API (https://blockrun.ai)","review_kind":"capability","source_category":"network","source_severity":"low","confidence_reasoning":"The documentation states requests go to the BlockRun API. That confirms external network transfer for prompts and payment-related signatures."},{"title":"Unpinned Package Installation From PyPI","locations":[{"file":"SKILL.md","line_end":17,"line_start":15},{"file":"README.md","line_end":28,"line_start":25}],"confidence":0.86,"description":"Setup instructions install blockrun-llm without a pinned version or hash. This increases supply-chain risk because the package handles wallet signing material.","review_kind":"security","source_category":"semantic","source_severity":"medium","confidence_reasoning":"Both setup sections show an unversioned pip install command, and the runtime passes a private key into the installed SDK."},{"title":"Paid Generation Can Spend User Funds Without Script Confirmation","locations":[{"file":"SKILL.md","line_end":55,"line_start":46},{"file":"scripts/generate.py","line_end":52,"line_start":51}],"confidence":0.8,"description":"The skill presents image generation as an automatic action and the script calls client.generate directly. It does not show a local confirmation step with model price before making a paid request.","review_kind":"security","source_category":"semantic","source_severity":"medium","confidence_reasoning":"The documentation describes pay-per-request usage and the script invokes generation after printing the selected model, with no explicit confirmation gate."}],"low_findings":[{"title":"dotenv library","locations":[{"file":"scripts/generate.py","line_end":8,"line_start":8}],"confidence":0.74,"description":"from dotenv import load_dotenv","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The dotenv import enables the later .env loading in this script. In context, that loading is used to configure a wallet private key."},{"title":"Python dotenv loader","locations":[{"file":"scripts/generate.py","line_end":8,"line_start":8}],"confidence":0.74,"description":"from dotenv import load_dotenv","review_kind":"capability","source_category":"env_access","source_severity":"low","confidence_reasoning":"The python-dotenv loader is imported and used in the same script. Its purpose here is to load wallet-related environment configuration."},{"title":"Hardcoded URL","locations":[{"file":"SKILL.md","line_end":20,"line_start":20}],"confidence":0.72,"description":"- Bridge from Ethereum: https://bridge.base.org","review_kind":"capability","source_category":"network","source_severity":"low","confidence_reasoning":"The setup directs users to an external bridge service to obtain funds. This is a user-facing financial network dependency, though it is not hidden behavior."},{"title":"Unscanned file (binary) — manual review required","locations":[{"file":"example_image.png","line_end":1,"line_start":1}],"confidence":1,"description":"[unscanned: binary]","review_kind":"false_positive","source_category":"metadata","source_severity":"low","confidence_reasoning":"Force-confirmed metadata/low static finding; AI dismissal overridden."}],"dangerous_patterns":[],"files_scanned":4,"total_lines":471,"audit_model":"codex","audited_at":"2026-07-06T05:22:44.89+00:00","created_at":"2026-07-06T06:51:49.681663+00:00","static_findings":[{"id":"network:LICENSE:3:hardcoded-url","file":"LICENSE","pattern":"Hardcoded URL","snippet":"http://www.apache.org/licenses/","category":"network","line_end":3,"severity":"low","line_start":3},{"id":"network:LICENSE:184:hardcoded-url","file":"LICENSE","pattern":"Hardcoded URL","snippet":"http://www.apache.org/licenses/LICENSE-2.0","category":"network","line_end":184,"severity":"low","line_start":184},{"id":"network:README.md:20:hardcoded-url","file":"README.md","pattern":"Hardcoded URL","snippet":"git clone https://github.com/BlockRunAI/nano-banana-blockrun.git ~/.claude/skills/nano-banana-blockr","category":"network","line_end":20,"severity":"low","line_start":20},{"id":"filesystem:README.md:20:hidden-file-in-home-directory","file":"README.md","pattern":"Hidden file in home directory","snippet":"git clone https://github.com/BlockRunAI/nano-banana-blockrun.git ~/.claude/skills/nano-banana-blockr","category":"filesystem","line_end":20,"severity":"high","line_start":20},{"id":"filesystem:README.md:20:hidden-file-access","file":"README.md","pattern":"Hidden file access","snippet":"git clone https://github.com/BlockRunAI/nano-banana-blockrun.git ~/.claude/skills/nano-banana-blockr","category":"filesystem","line_end":20,"severity":"medium","line_start":20},{"id":"sensitive:README.md:38:environment-file-access","file":"README.md","pattern":"Environment file access","snippet":"cp .env.example .env","category":"sensitive","line_end":38,"severity":"high","line_start":38},{"id":"sensitive:README.md:40:environment-file-access","file":"README.md","pattern":"Environment file access","snippet":"# Edit .env and add your private key","category":"sensitive","line_end":40,"severity":"high","line_start":40},{"id":"sensitive:README.md:40:crypto-seed-private-key-mention","file":"README.md","pattern":"Crypto seed/private key mention","snippet":"# Edit .env and add your private key","category":"sensitive","line_end":40,"severity":"high","line_start":40},{"id":"sensitive:README.md:102:crypto-seed-private-key-mention","file":"README.md","pattern":"Crypto seed/private key mention","snippet":"**Your private key NEVER leaves your machine.**","category":"sensitive","line_end":102,"severity":"high","line_start":102},{"id":"filesystem:scripts/generate.py:61:python-file-write-append","file":"scripts/generate.py","pattern":"Python file write/append","snippet":"with open(filename, 'wb') as f:","category":"filesystem","line_end":61,"severity":"medium","line_start":61},{"id":"env_access:scripts/generate.py:21:python-environment-access","file":"scripts/generate.py","pattern":"Python environment access","snippet":"output_dir = sys.argv[3] if len(sys.argv) > 3 else os.environ.get(\"OUTPUT_DIR\", os.getcwd())","category":"env_access","line_end":21,"severity":"low","line_start":21},{"id":"env_access:scripts/generate.py:29:python-environment-access","file":"scripts/generate.py","pattern":"Python environment access","snippet":"key = os.environ.get(\"BLOCKRUN_WALLET_KEY\")","category":"env_access","line_end":29,"severity":"low","line_start":29},{"id":"env_access:scripts/generate.py:8:dotenv-library","file":"scripts/generate.py","pattern":"dotenv library","snippet":"from dotenv import load_dotenv","category":"env_access","line_end":8,"severity":"low","line_start":8},{"id":"env_access:scripts/generate.py:24:dotenv-library","file":"scripts/generate.py","pattern":"dotenv library","snippet":"load_dotenv(os.path.join(output_dir, '.env'))","category":"env_access","line_end":24,"severity":"low","line_start":24},{"id":"env_access:scripts/generate.py:25:dotenv-library","file":"scripts/generate.py","pattern":"dotenv library","snippet":"load_dotenv(os.path.join(os.getcwd(), '.env'))","category":"env_access","line_end":25,"severity":"low","line_start":25},{"id":"env_access:scripts/generate.py:26:dotenv-library","file":"scripts/generate.py","pattern":"dotenv library","snippet":"load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))","category":"env_access","line_end":26,"severity":"low","line_start":26},{"id":"env_access:scripts/generate.py:8:python-dotenv-loader","file":"scripts/generate.py","pattern":"Python dotenv loader","snippet":"from dotenv import load_dotenv","category":"env_access","line_end":8,"severity":"low","line_start":8},{"id":"env_access:scripts/generate.py:24:python-dotenv-loader","file":"scripts/generate.py","pattern":"Python dotenv loader","snippet":"load_dotenv(os.path.join(output_dir, '.env'))","category":"env_access","line_end":24,"severity":"low","line_start":24},{"id":"env_access:scripts/generate.py:25:python-dotenv-loader","file":"scripts/generate.py","pattern":"Python dotenv loader","snippet":"load_dotenv(os.path.join(os.getcwd(), '.env'))","category":"env_access","line_end":25,"severity":"low","line_start":25},{"id":"env_access:scripts/generate.py:26:python-dotenv-loader","file":"scripts/generate.py","pattern":"Python dotenv loader","snippet":"load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))","category":"env_access","line_end":26,"severity":"low","line_start":26},{"id":"env_access:scripts/generate.py:48:generic-api-secret-keys","file":"scripts/generate.py","pattern":"Generic API/secret keys","snippet":"client = ImageClient(private_key=key)","category":"env_access","line_end":48,"severity":"high","line_start":48},{"id":"obfuscation:scripts/generate.py:62:python-base64-decode","file":"scripts/generate.py","pattern":"Python base64 decode","snippet":"f.write(base64.b64decode(data))","category":"obfuscation","line_end":62,"severity":"medium","line_start":62},{"id":"sensitive:scripts/generate.py:12:environment-file-access","file":"scripts/generate.py","pattern":"Environment file access","snippet":"# Parse arguments first to get output_dir for .env loading","category":"sensitive","line_end":12,"severity":"high","line_start":12},{"id":"sensitive:scripts/generate.py:23:environment-file-access","file":"scripts/generate.py","pattern":"Environment file access","snippet":"# Load .env files: output_dir first, then cwd, then skill directory as fallback","category":"sensitive","line_end":23,"severity":"high","line_start":23},{"id":"sensitive:scripts/generate.py:24:environment-file-access","file":"scripts/generate.py","pattern":"Environment file access","snippet":"load_dotenv(os.path.join(output_dir, '.env'))","category":"sensitive","line_end":24,"severity":"high","line_start":24},{"id":"sensitive:scripts/generate.py:25:environment-file-access","file":"scripts/generate.py","pattern":"Environment file access","snippet":"load_dotenv(os.path.join(os.getcwd(), '.env'))","category":"sensitive","line_end":25,"severity":"high","line_start":25},{"id":"sensitive:scripts/generate.py:26:environment-file-access","file":"scripts/generate.py","pattern":"Environment file access","snippet":"load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))","category":"sensitive","line_end":26,"severity":"high","line_start":26},{"id":"external_commands:SKILL.md:15:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":17,"severity":"medium","line_start":15},{"id":"external_commands:SKILL.md:17:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":25,"severity":"medium","line_start":17},{"id":"external_commands:SKILL.md:25:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":31,"severity":"medium","line_start":25},{"id":"external_commands:SKILL.md:31:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":38,"severity":"medium","line_start":31},{"id":"external_commands:SKILL.md:38:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```python","category":"external_commands","line_end":44,"severity":"medium","line_start":38},{"id":"external_commands:SKILL.md:44:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":47,"severity":"medium","line_start":44},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```bash","category":"external_commands","line_end":55,"severity":"medium","line_start":47},{"id":"external_commands:SKILL.md:55:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"```","category":"external_commands","line_end":61,"severity":"medium","line_start":55},{"id":"external_commands:SKILL.md:61:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `google/nano-banana` | ~$0.05 | 1024x1024 |","category":"external_commands","line_end":62,"severity":"medium","line_start":61},{"id":"external_commands:SKILL.md:62:ruby-shell-backtick-execution","file":"SKILL.md","pattern":"Ruby/shell backtick execution","snippet":"| `google/nano-banana-pro` | ~$0.10 | up to 4K |","category":"external_commands","line_end":63,"severity":"medium","line_start":62},{"id":"network:SKILL.md:20:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"- Bridge from Ethereum: https://bridge.base.org","category":"network","line_end":20,"severity":"low","line_start":20},{"id":"network:SKILL.md:67:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"1. Your request goes to BlockRun API (https://blockrun.ai)","category":"network","line_end":67,"severity":"low","line_start":67},{"id":"network:SKILL.md:84:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"- [BlockRun](https://blockrun.ai)","category":"network","line_end":84,"severity":"low","line_start":84},{"id":"network:SKILL.md:85:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"- [x402 Protocol](https://x402.org)","category":"network","line_end":85,"severity":"low","line_start":85},{"id":"network:SKILL.md:86:hardcoded-url","file":"SKILL.md","pattern":"Hardcoded URL","snippet":"- [PyPI Package](https://pypi.org/project/blockrun-llm/)","category":"network","line_end":86,"severity":"low","line_start":86},{"id":"sensitive:SKILL.md:26:environment-file-access","file":"SKILL.md","pattern":"Environment file access","snippet":"# Copy .env.example to .env and add your key","category":"sensitive","line_end":26,"severity":"high","line_start":26},{"id":"sensitive:SKILL.md:27:environment-file-access","file":"SKILL.md","pattern":"Environment file access","snippet":"cp .env.example .env","category":"sensitive","line_end":27,"severity":"high","line_start":27},{"id":"sensitive:SKILL.md:24:crypto-seed-private-key-mention","file":"SKILL.md","pattern":"Crypto seed/private key mention","snippet":"3. Set your wallet private key:","category":"sensitive","line_end":24,"severity":"high","line_start":24},{"id":"sensitive:SKILL.md:77:crypto-seed-private-key-mention","file":"SKILL.md","pattern":"Crypto seed/private key mention","snippet":"**Your private key NEVER leaves your machine.**","category":"sensitive","line_end":77,"severity":"high","line_start":77},{"id":"metadata:example_image.png:1:unscanned-file-binary-manual-review-required","file":"example_image.png","pattern":"Unscanned file (binary) — manual review required","snippet":"[unscanned: binary]","category":"metadata","line_end":1,"severity":"low","line_start":1}],"finding_verdicts":[{"id":"network:LICENSE:3:hardcoded-url","reason":"This is a standard Apache License reference URL in LICENSE, not runtime network behavior. It does not cause the skill to contact an external service.","verdict":"false_positive","confidence":0.99},{"id":"network:LICENSE:184:hardcoded-url","reason":"This is the standard Apache License URL in license text. It is documentation, not executable network access or data transfer.","verdict":"false_positive","confidence":0.99},{"id":"network:README.md:20:hardcoded-url","reason":"The URL is a GitHub clone target in installation documentation. It is expected source installation guidance, not hidden runtime exfiltration.","verdict":"false_positive","confidence":0.86},{"id":"filesystem:README.md:20:hidden-file-in-home-directory","reason":"The hidden path is the normal Claude skills installation directory under ~/.claude. This is user-directed installation documentation, not covert filesystem access.","verdict":"false_positive","confidence":0.9},{"id":"filesystem:README.md:20:hidden-file-access","reason":"The hidden path appears only in a documented git clone command for installing the skill. It does not show runtime access to hidden user files.","verdict":"false_positive","confidence":0.9},{"id":"sensitive:README.md:38:environment-file-access","reason":"The README instructs users to create a .env file for configuration. The surrounding setup stores a wallet private key there, making this sensitive secret handling.","verdict":"confirmed","severity":"high","confidence":0.88},{"id":"sensitive:README.md:40:environment-file-access","reason":"The README explicitly tells users to add a private key to .env. Plaintext environment files containing wallet keys are high-risk secret storage.","verdict":"confirmed","severity":"high","confidence":0.95},{"id":"sensitive:README.md:40:crypto-seed-private-key-mention","reason":"The setup explicitly asks for a wallet private key. Handling a crypto private key creates direct financial-loss risk if mishandled.","verdict":"confirmed","severity":"high","confidence":0.96},{"id":"sensitive:README.md:102:crypto-seed-private-key-mention","reason":"This line acknowledges that the skill handles a wallet private key. Even with local signing claims, the key remains highly sensitive material used by the skill.","verdict":"confirmed","severity":"high","confidence":0.82},{"id":"filesystem:scripts/generate.py:61:python-file-write-append","reason":"The script writes generated PNG bytes to output_dir, which can come from an argument or environment variable. This intended behavior is still a filesystem side effect with overwrite and path-placement risk.","verdict":"confirmed","severity":"medium","confidence":0.78},{"id":"env_access:scripts/generate.py:21:python-environment-access","reason":"The script reads OUTPUT_DIR from the environment, and that value controls both file output and the first .env lookup path. This can affect where secrets are loaded from.","verdict":"confirmed","severity":"medium","confidence":0.78},{"id":"env_access:scripts/generate.py:29:python-environment-access","reason":"The script reads BLOCKRUN_WALLET_KEY from the environment. That variable is a crypto wallet private key used for paid requests.","verdict":"confirmed","severity":"high","confidence":0.94},{"id":"env_access:scripts/generate.py:8:dotenv-library","reason":"The dotenv import enables the later .env loading in this script. In context, that loading is used to configure a wallet private key.","verdict":"confirmed","severity":"low","confidence":0.74},{"id":"env_access:scripts/generate.py:24:dotenv-library","reason":"The script loads a .env file from output_dir before reading the wallet key. Because output_dir can be user-selected, this is sensitive configuration loading.","verdict":"confirmed","severity":"high","confidence":0.9},{"id":"env_access:scripts/generate.py:25:dotenv-library","reason":"The script loads .env from the current working directory before reading the wallet key. This can pick up plaintext secrets from local project files.","verdict":"confirmed","severity":"high","confidence":0.88},{"id":"env_access:scripts/generate.py:26:dotenv-library","reason":"The script loads .env from the skill directory before reading the wallet key. Storing wallet credentials beside the skill creates sensitive local secret exposure.","verdict":"confirmed","severity":"high","confidence":0.86},{"id":"env_access:scripts/generate.py:8:python-dotenv-loader","reason":"The python-dotenv loader is imported and used in the same script. Its purpose here is to load wallet-related environment configuration.","verdict":"confirmed","severity":"low","confidence":0.74},{"id":"env_access:scripts/generate.py:24:python-dotenv-loader","reason":"The loader reads .env from output_dir, which is controlled by an argument or environment variable. This can alter which wallet key the paid request uses.","verdict":"confirmed","severity":"high","confidence":0.9},{"id":"env_access:scripts/generate.py:25:python-dotenv-loader","reason":"The loader reads a .env file from the current working directory. In this skill, those environment values include a wallet private key.","verdict":"confirmed","severity":"high","confidence":0.88},{"id":"env_access:scripts/generate.py:26:python-dotenv-loader","reason":"The loader reads a .env file from the skill directory. This is sensitive because the documented .env value is a crypto wallet private key.","verdict":"confirmed","severity":"high","confidence":0.86},{"id":"env_access:scripts/generate.py:48:generic-api-secret-keys","reason":"The wallet key is passed into ImageClient as private_key. The skill relies on a third-party SDK to handle sensitive signing material for payments.","verdict":"confirmed","severity":"high","confidence":0.93},{"id":"obfuscation:scripts/generate.py:62:python-base64-decode","reason":"The base64 decode handles returned PNG image data before saving it as a file. There is no evidence that it decodes executable code or hidden commands.","verdict":"false_positive","confidence":0.9},{"id":"sensitive:scripts/generate.py:12:environment-file-access","reason":"The comment describes using output_dir for .env loading, which is implemented later in the script. In context, that .env can provide the wallet private key.","verdict":"confirmed","severity":"high","confidence":0.76},{"id":"sensitive:scripts/generate.py:23:environment-file-access","reason":"The comment documents multiple .env lookup locations before wallet-key use. This confirms sensitive environment-file handling is intentional behavior.","verdict":"confirmed","severity":"high","confidence":0.84},{"id":"sensitive:scripts/generate.py:24:environment-file-access","reason":"The script loads .env from output_dir before checking BLOCKRUN_WALLET_KEY. A user-selected directory controlling secret loading is a high-risk configuration pattern.","verdict":"confirmed","severity":"high","confidence":0.92},{"id":"sensitive:scripts/generate.py:25:environment-file-access","reason":"The script loads .env from the current working directory before reading the wallet key. This can unintentionally consume plaintext secrets from a project directory.","verdict":"confirmed","severity":"high","confidence":0.9},{"id":"sensitive:scripts/generate.py:26:environment-file-access","reason":"The script loads .env from the skill directory as a fallback. This encourages storing wallet credentials in the installed skill tree.","verdict":"confirmed","severity":"high","confidence":0.88},{"id":"external_commands:SKILL.md:15:ruby-shell-backtick-execution","reason":"The scanner matched a Markdown code fence around a bash example. This is documentation syntax, not Ruby backtick shell execution.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:17:ruby-shell-backtick-execution","reason":"The matched text is the closing Markdown fence for an example block. It is not executable shell substitution.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:25:ruby-shell-backtick-execution","reason":"The scanner matched a Markdown bash code fence in setup instructions. The fence itself is not command execution.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:31:ruby-shell-backtick-execution","reason":"The matched text is a Markdown code-fence close. It does not execute commands or evaluate user-controlled input.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:38:ruby-shell-backtick-execution","reason":"The scanner matched a Markdown python code fence. This is a documentation block marker, not shell execution.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:44:ruby-shell-backtick-execution","reason":"The matched text closes a Markdown code block. It is not a Ruby or shell backtick execution primitive.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:47:ruby-shell-backtick-execution","reason":"The scanner matched a Markdown bash fence before example commands. The marker itself is not executable code.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:55:ruby-shell-backtick-execution","reason":"The matched text is a Markdown code-fence close. There is no Ruby backtick execution at this location.","verdict":"false_positive","confidence":0.98},{"id":"external_commands:SKILL.md:61:ruby-shell-backtick-execution","reason":"The backticks here are inline Markdown formatting for a model name in a pricing table. They do not execute commands.","verdict":"false_positive","confidence":0.97},{"id":"external_commands:SKILL.md:62:ruby-shell-backtick-execution","reason":"The backticks here are inline Markdown formatting for a model name. They are not shell execution syntax in code.","verdict":"false_positive","confidence":0.97},{"id":"network:SKILL.md:20:hardcoded-url","reason":"The setup directs users to an external bridge service to obtain funds. This is a user-facing financial network dependency, though it is not hidden behavior.","verdict":"confirmed","severity":"low","confidence":0.72},{"id":"network:SKILL.md:67:hardcoded-url","reason":"The documentation states requests go to the BlockRun API. That confirms external network transfer for prompts and payment-related signatures.","verdict":"confirmed","severity":"medium","confidence":0.9},{"id":"network:SKILL.md:84:hardcoded-url","reason":"This is a documentation link to the provider home page. The line alone does not create runtime network behavior.","verdict":"false_positive","confidence":0.86},{"id":"network:SKILL.md:85:hardcoded-url","reason":"This is a documentation link to the x402 protocol. It is informational and not a runtime call.","verdict":"false_positive","confidence":0.9},{"id":"network:SKILL.md:86:hardcoded-url","reason":"This is a documentation link to the PyPI package page. The unpinned install guidance is addressed separately as a semantic supply-chain risk.","verdict":"false_positive","confidence":0.84},{"id":"sensitive:SKILL.md:26:environment-file-access","reason":"The setup tells users to put their wallet key into a .env file. This is sensitive plaintext secret storage.","verdict":"confirmed","severity":"high","confidence":0.94},{"id":"sensitive:SKILL.md:27:environment-file-access","reason":"The setup copies an example environment file into .env for wallet configuration. The documented purpose is storing a private key.","verdict":"confirmed","severity":"high","confidence":0.88},{"id":"sensitive:SKILL.md:24:crypto-seed-private-key-mention","reason":"The skill explicitly requires a wallet private key. Crypto private-key handling creates direct asset-loss risk if storage or signing is compromised.","verdict":"confirmed","severity":"high","confidence":0.96},{"id":"sensitive:SKILL.md:77:crypto-seed-private-key-mention","reason":"This line acknowledges private-key handling by the skill. The local-signing claim does not remove the sensitivity of loading and using the key.","verdict":"confirmed","severity":"high","confidence":0.82},{"id":"metadata:example_image.png:1:unscanned-file-binary-manual-review-required","reason":"Manual review shows the PNG is a benign generated illustration. No visible prompt injection, credential material, or executable content was found.","verdict":"false_positive","confidence":0.92}],"semantic_findings":[{"title":"User-Selected Directory Controls Wallet Configuration","severity":"high","locations":[{"file":"scripts/generate.py","line_end":29,"line_start":21}],"confidence":0.82,"description":"The script lets output_dir come from arguments or OUTPUT_DIR, then loads .env from that directory before reading BLOCKRUN_WALLET_KEY. A prompt or wrapper that changes the output path could change which wallet key is used.","confidence_reasoning":"The code path is direct: output_dir is selected on line 21, .env is loaded from it on line 24, and the wallet key is read on line 29."},{"title":"Unpinned Package Installation From PyPI","severity":"medium","locations":[{"file":"SKILL.md","line_end":17,"line_start":15},{"file":"README.md","line_end":28,"line_start":25}],"confidence":0.86,"description":"Setup instructions install blockrun-llm without a pinned version or hash. This increases supply-chain risk because the package handles wallet signing material.","confidence_reasoning":"Both setup sections show an unversioned pip install command, and the runtime passes a private key into the installed SDK."},{"title":"Paid Generation Can Spend User Funds Without Script Confirmation","severity":"medium","locations":[{"file":"SKILL.md","line_end":55,"line_start":46},{"file":"scripts/generate.py","line_end":52,"line_start":51}],"confidence":0.8,"description":"The skill presents image generation as an automatic action and the script calls client.generate directly. It does not show a local confirmation step with model price before making a paid request.","confidence_reasoning":"The documentation describes pay-per-request usage and the script invokes generation after printing the selected model, with no explicit confirmation gate."}],"subject_marketplace_commit_sha":null,"subject_content_hash":null,"subject_tree_hash":null,"subject_plugin_path":null,"audit_payload_hash":null,"confirmed_risk_level":null,"scanner_version":null,"policy_version":null,"subject":{"marketplaceCommitSha":null,"contentHash":null,"treeHash":null,"pluginPath":null,"auditPayloadHash":null},"scannerVersion":null,"policyVersion":null},"auditTranslation":null,"localization":{"requestedLocale":"en","contentLocale":"en","availableLocales":["en"],"fallbackToEnglish":false},"attestation":{"availability":"not_attestable","url":null,"status":null,"reason":"confirmed_risk_level does not match the canonical trust resolver"},"trust":{"publicState":"public","auditState":"complete","auditCurrentness":null,"confirmedRiskLevel":"high","confirmedFindingCount":16,"capabilityReviewCount":14,"needsReviewCount":0,"falsePositiveCount":0,"agentAutoInstallPolicy":"confirmation_required","manualInstallPolicy":"allowed","artifactSignatureState":"available","attestationState":"not_attestable","verificationState":"not_verified"},"isLatest":false}}