Recent Activities

This page shows what are we working on.

swissup / module-pro-labels

8 hours ago success
  • head
    • Restrict round method to allowed values and add missing ACL checks f974cb

    • Enhance image upload safety by improving file name validation and ensuring safe relative path retrieval d10521

    • Secure label variables rendering. 271155

    • Implement locking mechanism in buildIndexes method to prevent duplicate index rows (close #49) cb44f9

  • 1.10.1
    • Version 1.10.1 c44745

    • Add null check for associated products in getCheapestFromGrouped method 9156db

swissup / module-sold-together

8 hours ago success
  • head
    • Fixed cart price tampering via soldtogether promoted_price (#52)

      * Fixed cart price tampering via soldtogether promoted_price

      The promoted price was read from the quote item's info_buyRequest, which
      stores raw add-to-cart request params. Posting
      soldtogether[promoted_by]/soldtogether[promoted_price] let anyone set an
      arbitrary (even negative) price for any product in the cart.

      Recalculate the promoted price from the stored relation config instead.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

      * Calculate configurable promoted price from selected child price

      Promoted price for configurable item was calculated from parent's raw
      `price` and copied to the child. With parent price 0 and a by_fixed
      promo the child was charged 0.01; percent promos were lost.

      Calculate the price for the selected child, using parent id to find
      the relation.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

      * Enhance key generation for promoted price by including parent ID and filtering null/empty values

      ---------

      Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
      Co-authored-by: Vitaliy Dmytruk <vitaliy@swissuplabs.com> f98d14

    • Enhance key generation for promoted price by including parent ID and filtering null/empty values f20c27

    • Calculate configurable promoted price from selected child price

      Promoted price for configurable item was calculated from parent's raw
      `price` and copied to the child. With parent price 0 and a by_fixed
      promo the child was charged 0.01; percent promos were lost.

      Calculate the price for the selected child, using parent id to find
      the relation.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> f95778

    • Fixed cart price tampering via soldtogether promoted_price

      The promoted price was read from the quote item's info_buyRequest, which
      stores raw add-to-cart request params. Posting
      soldtogether[promoted_by]/soldtogether[promoted_price] let anyone set an
      arbitrary (even negative) price for any product in the cart.

      Recalculate the promoted price from the stored relation config instead.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> 54d858

swissup / module-pro-labels-configurable-product

9 hours ago success
  • head
    • Fix child labels ignoring schedule, store and product status

      - Skip expired labels and pass active period to frontend
      - Filter index by store view, skip disabled and out-of-website children
      - Validate $mode before using it in SQL column names
      - Require swissup/module-pro-labels ^1.10.0 (active period columns) 3074b5

swissup / module-askit

14 hours ago success
  • head
    • fix: fail closed on throttle save, limit owner exception, always stop emulation (#75)

      - SubmissionThrottle rejects the request when the counter cannot be saved
      - the question owner may only answer while it is pending, not after it was
      disapproved or closed
      - Notification\Admin/Customer stop environment emulation in a finally block,
      so the early returns no longer leave the request in admin area emulation
      (now reachable from the GraphQL mutations)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> cd8a83

    • test: add regression tests for the security fixes, keep throttle window fixed (#75)

      - SubmissionThrottle: window starts at the first hit and is no longer
      extended by every accepted submission
      - unit tests for InputFilter (whitelist, sanitizing, answerable question
      incl. store/private/status), MessageVoter (config, guest, unknown, hidden,
      other store, duplicate, atomic success, rollback) and SubmissionThrottle

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 697db9

    • fix: keep private-question checkbox for customers, redact vote response (#75)

      - allow is_private on new questions, normalized to 0/1 and only for logged-in customers
      - createVote no longer returns the author's email and customer id

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 337d52

    • fix: address review on throttle, store scope and vote validation (#75)

      - scope answers and votes to the current store (or store 0)
      - validate message status/privacy/store from the locked row inside the vote transaction
      - clean the message cache tags after a vote, as the update bypasses the model
      - throttle votes too; serialize the per-IP counter with a lock
      - GraphQL createQuestion: only guests are restricted, like the storefront controller

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> e7d702

    • fix(graphql): stop leaking author data and hidden answers (#75)

      The public askitQuestions query returned author email and customer id, did
      not apply the private filter to answers (so answers an admin made private
      still appeared in GraphQL and in the FAQPage JSON-LD), accepted any
      pageSize and varied per session although results are cached.

      Null out email/customer_id, filter private answers in both data providers,
      cap pageSize at 100 and drop the session-dependent branch.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 1af30c

    • fix(graphql): authenticate by token and apply storefront rules to mutations (#75)

      createQuestion/createAnswer/createVote looked a customer up by the email
      argument for unauthenticated callers, so anyone could post or vote as any
      registered customer and read back their name. Mutations also skipped the
      guest/customer permission settings, input sanitizing, parent-question
      visibility checks and any rate limit, and a guest vote bumped the hint and
      then failed on the NOT NULL customer_id.

      The identity now comes from the GraphQL context (customer token) only.
      Mutations reuse InputFilter and MessageVoter, enforce the allowed* settings,
      are limited per IP (Service\SubmissionThrottle) and dispatch
      askit_message_after_save so admins are notified.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 84217a

    • fix(graphql): stop evaluating CMS directives in message text (#75)

      DataProvider\Message ran every message text through the widget template
      filter, so an anonymous createQuestion/createAnswer containing {{block}},
      {{widget}} or {{config}} was evaluated server-side and returned in the
      mutation response and to every later reader. Return the stored text as is.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 53a198

    • fix(vote): validate target message and make voting atomic (#75)

      Vote/Inc loaded the message without checking it exists, so voting on an
      unknown id inserted an empty pending message (non-strict SQL mode). It also
      accepted private/unapproved messages, ignored the allowedHint setting and
      could be called with GET.

      Move voting into Service\MessageVoter: requires a real customer, an existing
      public message and allowedHint, and records vote plus hint change in one
      transaction under a row lock. Vote controllers are now POST-only.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 5daf64

    • fix(frontend): whitelist submitted fields and enforce answer permissions (#75)

      Question/Save and Answer/Save passed the whole POST body to setData(), so a
      request carrying id=<existing> overwrote another customer's message and
      hint, parent_id, is_private and assign[...] were attacker-controlled.

      Add Model\Message\InputFilter that keeps an explicit field list, move the
      sanitizing there, and make Answer/Save enforce the guest/customer answer
      settings and only accept a visible top-level question as parent.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 0c253c

    • fix(acl): enforce ACL on mass actions, grids and delete (#75)

      Mass delete/enable/disable/status, the grid and assign endpoints had no
      ADMIN_RESOURCE and fell back to Magento_Backend::admin, so any admin role
      could use them. Delete actions now require the previously unused
      Swissup_Askit::message_delete resource.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> d85293

  • 1.14.27

swissup / module-easytabs

14 hours ago success
  • head
    • Pick same tab as storefront for duplicate aliases in GraphQL

      Mirror Block\Tabs::_buildTabs(): among tabs passing conditions, prefer
      the requested store view over default store view (0), and within a store
      take the same tab the storefront does.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> 06c0a8

    • Address review: stable parent check, duplicate aliases, real customer group

      - Helper\Product: check parent's children via type instance
      getChildrenIds() instead of Relation::getRelationsByChildren(),
      whose return shape is not a stable API. Drops ObjectManager usage.
      - GraphQL data provider: with duplicate aliases, return first tab that
      passes conditions instead of failing on the highest-priority one.
      - GraphQL resolver: load caller's customer group via customer repository
      instead of defaulting to guest when context has no customer_group_id
      (guest fallback exposed "not in group X" tabs on older Magento).

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> 3871b9

    • Fix product visibility bypass and GraphQL tab conditions bypass

      - Helper\Product::canShow(): parent_id fallback now applies only to
      enabled products and only when parent_id is their real parent
      (catalog_product_relation). Previously any visible product id in
      parent_id exposed disabled products via the easytabs AJAX endpoint.
      - GraphQL getEasyTab now validates tab conditions (customer group, etc.)
      using the customer group from GraphQL context, like the storefront does.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> 3a8cc0

  • 1.13.5
    • Version 1.13.5 0b1250

    • Fix ARIA tablist in expanded and accordion layouts 4f7a83

swissup / module-ajaxpro

14 hours ago success
  • head
    • fix(security): do not expose url of disabled products in add-to-cart response (#71)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> cf9457

    • fix(security): do not render disabled or foreign-website products in ajaxpro-product section (#71)

      The section loaded any id from ajaxpro[product_id] and rendered the full
      product view. Require enabled, visible and assigned to the current website;
      also ignore non-scalar ids instead of throwing a TypeError.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 904572

    • fix(security): accept only base64url uenc from ajax requests (#71)

      Core templates print uenc without escaping, so a raw request value broke
      out of the hidden input attribute (reflected XSS, cacheable via FPC).

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 4751d0

    • fix(security): do not expose url of disabled products in add-to-cart response (#71)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> bf10ea

    • fix(security): do not render disabled or foreign-website products in ajaxpro-product section (#71)

      The section loaded any id from ajaxpro[product_id] and rendered the full
      product view. Require enabled, visible and assigned to the current website;
      also ignore non-scalar ids instead of throwing a TypeError.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 5b03e7

    • fix(security): accept only base64url uenc from ajax requests (#71)

      Core templates print uenc without escaping, so a raw request value broke
      out of the hidden input attribute (reflected XSS, cacheable via FPC).

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 70e310

    • fix(security): do not render disabled or foreign-website products in ajaxpro-product section (#71)

      The section loaded any id from ajaxpro[product_id] and rendered the full
      product view. Require enabled, visible and assigned to the current website;
      also ignore non-scalar ids instead of throwing a TypeError.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> b33d87

    • fix(security): do not render disabled or foreign-website products in ajaxpro-product section (#71)

      The section loaded any id from ajaxpro[product_id] and rendered the full
      product view. Require enabled, visible and assigned to the current website;
      also ignore non-scalar ids instead of throwing a TypeError.

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> b22c5c

    • fix(security): accept only base64url uenc from ajax requests (#71)

      Core templates print uenc without escaping, so a raw request value broke
      out of the hidden input attribute (reflected XSS, cacheable via FPC).

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> caf378

  • 1.7.43
    • Version 1.7.43 4bb05d

    • docs: say where the fixers of the section markup live (#66) (#70)

      Co-authored-by: Claude Opus 5 <noreply@anthropic.com> a79b65

    • docs: say where the fixers of the section markup live (#66)

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> a891e2

    • docs: say where the fixers of the section markup live (#66)

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 38ebf8

    • fix(cart): keep popup image styles off the listing thumbnails (#66) (#67)

      * fix: keep cart popup image styles off the listing thumbnails (#66)

      image_with_borders.phtml renders every product image with a
      `product-image-container-<productId>` class and emits a global `<style>`
      sizing that class. The ajaxpro-cart section shows the product that has just
      been added - the very one the listing behind the popup shows - and its markup
      lands in the document after that listing, so its rules won and the listing
      tile took the cart thumbnail size: a 240x300 tile collapsed to 165x165.

      #61 fixed this for the ajaxpro-product section only. The cart markup now goes
      through the same ProductImageStyles::isolate(), both for `checkout.cart` (every
      handle, with or without a quote) and for `checkout.cart.fixes`. The `reinit`
      markup has no product images and is left alone.

      isolate() now skips any class that already carries a prefix, not only its own:
      Swissup_Suggestpage isolates the markup of its block with a `suggestpage-`
      prefix, and that block is what `checkout.cart` renders under the
      `ajaxpro_popup_suggestpage_view` handle. Its classes and selectors keep naming
      each other instead of being prefixed twice.

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

      * refactor: run section markup through a fixer pipeline (#66)

      Calling the isolator by hand at every place that renders markup is a patch in
      three files that the next section will forget again. The rule is not "the cart
      needs isolating" - it is "nothing this module injects into an already rendered
      page may restyle that page", and it belongs where that markup leaves the
      module.

      AbstractSectionData::getBlockHtml() is that one door: every section (Init,
      Cart, Product) renders through it, and there is no other out-of-band render
      point in the module. It now runs the markup through a FixerInterface, injected
      through the constructor.

      The fixers themselves are declared in di.xml and composed by FixerPool, which
      is a FixerInterface too - so the sections depend on the pipeline, not on what
      is in it, and adding, reordering or removing a pass is a di.xml change. The
      isolator becomes Html\Fixer\ProductImageStyles, the first entry in the
      pipeline; Model\View\ProductImageStyles stays as a deprecated alias.

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

      ---------

      Co-authored-by: Claude Opus 5 <noreply@anthropic.com> 2fe057

    • fix(popup): drop the gallery skip links from the Quick View markup (#68) (#69)

      The popup renders a second copy of the product view into a page that already
      has one, so every DOM id in it is duplicated. Measured on 2.4.8 sample data,
      8 ids really collide in the live DOM (the 11 map-popup-* ones sit inside
      Magento_Msrp's text/x-magento-template bodies and are never parsed).

      Almost all of that is harmless: the popup markup is appended last, so
      getElementById and jQuery's $('#id') fast path always return the page's copy,
      and the widgets that matter - catalogAddToCart, mage.productValidate - bind to
      this.element and serialise that form instead of looking the id up globally.
      Add-to-cart, qty, swatches and price stay correct on both sides, verified for a
      simple product over a simple page and a configurable over a configurable one.

      The exception is gallery-next-area / gallery-prev-area. They are not widget
      hooks but skip-link targets, and the popup carries both the <a href="#..."> and
      its own <a id="..."> target. A fragment resolves to the first match - the
      page's - so activating "Skip to the end of the images gallery" inside the modal
      moves focus out of the dialog into the page behind it.

      Remove them in the popup handle rather than renaming ids in the payload. A
      rename would have to rewrite the x-magento-init selector keys in the same
      payload in lockstep, and missing them would cost the popup form its
      validate-product widget - worse than the bug it fixes. Skip links are a
      page-scoped affordance anyway: the modal does its own focus management and has
      no gallery to skip past.

      Co-authored-by: Claude Opus 5 <noreply@anthropic.com> 380135

    • fix(popup): drop the gallery skip links from the Quick View markup (#68)

      The popup renders a second copy of the product view into a page that already
      has one, so every DOM id in it is duplicated. Measured on 2.4.8 sample data,
      8 ids really collide in the live DOM (the 11 map-popup-* ones sit inside
      Magento_Msrp's text/x-magento-template bodies and are never parsed).

      Almost all of that is harmless: the popup markup is appended last, so
      getElementById and jQuery's $('#id') fast path always return the page's copy,
      and the widgets that matter - catalogAddToCart, mage.productValidate - bind to
      this.element and serialise that form instead of looking the id up globally.
      Add-to-cart, qty, swatches and price stay correct on both sides, verified for a
      simple product over a simple page and a configurable over a configurable one.

      The exception is gallery-next-area / gallery-prev-area. They are not widget
      hooks but skip-link targets, and the popup carries both the <a href="#..."> and
      its own <a id="..."> target. A fragment resolves to the first match - the
      page's - so activating "Skip to the end of the images gallery" inside the modal
      moves focus out of the dialog into the page behind it.

      Remove them in the popup handle rather than renaming ids in the payload. A
      rename would have to rewrite the x-magento-init selector keys in the same
      payload in lockstep, and missing them would cost the popup form its
      validate-product widget - worse than the bug it fixes. Skip links are a
      page-scoped affordance anyway: the modal does its own focus management and has
      no gallery to skip past.

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 475db9

    • refactor: run section markup through a fixer pipeline (#66)

      Calling the isolator by hand at every place that renders markup is a patch in
      three files that the next section will forget again. The rule is not "the cart
      needs isolating" - it is "nothing this module injects into an already rendered
      page may restyle that page", and it belongs where that markup leaves the
      module.

      AbstractSectionData::getBlockHtml() is that one door: every section (Init,
      Cart, Product) renders through it, and there is no other out-of-band render
      point in the module. It now runs the markup through a FixerInterface, injected
      through the constructor.

      The fixers themselves are declared in di.xml and composed by FixerPool, which
      is a FixerInterface too - so the sections depend on the pipeline, not on what
      is in it, and adding, reordering or removing a pass is a di.xml change. The
      isolator becomes Html\Fixer\ProductImageStyles, the first entry in the
      pipeline; Model\View\ProductImageStyles stays as a deprecated alias.

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> 10292c

    • fix: keep cart popup image styles off the listing thumbnails (#66)

      image_with_borders.phtml renders every product image with a
      `product-image-container-<productId>` class and emits a global `<style>`
      sizing that class. The ajaxpro-cart section shows the product that has just
      been added - the very one the listing behind the popup shows - and its markup
      lands in the document after that listing, so its rules won and the listing
      tile took the cart thumbnail size: a 240x300 tile collapsed to 165x165.

      #61 fixed this for the ajaxpro-product section only. The cart markup now goes
      through the same ProductImageStyles::isolate(), both for `checkout.cart` (every
      handle, with or without a quote) and for `checkout.cart.fixes`. The `reinit`
      markup has no product images and is left alone.

      isolate() now skips any class that already carries a prefix, not only its own:
      Swissup_Suggestpage isolates the markup of its block with a `suggestpage-`
      prefix, and that block is what `checkout.cart` renders under the
      `ajaxpro_popup_suggestpage_view` handle. Its classes and selectors keep naming
      each other instead of being prefixed twice.

      Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> c6fd19

swissup / module-highlight

15 hours ago success
  • head
    • Security: sign carousel block data, harden GraphQL resolver (#28)

      * Security: sign carousel block data, harden GraphQL resolver

      - Sign block_data passed to highlight/carousel/slide with HMAC (crypt key)
      and reject unsigned/tampered requests. Previously any visitor could pick
      an arbitrary template, rule conditions, products_count, etc.
      - Restrict block type to Swissup\Highlight\Block\ namespace.
      - GraphQL getHighlightProducts: cap pageSize at 100, whitelist period,
      allow conditions only by storefront-exposed attributes, and stop leaking
      raw exception messages.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> 16fe35

    • Clarify that block data signature is a keyed HMAC

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> fd51ea

    • Security: sign carousel block data, harden GraphQL resolver

      - Sign block_data passed to highlight/carousel/slide with HMAC (crypt key)
      and reject unsigned/tampered requests. Previously any visitor could pick
      an arbitrary template, rule conditions, products_count, etc.
      - Restrict block type to Swissup\Highlight\Block\ namespace.
      - GraphQL getHighlightProducts: cap pageSize at 100, whitelist period,
      allow conditions only by storefront-exposed attributes, and stop leaking
      raw exception messages.

      Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> 4340ee

swissup / module-ajaxsearch

15 hours ago success
  • 1.14.12
    • Version 1.14.12 2bd375

    • fix(autocomplete): honor display_in_terms for popular terms (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> a0c591

    • fix(frontend): escape product, category and page titles in suggestions (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 5b5451

    • fix(graphql): log short-query attempts at debug without args and user agent (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 7fc1e6

    • fix(category-options): do not disclose disabled categories (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 785c7f

    • fix(autocomplete): honor display_in_terms for popular terms (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> c04e7b

    • fix(frontend): escape product, category and page titles in suggestions (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> ec632d

    • fix(graphql): log short-query attempts at debug without args and user agent (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 655e16

    • fix(category-options): do not disclose disabled categories (#58)

      Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> 79c6ba

    • Fixed CLS caused by close button on Luma+Breeze f156b4

  • 1.14.11
    • Version 1.14.11 0db900

    • Fixed layout shift when result list is long 8715cc

    • Fixed overflowed content in Firefox (Apollo theme) de660b