Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
This library helps to avoid the code smell of "cascading conditionals" (otherwise known as the StreetFighter anti-pattern) by using providing an "Either" data type and interface to your program.
Image from Paul Dragoonis.
In the game Street Fighter, the hero, Ryu, must defeat 10 opponents. We'll just list three for demonstration:
Player info from Strategy Wiki.
Let's say that Ryu must fight the three opponents above in sequence. He continues battling until he is defeated or he defeats all three opponents. As the game programmer, you must either return a structure representing Ryu, unscathed at the end of the fights, or you must return the opponent who defeated Ryu.
A first attempt at modeling this problem may look as follows. We assume there is a function called battle
that returns the winner of a particular fight. Note the "Street Fighter anti-pattern":
def fight
if ryu == battle(ryu, retsu)
if ryu == battle(ryu, geki)
if ryu == battle(ryu, joe)
ryu
else
joe
end
else
geku
end
else
retsu
end
end
It's hard to read, let alone verify that this logic is consistent with the game requirements. Let's try it using the StreetFighter gem. First, we'll create a structure to represent the tournamenters. For now, a simple Struct with a name and a boolean representing whether they're the hero or opponent will suffice:
Player = Struct.new(:name, :hero)
Let's define the hero and three opponents:
ryu = Player.new(:ryu, true) # The hero!
# The bad guys.
retsu = Player.new(:retsu, false)
geki = Player.new(:geki, false)
joe = Player.new(:joe, false)
And the fighting function:
# Perform a random battle, giving the hero 3 chances to win to every
# 1 chance for the opponent. We must return the winner wrapped in a `Left`
# if the winner is the opponent, or a `Right` if the winner is our hero.
def battle(opponent, hero)
winner = ([hero] * 3 << opponent).sample
winner.hero ? StreetFighter::Right.new(winner) :
StreetFighter::Left.new(winner)
end
Finally we define the rounds of the game. We're going to use currying here so that we can apply an opponent to each battle, and wind up with a partially-applied function. Essentially, each partially-applied function can be thought of as an opponent angrily waiting until he has the opportunity to try to do serious damage to our hero, Ryu.
fight = method(:battle).to_proc.curry
I hope you're ready - the fight is about to begin!
winner = StreetFighter.tournament(ryu, fight[retsu], fight[geki], fight[joe])
All that's left is to see the results. Having the result of the computation wrapped in an EitherValue
(Right
or Left
) facilitates using a simple case statement on the return value:
case winner
when StreetFighter::Left
puts "Our hero has been defeated, and #{winner.value.name} is the new champion."
when StreetFighter::Right
puts "Ryu has defeated his opponents!"
end
Fortunately, the output is:
Ryu has defeated his opponents!
Not only has Ryu won this round, but we've completely defeated the cascading conditionals by applying some easy-to-use functional patterns. If you'd like to tournament around with the game and see if Ryu keeps up his winning streak, it's a part of the test suite for this library.
Thanks to Paul Dragoonis for identifying this anti-pattern and giving us a great graphical illustration of the beast in the wild.
FAQs
Unknown package
We found that street_fighter demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.