Skip to content
Report library
Purpose / Other

Extension Stripe Skill Security Audit

What the author says it does (original text)

Payment support based on Stripe, supporting credit cards and debit cards

Independent security check

Do not install or run it yet

Files checked
1
Risks found
4
Could it run dangerous commands?Looks for programs run straight after downloading, remote control of your computer, and hidden commands.No risks found
Could it expose your files or keys?Looks for uploads of files containing passwords or keys, and keys written directly in the code.Risks found: 1
Medium risk

Any caller can query payment status and associated user with a known session ID

Source references: 2
What we found

getStripeSessionStatus has no login, ownership, or administrator check. Its return type may contain the Stripe response body and userPrincipal, and the lookup requires only a sessionId.

Why this matters

Someone who obtains or guesses a valid session ID may receive payment status, transaction details present in the raw response, and the associated user identifier. The exact disclosure depends on which fields the Stripe module places in response.

The status type can return the Stripe response body and userPrincipal. Its public wrapper accepts only a sessionId and shows no authentication, session-ownership, or administrator check. Someone who obtains or guesses a valid ID could potentially read another customer's payment state and associated identifier. Exposure depends on the dependency's response contents and ID entropy; users should require caller-bound ownership checks and minimal returned fields.

SKILL.md:49In the instructionsOpen original file
    public type StripeSessionStatus = {    #failed : { error : Text };    #completed : { response : Text; userPrincipal : ?Text };  };  /// Check payment status.  public func getSessionStatus(configuration : StripeConfiguration, sessionId : Text, transform : OutCall.Transform) : async StripeSessionStatus;};
Show 1 other places
SKILL.md:127In the instructionsOpen original file
    public func getStripeSessionStatus(sessionId : Text) : async Stripe.StripeSessionStatus {        await Stripe.getSessionStatus(getStripeConfiguration(), sessionId, transform);    };
Could it delete files or keep running?Looks for broad file deletion, disk overwrites, and programs set to start automatically.No risks found
Could it bypass safety checks?Looks for skipped website security checks, excessive file access, or actions that skip your approval.Risks found: 1
Medium risk

Checkout redirects without validating a Stripe host or HTTPS

Source references: 3
What we found

The frontend checks only that session.url is non-empty, then assigns it to window.location.href. It does not validate the scheme or destination host, and the URL comes from JSON returned by the dependency-backed method.

Why this matters

If the backend dependency, its response, or the checkout endpoint is compromised, users can be sent without an effective warning to a counterfeit payment page, malicious download, or unencrypted site.

The frontend parses backend text as JSON, checks only that url is non-empty, and then performs a full-page redirect without a shown HTTPS or Stripe-host allowlist. The dependency should normally return Stripe, but a malicious or malformed dependency/backend response could send the user to an arbitrary phishing site. Users should require strict validation against expected HTTPS Stripe hosts, preferably enforced by the trusted backend.

SKILL.md:212In the instructionsOpen original file
                const cancelUrl = `${baseUrl}/payment-failure`;                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);                // JSON parsing is important!                const session = JSON.parse(result) as CheckoutSession;                if (!session?.url) {                    throw new Error('Stripe session missing url');                }                return session;            }
Show 2 other places
SKILL.md:228In the instructionsOpen original file
    * Anaylze the `CheckoutSession` result.    * Redirect webpage to url in `CheckoutSession`: This allows the user to complete the payment.    * Do NOT use router navigation for the Stripe URL. Use `window.location.href`.    * Never navigate to `/undefined`; if `session.url` is missing, show an error and stop.
SKILL.md:233In the instructionsOpen original file
    ```    const session = await createCheckoutSession.mutateAsync(shoppingItems);    if (!session?.url) throw new Error('Stripe session missing url');    window.location.href = session.url;    ```
Could it mislead the AI or hide text?Checks the skill instructions for requests to ignore you, influence the report, or hide text in invisible characters.No risks found
Could it change links or payment recipients without asking?Looks for forced referral or payment changes combined with instructions to hide the change.Risks found: 2
High risk

Clients can choose checkout products, prices, and quantities

Source references: 4
What we found

The public createCheckoutSession method forwards caller-supplied ShoppingItem[] directly to the Stripe module. ShoppingItem includes the product name, price, and quantity, but the code neither looks the item up in the backend products catalog nor recalculates or validates the amount.

Why this matters

If the application treats a completed payment as proof that an order was fully paid, a caller can bypass the normal interface and create a session with a reduced price, zero quantity, or fabricated item, potentially obtaining goods or services after underpaying.

The example places price, quantity, currency, and product details in caller-supplied ShoppingItem values. The public checkout method forwards them without shown catalog lookup, price recomputation, or quantity validation. A caller bypassing the official UI could therefore request a checkout with a reduced price or abnormal quantity. Users should ask whether caffeineai-stripe performs independent validation and require the API to accept only product IDs and quantities.

SKILL.md:37In the instructionsOpen original file
  public type ShoppingItem = {    currency : Text;    productName : Text;    productDescription : Text;    priceInCents : Nat;    quantity : Nat;  };
Show 3 other places
SKILL.md:82In the instructionsOpen original file
    let products : Map.Map<Text, Product>;    public query func getProducts() : async [Product] {        products.values().toArray();    };
SKILL.md:131In the instructionsOpen original file
    public shared ({ caller }) func createCheckoutSession(items : [Stripe.ShoppingItem], successUrl : Text, cancelUrl : Text) : async Text {        await Stripe.createCheckoutSession(getStripeConfiguration(), caller, items, successUrl, cancelUrl, transform);    };
SKILL.md:84In the instructionsOpen original file
    public query func getProducts() : async [Product] {        products.values().toArray();    };
Medium risk

Callers can choose arbitrary Stripe success and cancellation destinations

Source references: 4
What we found

The public backend method accepts successUrl and cancelUrl and passes them unchanged to the Stripe module, without requiring them to belong to the merchant's site. The supplied frontend constructs same-site URLs, but a direct actor caller is not constrained by that frontend behavior.

Why this matters

An attacker can create a session that redirects to a third-party site after payment and distribute that session to someone else. This can combine a genuine Stripe payment flow with subsequent phishing, false receipts, or misleading pages.

Both the underlying function and public wrapper accept caller-provided successUrl and cancelUrl and pass them to the Stripe module, with no shown origin, scheme, or merchant-domain restriction. The official hook uses the current site, but direct callers of the public actor are not bound by that UI and could request a session that redirects to a phishing site after checkout. The source does not show whether Stripe rejects it; users should require server-fixed or strictly allowlisted return URLs.

SKILL.md:47In the instructionsOpen original file
  /// Returns Stripe JSON reply message.  public func createCheckoutSession(configuration : StripeConfiguration, caller : Principal, items : [ShoppingItem], successUrl : Text, cancelUrl : Text, transform : OutCall.Transform) : async Text;  
Show 3 other places
SKILL.md:131In the instructionsOpen original file
    public shared ({ caller }) func createCheckoutSession(items : [Stripe.ShoppingItem], successUrl : Text, cancelUrl : Text) : async Text {        await Stripe.createCheckoutSession(getStripeConfiguration(), caller, items, successUrl, cancelUrl, transform);    };
SKILL.md:45In the instructionsOpen original file
  /// Initiate payment session for shopping items.  /// Returns Stripe JSON reply message.  public func createCheckoutSession(configuration : StripeConfiguration, caller : Principal, items : [ShoppingItem], successUrl : Text, cancelUrl : Text, transform : OutCall.Transform) : async Text;  
SKILL.md:209In the instructionsOpen original file
                if (!actor) throw new Error('Actor not available');                const baseUrl = `${window.location.protocol}//${window.location.host}`;                const successUrl = `${baseUrl}/payment-success`;                const cancelUrl = `${baseUrl}/payment-failure`;                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);                // JSON parsing is important!

Inside this skill

2 instruction sections

The Skill asks an administrator to enter a Stripe secret key and allowed countries in the frontend, then stores the complete configuration in backend actor state.

View source
SKILL.md:110In the instructionsOpen original file
    // Stripe integration    var configuration : ?Stripe.StripeConfiguration;
SKILL.md:116In the instructionsOpen original file
    public shared ({ caller }) func setStripeConfiguration(config : Stripe.StripeConfiguration) : async () {        if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {            Runtime.trap("Unauthorized: Only admins can perform this action");        };        configuration := ?config;    };
SKILL.md:181In the instructionsOpen original file
1. Implement a PaymentSetup component with:    * Use `isStripeConfigured()` and `setStripeConfiguration()`    * Checks whether Stripe payment is configured.    * If not, opens an admin panel and asks the user to initialze Stripe with `StripeConfiguration`.      - Stripe secret key      - List of allowed countries, notation ["US", "CA", "GB"] etc., see the Stripe documentation.    * Do not show the payment setup when it has already been configured!

Product-management operations are protected by administrator permission checks, while checkout creation and session-status lookup do not have equivalent authorization checks.

View source
SKILL.md:88In the instructionsOpen original file
    public shared ({ caller }) func addProduct(product : Product) : async () {        if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {            Runtime.trap("Unauthorized: Only admins can add products");        };        products.add(product.id, product);    };
SKILL.md:127In the instructionsOpen original file
    public func getStripeSessionStatus(sessionId : Text) : async Stripe.StripeSessionStatus {        await Stripe.getSessionStatus(getStripeConfiguration(), sessionId, transform);    };    public shared ({ caller }) func createCheckoutSession(items : [Stripe.ShoppingItem], successUrl : Text, cancelUrl : Text) : async Text {        await Stripe.createCheckoutSession(getStripeConfiguration(), caller, items, successUrl, cancelUrl, transform);    };

The checkout result is parsed as JSON, and the frontend navigates the entire page to its URL as long as that URL is non-empty.

View source
SKILL.md:212In the instructionsOpen original file
                const cancelUrl = `${baseUrl}/payment-failure`;                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);                // JSON parsing is important!                const session = JSON.parse(result) as CheckoutSession;                if (!session?.url) {                    throw new Error('Stripe session missing url');                }                return session;            }
SKILL.md:233In the instructionsOpen original file
    ```    const session = await createCheckoutSession.mutateAsync(shoppingItems);    if (!session?.url) throw new Error('Stripe session missing url');    window.location.href = session.url;    ```
Start here · InstructionsSKILL.md
extension-stripe
Lines connect the instruction file to its sections, not an observed execution order. Select a section to read the source.
Files and check records1 files

Coverage and gaps

Content covered in each file

These are the source ranges included in this check, not a guarantee that every issue has been resolved.

  • SKILL.mdFull text included

This report is for the version above. We read the available code and instructions without running the skill or checking extra packages it installs. This is not a promise of safety: a different version or setup may behave differently.

  • SKILL.mdInstructions

Operations mentioned in code and instructions

Connect to websites
SKILL.md:14In the instructionsOpen original file
# Stripe Payment IntegrationStripe payment extension for [Caffeine AI](https://caffeine.ai?utm_source=caffeine-skill&utm_medium=referral).
Lines read
248
File checksum (to compare versions)
9bbb1033ba0a02fdcd2e7e194450850c8ede001cfe32c68416d1593a21163531