agent-security-scanner-mcp 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +451 -739
- package/analyzer.py +51 -7
- package/index.js +42 -2697
- package/package.json +7 -6
- package/regex_fallback.py +66 -0
- package/rules/__init__.py +124 -36
- package/rules/generic/secrets/gitleaks/adafruit-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/adobe-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/adobe-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/age-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/airtable-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/algolia-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/alibaba-access-key-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/alibaba-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/asana-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/asana-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/atlassian-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/authress-service-client-access-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/aws-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/beamer-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/bitbucket-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/bitbucket-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/bittrex-access-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/bittrex-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/clojars-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/cloudflare-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/cloudflare-global-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/cloudflare-origin-ca-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/codecov-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/coinbase-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/confluent-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/confluent-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/contentful-delivery-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/databricks-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/datadog-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/defined-networking-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/digitalocean-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/digitalocean-pat.yaml +27 -0
- package/rules/generic/secrets/gitleaks/digitalocean-refresh-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/discord-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/discord-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/discord-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/doppler-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/droneci-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/dropbox-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/dropbox-long-lived-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/dropbox-short-lived-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/duffel-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/dynatrace-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/easypost-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/easypost-test-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/etsy-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/facebook-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/facebook-page-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/facebook-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/facebook.yaml +27 -0
- package/rules/generic/secrets/gitleaks/fastly-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/finicity-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/finicity-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/finnhub-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/flickr-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/flutterwave-encryption-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/flutterwave-public-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/flutterwave-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/frameio-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/freshbooks-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/gcp-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/generic-api-key.yaml +76 -0
- package/rules/generic/secrets/gitleaks/github-app-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/github-fine-grained-pat.yaml +27 -0
- package/rules/generic/secrets/gitleaks/github-oauth.yaml +27 -0
- package/rules/generic/secrets/gitleaks/github-pat.yaml +27 -0
- package/rules/generic/secrets/gitleaks/github-refresh-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/gitlab-pat.yaml +27 -0
- package/rules/generic/secrets/gitleaks/gitlab-ptt.yaml +27 -0
- package/rules/generic/secrets/gitleaks/gitlab-rrt.yaml +27 -0
- package/rules/generic/secrets/gitleaks/gitter-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/gocardless-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/grafana-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/grafana-cloud-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/grafana-service-account-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/harness-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/hashicorp-tf-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/hashicorp-tf-password.yaml +31 -0
- package/rules/generic/secrets/gitleaks/heroku-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/hubspot-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/huggingface-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/huggingface-organization-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/infracost-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/intercom-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/intra42-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/jfrog-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/jfrog-identity-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/jwt-base64.yaml +27 -0
- package/rules/generic/secrets/gitleaks/jwt.yaml +27 -0
- package/rules/generic/secrets/gitleaks/kraken-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/kucoin-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/kucoin-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/launchdarkly-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/linear-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/linear-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/linkedin-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/linkedin-client-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/lob-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/lob-pub-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/mailchimp-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/mailgun-private-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/mailgun-pub-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/mailgun-signing-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/mapbox-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/mattermost-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/messagebird-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/messagebird-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/microsoft-teams-webhook.yaml +27 -0
- package/rules/generic/secrets/gitleaks/netlify-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/new-relic-browser-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/new-relic-insert-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/new-relic-user-api-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/new-relic-user-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/npm-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/nytimes-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/okta-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/openai-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/plaid-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/plaid-client-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/plaid-secret-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/planetscale-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/planetscale-oauth-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/planetscale-password.yaml +27 -0
- package/rules/generic/secrets/gitleaks/postman-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/prefect-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/private-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/pulumi-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/pypi-upload-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/rapidapi-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/readme-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/rubygems-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/scalingo-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sendbird-access-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sendbird-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sendgrid-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sendinblue-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sentry-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/shippo-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/shopify-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/shopify-custom-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/shopify-private-app-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/shopify-shared-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sidekiq-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sidekiq-sensitive-url.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-app-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-bot-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-config-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-config-refresh-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-legacy-bot-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-legacy-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-legacy-workspace-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-user-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/slack-webhook-url.yaml +27 -0
- package/rules/generic/secrets/gitleaks/snyk-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/square-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/squarespace-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/stripe-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sumologic-access-id.yaml +27 -0
- package/rules/generic/secrets/gitleaks/sumologic-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/telegram-bot-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/travisci-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twilio-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twitch-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twitter-access-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twitter-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twitter-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twitter-api-secret.yaml +27 -0
- package/rules/generic/secrets/gitleaks/twitter-bearer-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/typeform-api-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/vault-batch-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/vault-service-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/yandex-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/yandex-api-key.yaml +27 -0
- package/rules/generic/secrets/gitleaks/yandex-aws-access-token.yaml +27 -0
- package/rules/generic/secrets/gitleaks/zendesk-secret-key.yaml +27 -0
- package/rules/generic/secrets/security/detected-amazon-mws-auth-token.yaml +26 -0
- package/rules/generic/secrets/security/detected-artifactory-password.yaml +47 -0
- package/rules/generic/secrets/security/detected-artifactory-token.yaml +44 -0
- package/rules/generic/secrets/security/detected-aws-access-key-id-value.yaml +29 -0
- package/rules/generic/secrets/security/detected-aws-account-id.yaml +58 -0
- package/rules/generic/secrets/security/detected-aws-appsync-graphql-key.yaml +27 -0
- package/rules/generic/secrets/security/detected-aws-secret-access-key.yaml +30 -0
- package/rules/generic/secrets/security/detected-aws-session-token.yaml +31 -0
- package/rules/generic/secrets/security/detected-bcrypt-hash.yaml +25 -0
- package/rules/generic/secrets/security/detected-codeclimate.yaml +27 -0
- package/rules/generic/secrets/security/detected-etc-shadow.yaml +27 -0
- package/rules/generic/secrets/security/detected-facebook-access-token.yaml +29 -0
- package/rules/generic/secrets/security/detected-facebook-oauth.yaml +27 -0
- package/rules/generic/secrets/security/detected-generic-api-key.yaml +29 -0
- package/rules/generic/secrets/security/detected-generic-secret.yaml +30 -0
- package/rules/generic/secrets/security/detected-github-token.yaml +47 -0
- package/rules/generic/secrets/security/detected-google-api-key.yaml +29 -0
- package/rules/generic/secrets/security/detected-google-cloud-api-key.yaml +27 -0
- package/rules/generic/secrets/security/detected-google-gcm-service-account.yaml +27 -0
- package/rules/generic/secrets/security/detected-google-oauth-access-token.yaml +26 -0
- package/rules/generic/secrets/security/detected-google-oauth.yaml +26 -0
- package/rules/generic/secrets/security/detected-heroku-api-key.yaml +27 -0
- package/rules/generic/secrets/security/detected-hockeyapp.yaml +27 -0
- package/rules/generic/secrets/security/detected-jwt-token.yaml +25 -0
- package/rules/generic/secrets/security/detected-kolide-api-key.yaml +25 -0
- package/rules/generic/secrets/security/detected-mailchimp-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-mailgun-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-npm-registry-auth-token.yaml +33 -0
- package/rules/generic/secrets/security/detected-onfido-live-api-token.yaml +20 -0
- package/rules/generic/secrets/security/detected-outlook-team.yaml +27 -0
- package/rules/generic/secrets/security/detected-paypal-braintree-access-token.yaml +27 -0
- package/rules/generic/secrets/security/detected-pgp-private-key-block.yaml +28 -0
- package/rules/generic/secrets/security/detected-picatic-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-private-key.yaml +39 -0
- package/rules/generic/secrets/security/detected-sauce-token.yaml +27 -0
- package/rules/generic/secrets/security/detected-sendgrid-api-key.yaml +27 -0
- package/rules/generic/secrets/security/detected-slack-token.yaml +28 -0
- package/rules/generic/secrets/security/detected-slack-webhook.yaml +27 -0
- package/rules/generic/secrets/security/detected-snyk-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-softlayer-api-key.yaml +27 -0
- package/rules/generic/secrets/security/detected-sonarqube-docs-api-key.yaml +40 -0
- package/rules/generic/secrets/security/detected-square-access-token.yaml +26 -0
- package/rules/generic/secrets/security/detected-square-oauth-secret.yaml +27 -0
- package/rules/generic/secrets/security/detected-ssh-password.yaml +27 -0
- package/rules/generic/secrets/security/detected-stripe-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-stripe-restricted-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-telegram-bot-api-key.yaml +30 -0
- package/rules/generic/secrets/security/detected-twilio-api-key.yaml +26 -0
- package/rules/generic/secrets/security/detected-username-and-password-in-uri.yaml +35 -0
- package/rules/generic/secrets/security/google-maps-apikeyleak.yaml +25 -0
- package/rules/prompt-injection.security.yaml +4 -0
- package/rules/python/flask/security/injection/flask-injection-sinks.yaml +352 -0
- package/src/analyzer.py +119 -0
- package/src/cli/demo.js +238 -0
- package/src/cli/doctor.js +273 -0
- package/src/cli/init.js +288 -0
- package/src/fix-patterns.js +698 -0
- package/src/tools/check-package.js +169 -0
- package/src/tools/fix-security.js +115 -0
- package/src/tools/scan-packages.js +154 -0
- package/src/tools/scan-prompt.js +570 -0
- package/src/tools/scan-security.js +117 -0
- package/src/utils.js +153 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
# Flask SQL Injection - Direct Execution
|
|
3
|
+
- id: flask-sql-injection-execute
|
|
4
|
+
message: >-
|
|
5
|
+
SQL Injection vulnerability detected. User input from Flask request flows
|
|
6
|
+
directly to database query execution. An attacker could manipulate the query
|
|
7
|
+
to access, modify, or delete data. Use parameterized queries instead:
|
|
8
|
+
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
|
|
9
|
+
metadata:
|
|
10
|
+
cwe:
|
|
11
|
+
- 'CWE-89: SQL Injection'
|
|
12
|
+
owasp:
|
|
13
|
+
- A03:2021 - Injection
|
|
14
|
+
- A05:2025 - Injection
|
|
15
|
+
category: security
|
|
16
|
+
technology:
|
|
17
|
+
- flask
|
|
18
|
+
- sqlite3
|
|
19
|
+
- mysql
|
|
20
|
+
- postgresql
|
|
21
|
+
subcategory:
|
|
22
|
+
- vuln
|
|
23
|
+
impact: HIGH
|
|
24
|
+
likelihood: HIGH
|
|
25
|
+
confidence: HIGH
|
|
26
|
+
fix: |
|
|
27
|
+
Use parameterized queries:
|
|
28
|
+
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
|
|
29
|
+
severity: ERROR
|
|
30
|
+
languages:
|
|
31
|
+
- python
|
|
32
|
+
mode: taint
|
|
33
|
+
pattern-sources:
|
|
34
|
+
- patterns:
|
|
35
|
+
- pattern-either:
|
|
36
|
+
- pattern: request.args.get(...)
|
|
37
|
+
- pattern: request.args[...]
|
|
38
|
+
- pattern: request.form.get(...)
|
|
39
|
+
- pattern: request.form[...]
|
|
40
|
+
- pattern: request.json
|
|
41
|
+
- pattern: request.json.get(...)
|
|
42
|
+
- pattern: request.json[...]
|
|
43
|
+
- pattern: request.data
|
|
44
|
+
- pattern: request.values.get(...)
|
|
45
|
+
- pattern: request.values[...]
|
|
46
|
+
- pattern: request.cookies.get(...)
|
|
47
|
+
- pattern: request.headers.get(...)
|
|
48
|
+
- pattern: flask.request.args.get(...)
|
|
49
|
+
- pattern: flask.request.form[...]
|
|
50
|
+
- pattern: flask.request.json
|
|
51
|
+
pattern-sinks:
|
|
52
|
+
- patterns:
|
|
53
|
+
- pattern-either:
|
|
54
|
+
- pattern: $CURSOR.execute(...)
|
|
55
|
+
- pattern: $CURSOR.executemany(...)
|
|
56
|
+
- pattern: $CONN.execute(...)
|
|
57
|
+
- pattern: $DB.execute(...)
|
|
58
|
+
- pattern: $SESSION.execute(...)
|
|
59
|
+
pattern-sanitizers:
|
|
60
|
+
- patterns:
|
|
61
|
+
- pattern-either:
|
|
62
|
+
- pattern: $CURSOR.execute($QUERY, (...))
|
|
63
|
+
- pattern: $CURSOR.execute($QUERY, [...])
|
|
64
|
+
|
|
65
|
+
# Flask Command Injection - os.system
|
|
66
|
+
- id: flask-command-injection-system
|
|
67
|
+
message: >-
|
|
68
|
+
Command Injection vulnerability detected. User input from Flask request flows
|
|
69
|
+
to os.system() which executes shell commands. An attacker could execute arbitrary
|
|
70
|
+
system commands. Use subprocess with shell=False and argument lists instead.
|
|
71
|
+
metadata:
|
|
72
|
+
cwe:
|
|
73
|
+
- 'CWE-78: OS Command Injection'
|
|
74
|
+
owasp:
|
|
75
|
+
- A03:2021 - Injection
|
|
76
|
+
- A05:2025 - Injection
|
|
77
|
+
category: security
|
|
78
|
+
technology:
|
|
79
|
+
- flask
|
|
80
|
+
subcategory:
|
|
81
|
+
- vuln
|
|
82
|
+
impact: CRITICAL
|
|
83
|
+
likelihood: HIGH
|
|
84
|
+
confidence: HIGH
|
|
85
|
+
fix: |
|
|
86
|
+
Use subprocess with shell=False:
|
|
87
|
+
subprocess.run(['ping', '-c', '1', host], shell=False)
|
|
88
|
+
severity: ERROR
|
|
89
|
+
languages:
|
|
90
|
+
- python
|
|
91
|
+
mode: taint
|
|
92
|
+
pattern-sources:
|
|
93
|
+
- patterns:
|
|
94
|
+
- pattern-either:
|
|
95
|
+
- pattern: request.args.get(...)
|
|
96
|
+
- pattern: request.args[...]
|
|
97
|
+
- pattern: request.form.get(...)
|
|
98
|
+
- pattern: request.form[...]
|
|
99
|
+
- pattern: request.json
|
|
100
|
+
- pattern: request.json.get(...)
|
|
101
|
+
- pattern: request.json[...]
|
|
102
|
+
- pattern: flask.request.args.get(...)
|
|
103
|
+
- pattern: flask.request.form[...]
|
|
104
|
+
pattern-sinks:
|
|
105
|
+
- patterns:
|
|
106
|
+
- pattern-either:
|
|
107
|
+
- pattern: os.system(...)
|
|
108
|
+
- pattern: os.popen(...)
|
|
109
|
+
- pattern: os.spawn(...)
|
|
110
|
+
- pattern: os.spawnl(...)
|
|
111
|
+
- pattern: os.spawnle(...)
|
|
112
|
+
- pattern: os.spawnlp(...)
|
|
113
|
+
- pattern: os.spawnlpe(...)
|
|
114
|
+
- pattern: os.spawnv(...)
|
|
115
|
+
- pattern: os.spawnve(...)
|
|
116
|
+
- pattern: os.spawnvp(...)
|
|
117
|
+
- pattern: os.spawnvpe(...)
|
|
118
|
+
pattern-sanitizers:
|
|
119
|
+
- patterns:
|
|
120
|
+
- pattern-either:
|
|
121
|
+
- pattern: shlex.quote(...)
|
|
122
|
+
- pattern: shlex.split(...)
|
|
123
|
+
|
|
124
|
+
# Flask Command Injection - subprocess
|
|
125
|
+
- id: flask-command-injection-subprocess
|
|
126
|
+
message: >-
|
|
127
|
+
Command Injection vulnerability detected. User input from Flask request flows
|
|
128
|
+
to subprocess with shell=True. An attacker could execute arbitrary system commands.
|
|
129
|
+
Use subprocess with shell=False and pass arguments as a list.
|
|
130
|
+
metadata:
|
|
131
|
+
cwe:
|
|
132
|
+
- 'CWE-78: OS Command Injection'
|
|
133
|
+
owasp:
|
|
134
|
+
- A03:2021 - Injection
|
|
135
|
+
- A05:2025 - Injection
|
|
136
|
+
category: security
|
|
137
|
+
technology:
|
|
138
|
+
- flask
|
|
139
|
+
subcategory:
|
|
140
|
+
- vuln
|
|
141
|
+
impact: CRITICAL
|
|
142
|
+
likelihood: HIGH
|
|
143
|
+
confidence: HIGH
|
|
144
|
+
fix: |
|
|
145
|
+
Use subprocess with shell=False:
|
|
146
|
+
subprocess.run(['command', arg1, arg2], shell=False)
|
|
147
|
+
severity: ERROR
|
|
148
|
+
languages:
|
|
149
|
+
- python
|
|
150
|
+
mode: taint
|
|
151
|
+
pattern-sources:
|
|
152
|
+
- patterns:
|
|
153
|
+
- pattern-either:
|
|
154
|
+
- pattern: request.args.get(...)
|
|
155
|
+
- pattern: request.form[...]
|
|
156
|
+
- pattern: request.json
|
|
157
|
+
- pattern: request.json[...]
|
|
158
|
+
- pattern: flask.request.args.get(...)
|
|
159
|
+
pattern-sinks:
|
|
160
|
+
- patterns:
|
|
161
|
+
- pattern-either:
|
|
162
|
+
- pattern: subprocess.call(..., shell=True, ...)
|
|
163
|
+
- pattern: subprocess.run(..., shell=True, ...)
|
|
164
|
+
- pattern: subprocess.Popen(..., shell=True, ...)
|
|
165
|
+
- pattern: subprocess.check_output(..., shell=True, ...)
|
|
166
|
+
- pattern: subprocess.check_call(..., shell=True, ...)
|
|
167
|
+
pattern-sanitizers:
|
|
168
|
+
- patterns:
|
|
169
|
+
- pattern: shlex.quote(...)
|
|
170
|
+
|
|
171
|
+
# Flask Path Traversal
|
|
172
|
+
- id: flask-path-traversal
|
|
173
|
+
message: >-
|
|
174
|
+
Path Traversal vulnerability detected. User input from Flask request is used
|
|
175
|
+
in file operations without validation. An attacker could access files outside
|
|
176
|
+
the intended directory using '../' sequences. Validate and sanitize file paths.
|
|
177
|
+
metadata:
|
|
178
|
+
cwe:
|
|
179
|
+
- 'CWE-22: Path Traversal'
|
|
180
|
+
owasp:
|
|
181
|
+
- A01:2021 - Broken Access Control
|
|
182
|
+
- A01:2025 - Broken Access Control
|
|
183
|
+
category: security
|
|
184
|
+
technology:
|
|
185
|
+
- flask
|
|
186
|
+
subcategory:
|
|
187
|
+
- vuln
|
|
188
|
+
impact: HIGH
|
|
189
|
+
likelihood: MEDIUM
|
|
190
|
+
confidence: HIGH
|
|
191
|
+
fix: |
|
|
192
|
+
Use secure_filename and validate paths:
|
|
193
|
+
from werkzeug.utils import secure_filename
|
|
194
|
+
filename = secure_filename(user_input)
|
|
195
|
+
safe_path = os.path.join(base_dir, filename)
|
|
196
|
+
if not safe_path.startswith(base_dir):
|
|
197
|
+
abort(403)
|
|
198
|
+
severity: ERROR
|
|
199
|
+
languages:
|
|
200
|
+
- python
|
|
201
|
+
mode: taint
|
|
202
|
+
pattern-sources:
|
|
203
|
+
- patterns:
|
|
204
|
+
- pattern-either:
|
|
205
|
+
- pattern: request.args.get(...)
|
|
206
|
+
- pattern: request.form[...]
|
|
207
|
+
- pattern: request.json[...]
|
|
208
|
+
- pattern: flask.request.args.get(...)
|
|
209
|
+
pattern-sinks:
|
|
210
|
+
- patterns:
|
|
211
|
+
- pattern-either:
|
|
212
|
+
- pattern: open(...)
|
|
213
|
+
- pattern: os.path.join(...)
|
|
214
|
+
- pattern: pathlib.Path(...)
|
|
215
|
+
- pattern: send_file(...)
|
|
216
|
+
- pattern: send_from_directory(...)
|
|
217
|
+
pattern-sanitizers:
|
|
218
|
+
- patterns:
|
|
219
|
+
- pattern-either:
|
|
220
|
+
- pattern: secure_filename(...)
|
|
221
|
+
- pattern: os.path.basename(...)
|
|
222
|
+
|
|
223
|
+
# Flask XSS via Template Injection
|
|
224
|
+
- id: flask-template-injection
|
|
225
|
+
message: >-
|
|
226
|
+
Server-Side Template Injection (SSTI) vulnerability detected. User input flows
|
|
227
|
+
to render_template_string() which can execute arbitrary Python code. Use
|
|
228
|
+
render_template() with separate template files and proper escaping instead.
|
|
229
|
+
metadata:
|
|
230
|
+
cwe:
|
|
231
|
+
- 'CWE-79: Cross-site Scripting'
|
|
232
|
+
- 'CWE-94: Code Injection'
|
|
233
|
+
owasp:
|
|
234
|
+
- A03:2021 - Injection
|
|
235
|
+
- A05:2025 - Injection
|
|
236
|
+
category: security
|
|
237
|
+
technology:
|
|
238
|
+
- flask
|
|
239
|
+
- jinja2
|
|
240
|
+
subcategory:
|
|
241
|
+
- vuln
|
|
242
|
+
impact: CRITICAL
|
|
243
|
+
likelihood: HIGH
|
|
244
|
+
confidence: HIGH
|
|
245
|
+
fix: |
|
|
246
|
+
Use render_template with separate files:
|
|
247
|
+
return render_template('greeting.html', name=name)
|
|
248
|
+
# In greeting.html: <h1>Hello, {{ name }}!</h1>
|
|
249
|
+
severity: ERROR
|
|
250
|
+
languages:
|
|
251
|
+
- python
|
|
252
|
+
mode: taint
|
|
253
|
+
pattern-sources:
|
|
254
|
+
- patterns:
|
|
255
|
+
- pattern-either:
|
|
256
|
+
- pattern: request.args.get(...)
|
|
257
|
+
- pattern: request.form[...]
|
|
258
|
+
- pattern: request.json[...]
|
|
259
|
+
- pattern: flask.request.args.get(...)
|
|
260
|
+
pattern-sinks:
|
|
261
|
+
- patterns:
|
|
262
|
+
- pattern-either:
|
|
263
|
+
- pattern: render_template_string(...)
|
|
264
|
+
- pattern: flask.render_template_string(...)
|
|
265
|
+
- pattern: jinja2.Template(...).render(...)
|
|
266
|
+
|
|
267
|
+
# Flask Insecure Deserialization
|
|
268
|
+
- id: flask-insecure-deserialization
|
|
269
|
+
message: >-
|
|
270
|
+
Insecure deserialization vulnerability detected. User input flows to pickle.loads()
|
|
271
|
+
which can execute arbitrary code. Never deserialize untrusted data with pickle.
|
|
272
|
+
Use JSON or other safe serialization formats.
|
|
273
|
+
metadata:
|
|
274
|
+
cwe:
|
|
275
|
+
- 'CWE-502: Deserialization of Untrusted Data'
|
|
276
|
+
owasp:
|
|
277
|
+
- A08:2021 - Software and Data Integrity Failures
|
|
278
|
+
- A08:2025 - Software or Data Integrity Failures
|
|
279
|
+
category: security
|
|
280
|
+
technology:
|
|
281
|
+
- flask
|
|
282
|
+
subcategory:
|
|
283
|
+
- vuln
|
|
284
|
+
impact: CRITICAL
|
|
285
|
+
likelihood: MEDIUM
|
|
286
|
+
confidence: HIGH
|
|
287
|
+
fix: |
|
|
288
|
+
Use JSON instead of pickle:
|
|
289
|
+
data = json.loads(user_input)
|
|
290
|
+
severity: ERROR
|
|
291
|
+
languages:
|
|
292
|
+
- python
|
|
293
|
+
mode: taint
|
|
294
|
+
pattern-sources:
|
|
295
|
+
- patterns:
|
|
296
|
+
- pattern-either:
|
|
297
|
+
- pattern: request.data
|
|
298
|
+
- pattern: request.get_data(...)
|
|
299
|
+
- pattern: request.json
|
|
300
|
+
- pattern: flask.request.data
|
|
301
|
+
pattern-sinks:
|
|
302
|
+
- patterns:
|
|
303
|
+
- pattern-either:
|
|
304
|
+
- pattern: pickle.loads(...)
|
|
305
|
+
- pattern: pickle.load(...)
|
|
306
|
+
- pattern: yaml.load(...)
|
|
307
|
+
- pattern: yaml.unsafe_load(...)
|
|
308
|
+
pattern-sanitizers:
|
|
309
|
+
- patterns:
|
|
310
|
+
- pattern: yaml.safe_load(...)
|
|
311
|
+
|
|
312
|
+
# Flask Eval Injection
|
|
313
|
+
- id: flask-eval-injection
|
|
314
|
+
message: >-
|
|
315
|
+
Code injection vulnerability detected. User input flows to eval() or exec()
|
|
316
|
+
which can execute arbitrary Python code. Never use eval/exec with user input.
|
|
317
|
+
metadata:
|
|
318
|
+
cwe:
|
|
319
|
+
- 'CWE-94: Code Injection'
|
|
320
|
+
owasp:
|
|
321
|
+
- A03:2021 - Injection
|
|
322
|
+
- A05:2025 - Injection
|
|
323
|
+
category: security
|
|
324
|
+
technology:
|
|
325
|
+
- flask
|
|
326
|
+
subcategory:
|
|
327
|
+
- vuln
|
|
328
|
+
impact: CRITICAL
|
|
329
|
+
likelihood: HIGH
|
|
330
|
+
confidence: HIGH
|
|
331
|
+
fix: |
|
|
332
|
+
Avoid eval/exec entirely. Use safer alternatives like:
|
|
333
|
+
- ast.literal_eval() for simple data structures
|
|
334
|
+
- JSON parsing for data exchange
|
|
335
|
+
severity: ERROR
|
|
336
|
+
languages:
|
|
337
|
+
- python
|
|
338
|
+
mode: taint
|
|
339
|
+
pattern-sources:
|
|
340
|
+
- patterns:
|
|
341
|
+
- pattern-either:
|
|
342
|
+
- pattern: request.args.get(...)
|
|
343
|
+
- pattern: request.form[...]
|
|
344
|
+
- pattern: request.json[...]
|
|
345
|
+
- pattern: request.data
|
|
346
|
+
- pattern: flask.request.args.get(...)
|
|
347
|
+
pattern-sinks:
|
|
348
|
+
- patterns:
|
|
349
|
+
- pattern-either:
|
|
350
|
+
- pattern: eval(...)
|
|
351
|
+
- pattern: exec(...)
|
|
352
|
+
- pattern: compile(...)
|
package/src/analyzer.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Add the directory containing this script to the path
|
|
7
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
8
|
+
|
|
9
|
+
from rules import get_rules, get_rules_for_language, get_rule_stats
|
|
10
|
+
|
|
11
|
+
# File extension to language mapping
|
|
12
|
+
EXTENSION_MAP = {
|
|
13
|
+
'.py': 'python',
|
|
14
|
+
'.js': 'javascript',
|
|
15
|
+
'.ts': 'typescript',
|
|
16
|
+
'.tsx': 'typescript',
|
|
17
|
+
'.jsx': 'javascript',
|
|
18
|
+
'.java': 'java',
|
|
19
|
+
'.go': 'go',
|
|
20
|
+
'.rb': 'ruby',
|
|
21
|
+
'.php': 'php',
|
|
22
|
+
'.cs': 'csharp',
|
|
23
|
+
'.rs': 'rust',
|
|
24
|
+
'.c': 'c',
|
|
25
|
+
'.cpp': 'cpp',
|
|
26
|
+
'.h': 'c',
|
|
27
|
+
'.hpp': 'cpp',
|
|
28
|
+
'.sql': 'sql',
|
|
29
|
+
'.dockerfile': 'dockerfile',
|
|
30
|
+
'.yaml': 'yaml',
|
|
31
|
+
'.yml': 'yaml',
|
|
32
|
+
'.json': 'json',
|
|
33
|
+
'.tf': 'terraform',
|
|
34
|
+
'.hcl': 'terraform',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def detect_language(file_path):
|
|
38
|
+
"""Detect the programming language from file extension or name"""
|
|
39
|
+
basename = os.path.basename(file_path).lower()
|
|
40
|
+
|
|
41
|
+
if basename == 'dockerfile' or basename.startswith('dockerfile.') or basename.startswith('dockerfile_'):
|
|
42
|
+
return 'dockerfile'
|
|
43
|
+
|
|
44
|
+
_, ext = os.path.splitext(file_path.lower())
|
|
45
|
+
return EXTENSION_MAP.get(ext, 'generic')
|
|
46
|
+
|
|
47
|
+
def analyze_file(file_path):
|
|
48
|
+
"""Analyze a single file for security vulnerabilities"""
|
|
49
|
+
issues = []
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
language = detect_language(file_path)
|
|
53
|
+
rules = get_rules_for_language(language)
|
|
54
|
+
|
|
55
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
56
|
+
lines = f.readlines()
|
|
57
|
+
content = ''.join(lines)
|
|
58
|
+
|
|
59
|
+
for line_index, line in enumerate(lines):
|
|
60
|
+
original_line = line
|
|
61
|
+
line = line.strip()
|
|
62
|
+
if not line:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# Skip comment-only lines (basic detection)
|
|
66
|
+
if line.startswith('#') or line.startswith('//') or line.startswith('*'):
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
for rule_id, rule in rules.items():
|
|
70
|
+
for pattern in rule['patterns']:
|
|
71
|
+
try:
|
|
72
|
+
# Use IGNORECASE for better detection (API_KEY vs api_key)
|
|
73
|
+
matches = re.finditer(pattern, line, re.IGNORECASE)
|
|
74
|
+
for match in matches:
|
|
75
|
+
# Calculate column based on original line (preserve indentation)
|
|
76
|
+
col_offset = len(original_line) - len(original_line.lstrip())
|
|
77
|
+
issues.append({
|
|
78
|
+
'ruleId': rule['id'],
|
|
79
|
+
'message': f"[{rule['name']}] {rule['message']}",
|
|
80
|
+
'line': line_index,
|
|
81
|
+
'column': match.start() + col_offset,
|
|
82
|
+
'length': match.end() - match.start(),
|
|
83
|
+
'severity': rule['severity'],
|
|
84
|
+
'metadata': rule.get('metadata', {})
|
|
85
|
+
})
|
|
86
|
+
except re.error:
|
|
87
|
+
# Skip invalid regex patterns
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return {'error': str(e)}
|
|
92
|
+
|
|
93
|
+
# Deduplicate issues (same rule, same line)
|
|
94
|
+
seen = set()
|
|
95
|
+
unique_issues = []
|
|
96
|
+
for issue in issues:
|
|
97
|
+
key = (issue['ruleId'], issue['line'], issue['column'])
|
|
98
|
+
if key not in seen:
|
|
99
|
+
seen.add(key)
|
|
100
|
+
unique_issues.append(issue)
|
|
101
|
+
|
|
102
|
+
return unique_issues
|
|
103
|
+
|
|
104
|
+
def main():
|
|
105
|
+
if len(sys.argv) < 2:
|
|
106
|
+
print(json.dumps({'error': 'No file path provided'}))
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
|
|
109
|
+
file_path = sys.argv[1]
|
|
110
|
+
|
|
111
|
+
if not os.path.exists(file_path):
|
|
112
|
+
print(json.dumps({'error': f'File not found: {file_path}'}))
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
results = analyze_file(file_path)
|
|
116
|
+
print(json.dumps(results))
|
|
117
|
+
|
|
118
|
+
if __name__ == '__main__':
|
|
119
|
+
main()
|
package/src/cli/demo.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { execFileSync } from "child_process";
|
|
2
|
+
import { writeFileSync, unlinkSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
import { dirname } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
// Handle both ESM and CJS bundling (Smithery bundles to CJS)
|
|
9
|
+
let __dirname;
|
|
10
|
+
try {
|
|
11
|
+
__dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
} catch {
|
|
13
|
+
__dirname = process.cwd();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const DEMO_TEMPLATES = {
|
|
17
|
+
js: {
|
|
18
|
+
ext: 'js',
|
|
19
|
+
name: 'JavaScript',
|
|
20
|
+
code: `const express = require("express");
|
|
21
|
+
const child_process = require("child_process");
|
|
22
|
+
const app = express();
|
|
23
|
+
|
|
24
|
+
// SQL Injection vulnerability
|
|
25
|
+
app.get("/user", (req, res) => {
|
|
26
|
+
const userId = req.query.id;
|
|
27
|
+
db.query("SELECT * FROM users WHERE id = " + userId, (err, result) => {
|
|
28
|
+
res.send(result);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// XSS vulnerability
|
|
33
|
+
app.get("/profile", (req, res) => {
|
|
34
|
+
const name = req.query.name;
|
|
35
|
+
document.getElementById("welcome").innerHTML = name;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Command Injection vulnerability
|
|
39
|
+
app.get("/run", (req, res) => {
|
|
40
|
+
const cmd = req.query.cmd;
|
|
41
|
+
child_process.exec("ls " + cmd, (err, stdout) => {
|
|
42
|
+
res.send(stdout);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
`
|
|
46
|
+
},
|
|
47
|
+
py: {
|
|
48
|
+
ext: 'py',
|
|
49
|
+
name: 'Python',
|
|
50
|
+
code: `import pickle
|
|
51
|
+
import subprocess
|
|
52
|
+
import hashlib
|
|
53
|
+
|
|
54
|
+
API_SECRET = "stripe_test_FAKEFAKEFAKEFAKE1234"
|
|
55
|
+
|
|
56
|
+
def get_user(user_id):
|
|
57
|
+
query = f"SELECT * FROM users WHERE id = {user_id}"
|
|
58
|
+
cursor.execute(query)
|
|
59
|
+
return cursor.fetchone()
|
|
60
|
+
|
|
61
|
+
def load_data(data):
|
|
62
|
+
return pickle.loads(data)
|
|
63
|
+
|
|
64
|
+
def run_command(cmd):
|
|
65
|
+
return subprocess.call(cmd, shell=True)
|
|
66
|
+
|
|
67
|
+
def hash_password(password):
|
|
68
|
+
return hashlib.md5(password.encode()).hexdigest()
|
|
69
|
+
`
|
|
70
|
+
},
|
|
71
|
+
go: {
|
|
72
|
+
ext: 'go',
|
|
73
|
+
name: 'Go',
|
|
74
|
+
code: `package main
|
|
75
|
+
|
|
76
|
+
import (
|
|
77
|
+
\t"crypto/md5"
|
|
78
|
+
\t"database/sql"
|
|
79
|
+
\t"fmt"
|
|
80
|
+
\t"net/http"
|
|
81
|
+
\t"os/exec"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
var dbPassword = "super_secret_password_123"
|
|
85
|
+
|
|
86
|
+
func getUser(w http.ResponseWriter, r *http.Request) {
|
|
87
|
+
\tid := r.URL.Query().Get("id")
|
|
88
|
+
\tquery := fmt.Sprintf("SELECT * FROM users WHERE id = %s", id)
|
|
89
|
+
\tdb.Query(query)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func runCmd(w http.ResponseWriter, r *http.Request) {
|
|
93
|
+
\tcmd := r.URL.Query().Get("cmd")
|
|
94
|
+
\tout, _ := exec.Command("sh", "-c", cmd).Output()
|
|
95
|
+
\tw.Write(out)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
func hashData(data string) string {
|
|
99
|
+
\th := md5.Sum([]byte(data))
|
|
100
|
+
\treturn fmt.Sprintf("%x", h)
|
|
101
|
+
}
|
|
102
|
+
`
|
|
103
|
+
},
|
|
104
|
+
java: {
|
|
105
|
+
ext: 'java',
|
|
106
|
+
name: 'Java',
|
|
107
|
+
code: `import java.sql.*;
|
|
108
|
+
import java.io.*;
|
|
109
|
+
import java.security.MessageDigest;
|
|
110
|
+
|
|
111
|
+
public class VulnDemo {
|
|
112
|
+
private static final String DB_PASSWORD = "admin123";
|
|
113
|
+
|
|
114
|
+
public ResultSet getUser(String userId) throws SQLException {
|
|
115
|
+
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
|
|
116
|
+
Statement stmt = conn.createStatement();
|
|
117
|
+
return stmt.executeQuery("SELECT * FROM users WHERE id = " + userId);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public String runCommand(String cmd) throws IOException {
|
|
121
|
+
Runtime rt = Runtime.getRuntime();
|
|
122
|
+
Process proc = rt.exec(cmd);
|
|
123
|
+
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
|
|
124
|
+
return reader.readLine();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public String hashPassword(String password) throws Exception {
|
|
128
|
+
MessageDigest md = MessageDigest.getInstance("MD5");
|
|
129
|
+
byte[] hash = md.digest(password.getBytes());
|
|
130
|
+
return new String(hash);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
`
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
function parseDemoFlags(args) {
|
|
138
|
+
const flags = { lang: 'js' };
|
|
139
|
+
let i = 0;
|
|
140
|
+
while (i < args.length) {
|
|
141
|
+
const arg = args[i];
|
|
142
|
+
if ((arg === '--lang' || arg === '-l') && i + 1 < args.length) {
|
|
143
|
+
flags.lang = args[++i].toLowerCase();
|
|
144
|
+
} else if (!arg.startsWith('-')) {
|
|
145
|
+
flags.lang = arg.toLowerCase();
|
|
146
|
+
}
|
|
147
|
+
i++;
|
|
148
|
+
}
|
|
149
|
+
return flags;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function checkCommand(cmd, args) {
|
|
153
|
+
try {
|
|
154
|
+
const out = execFileSync(cmd, args, { timeout: 10000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
155
|
+
return { ok: true, output: out.trim() };
|
|
156
|
+
} catch {
|
|
157
|
+
return { ok: false, output: null };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function runDemo(args) {
|
|
162
|
+
const flags = parseDemoFlags(args);
|
|
163
|
+
const template = DEMO_TEMPLATES[flags.lang];
|
|
164
|
+
if (!template) {
|
|
165
|
+
console.log(`\n Unknown language: "${flags.lang}"`);
|
|
166
|
+
console.log(` Available: ${Object.keys(DEMO_TEMPLATES).join(', ')}\n`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const filename = `vuln-demo.${template.ext}`;
|
|
171
|
+
const filepath = join(process.cwd(), filename);
|
|
172
|
+
|
|
173
|
+
console.log(`\n agent-security-scanner-mcp demo\n`);
|
|
174
|
+
console.log(` Creating ${filename} with 3 intentional vulnerabilities...\n`);
|
|
175
|
+
|
|
176
|
+
// Write the vulnerable file
|
|
177
|
+
writeFileSync(filepath, template.code);
|
|
178
|
+
|
|
179
|
+
// Run the analyzer
|
|
180
|
+
const analyzerPath = join(__dirname, '..', '..', 'analyzer.py');
|
|
181
|
+
let pythonCmd = 'python3';
|
|
182
|
+
const py3 = checkCommand('python3', ['--version']);
|
|
183
|
+
if (!py3.ok) {
|
|
184
|
+
const py = checkCommand('python', ['--version']);
|
|
185
|
+
if (py.ok && py.output.includes('3.')) {
|
|
186
|
+
pythonCmd = 'python';
|
|
187
|
+
} else {
|
|
188
|
+
console.log(` Error: Python 3 not found. Run "npx agent-security-scanner-mcp doctor" to diagnose.\n`);
|
|
189
|
+
unlinkSync(filepath);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let results;
|
|
195
|
+
try {
|
|
196
|
+
const output = execFileSync(pythonCmd, [analyzerPath, filepath], { timeout: 30000, encoding: 'utf-8' });
|
|
197
|
+
results = JSON.parse(output);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
console.log(` Error running analyzer: ${e.message}\n`);
|
|
200
|
+
unlinkSync(filepath);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Display results
|
|
205
|
+
console.log(` Scanning...\n`);
|
|
206
|
+
|
|
207
|
+
if (results.length === 0) {
|
|
208
|
+
console.log(` No issues found (unexpected for demo file).\n`);
|
|
209
|
+
} else {
|
|
210
|
+
console.log(` Found ${results.length} issue(s):\n`);
|
|
211
|
+
for (const issue of results) {
|
|
212
|
+
const severity = (issue.severity || 'error').toUpperCase();
|
|
213
|
+
const icon = severity === 'ERROR' ? '\u2717' : severity === 'WARNING' ? '\u2717' : '\u2022';
|
|
214
|
+
console.log(` ${icon} ${severity.padEnd(8)} Line ${String(issue.line).padEnd(4)} ${issue.message}`);
|
|
215
|
+
if (issue.metadata) {
|
|
216
|
+
const refs = [issue.metadata.cwe, issue.metadata.owasp].filter(Boolean).join(' | ');
|
|
217
|
+
if (refs) console.log(` ${refs}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
console.log(`\n ${results.length} vulnerabilities detected.\n`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Ask to keep or delete
|
|
224
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
225
|
+
const answer = await new Promise((resolve) => {
|
|
226
|
+
rl.question(` Keep ${filename} for testing? (y/N): `, (a) => { rl.close(); resolve(a); });
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (answer.toLowerCase() === 'y') {
|
|
230
|
+
console.log(`\n Kept: ${filepath}`);
|
|
231
|
+
} else {
|
|
232
|
+
unlinkSync(filepath);
|
|
233
|
+
console.log(`\n Deleted: ${filename}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
console.log(`\n Next: Connect to your AI coding tool and ask it to`);
|
|
237
|
+
console.log(` "scan ${filename} for security issues"\n`);
|
|
238
|
+
}
|