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.
Many organizations and individuals rely on automatic notifications across various contexts in their daily operations. With BNS, we aim to provide an open-source platform that empowers users to create customized notification systems tailored to their unique requirements. BNS consists of a series of abstract components designed to facilitate the creation of diverse use cases, regardless of context.
The underlying idea is to develop generic components that can serve a wide range of needs, this approach ensures that all members of the community can leverage the platform's evolving suite of components and use cases to their advantage.
Install the gem and add to the application's Gemfile by executing:
$ bundle add bns
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install bns
The gem provides with basic interfaces, types, and methods to shape your own use cases in an easy way.
There are 2 currently implemented use cases:
For this example we'll analize the birthday notification use case, bringing data from a notion database, and dispatching the notifications to a Discord channel.
A Use Case object, consists on 4 main componenets, having it's own responsability:
Specifically, a fetcher is an object in charged of bringing data from a data source. The gem already provides the base interface for building your own fetcher for your specific data source, or rely on already built classes if they match your purpose.
The base interface for a fetcher can be found under the bns/fetcher/base.rb
class. Since this is a implementation of the Fetcher::Base
for bringing data from a Notion database, it was created on a new namespace for that data source, it can be found under
/bns/fetcher/notion/birthday.rb
. It implements specific logic for fetching the data and validating the response.
The Mapper responsability, is to shape the data using custom types from the app domain, bringing it into a common structure understandable for other components, specifically the Formatter.
Because of the use case, the Mapper implementation for it, relyes on specific types for representing a Birthday it self. It can be found
under /bns/mapper/notion/birthday.rb
The Formatter, is in charge of preparing the message to be sent in our notification, and give it the right format with the right data.
The template or 'format' to be used should be included in the use case configurations, which we'll review in a further step. It's
implementation can be found under /bns/formatter/discord/birthday.rb
.
Finally, the Dispatcher basically, sends or dispatches the formatted message into a destination, since the use case was implemented for
Discord, it implements specific logic to communicate with a Discord channel using a webhook. The webhook configuration and name for the 'Sender'
in the channel should be provided with the initial use case configurations. It can be found under /bns/dispatcher/discord/implementation.rb
In this example, we demonstrate how to instantiate a birthday notification use case and execute it in a basic Ruby project. We'll also cover its deployment in a serverless configuration, specifically using a simple Lambda deployment.
We'll need some configurations for this specific use case:
Complete Name (text) | BD_this_year (formula) | BD (date) |
---|---|---|
John Doe | January 24, 2024 | January 24, 2000 |
Jane Doe | June 20, 2024 | June 20, 2000 |
With the following formula for the BD_this_year column: dateAdd(prop("BD"), year(now()) - year(prop("BD")), "years")
A Notion secret, which can be obtained, by creating an integration here: https://developers.notion.com/
, browsing on the View my integations option, and selecting the New Integration or Create new integration buttons.
A webhook key, which can be generated directly on discord on the desired channel, following this instructions: https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks
A filter, to determine which data to bring from the database, for this specific case, the filter we used is:
#### file.rb ####
today = Date.now
{
"filter": {
"or": [
{
"property": "BD_this_year",
"date": {
"equals": today
}
}
]
},
"sorts": []
}
"NAME, Wishing you a very happy birthday! Enjoy your special day! :birthday: :gift:"
To instantiate the use case, the UseCases
module should provide a method for this purpose, for this specific case, the notify_birthday_from_notion_to_discord
is the desired one.
Normal ruby code
filter = {
"filter": {
"or": [
{
"property": "BD_this_year",
"date": {
"equals": today
}
}
]
},
"sorts": []
}
options = {
fetch_options: {
base_url: "https://api.notion.com",
database_id: NOTION_DATABASE_ID,
secret: NOTION_API_INTEGRATION_SECRET,
filter: filter
},
dispatch_options: {
webhook: "https://discord.com/api/webhooks/1199213527672565760/KmpoIzBet9xYG16oFh8W1RWHbpIqT7UtTBRrhfLcvWZdNiVZCTM-gpil2Qoy4eYEgpdf",
name: "Birthday Bot"
}
}
use_case = UseCases.notify_birthday_from_notion_to_discord(options)
use_case.perform
We'll explain how to configure and deploy a use case with serverless, this example will cover the PTO's notifications use case.
Create the environment variables configuration file.
cp env.yml.example env.yml
And put the following env variables
dev:
NOTION_DATABASE_ID: NOTION_DATABASE_ID
NOTION_SECRET: NOTION_SECRET
DISCORD_WEBHOOK: DISCORD_WEBHOOK
DISCORD_BOT_NAME: DISCORD_BOT_NAME
prod:
NOTION_DATABASE_ID: NOTION_DATABASE_ID
NOTION_SECRET: NOTION_SECRET
DISCORD_WEBHOOK: DISCORD_WEBHOOK
DISCORD_BOT_NAME: DISCORD_BOT_NAME
The variables should be defined either in the custom settings section within the serverless.yml
file to ensure accessibility by all lambdas, or in the environment configuration option for each lambda respectively. For example:
# Accessible by all the lambdas
custom:
settings:
api:
NOTION_DATABASE_ID: ${file(./env.yml):${env:STAGE}.NOTION_DATABASE_ID}
NOTION_SECRET: ${file(./env.yml):${env:STAGE}.NOTION_SECRET}}
# Accessible by the lambda
functions:
lambdaName:
environment:
NOTION_DATABASE_ID: ${file(./env.yml):${env:STAGE}.NOTION_DATABASE_ID}
NOTION_SECRET: ${file(./env.yml):${env:STAGE}.NOTION_SECRET}}
the schedule is configured using an environment variable containing the cron configuration. For example:
# env.yml file
SCHEDULER: cron(0 13 ? * MON-FRI *)
# serverless.yml
functions:
lambdaName:
events:
- schedule:
rate: ${file(./env.yml):${env:STAGE}.SCHEDULER}
To learn how to modify the cron configuration follow this guide: Schedule expressions using rate or cron
On your serverless configuration, create your lambda function, on your serverless /src
folder.
# frozen_string_literal: true
require 'bns'
# Initialize the environment variables
NOTION_BASE_URL = 'https://api.notion.com'
NOTION_DATABASE_ID = ENV.fetch('PTO_NOTION_DATABASE_ID')
NOTION_SECRET = ENV.fetch('PTO_NOTION_SECRET')
DISCORD_WEBHOOK = ENV.fetch('PTO_DISCORD_WEBHOOK')
DISCORD_BOT_NAME = ENV.fetch('PTO_DISCORD_BOT_NAME')
module Notifier
# Service description
class UseCaseName
def self.notify(*)
options = { fetch_options:, dispatch_options: }
begin
use_case = UseCases.use_case_build_function(options)
use_case.perform
rescue StandardError => e
{ body: { message: e.message } }
end
end
def self.fetch_options
{
base_url: NOTION_BASE_URL,
database_id: NOTION_DATABASE_ID,
secret: NOTION_SECRET,
filter: {
filter: { "and": fetch_filter }
}
}
end
def self.fetch_filter
today = Common::TimeZone.set_colombia_time_zone.strftime('%F').to_s
[
{ property: 'Desde?', date: { on_or_before: today } },
{ property: 'Hasta?', date: { on_or_after: today } }
]
end
def self.dispatch_options
{
webhook: DISCORD_WEBHOOK,
name: DISCORD_BOT_NAME
}
end
def self.format_options
{
template: ':beach: individual_name is on PTO'
}
end
end
end
In the serverless.yml
file, add a new instance in the functions
block with this structure:
functions:
ptoNotification:
handler: src/lambdas/pto_notification.Notifier::PTO.notify
environment:
PTO_NOTION_DATABASE_ID: ${file(./env.yml):${env:STAGE}.PTO_NOTION_DATABASE_ID}
PTO_NOTION_SECRET: ${file(./env.yml):${env:STAGE}.PTO_NOTION_SECRET}
PTO_DISCORD_WEBHOOK: ${file(./env.yml):${env:STAGE}.PTO_DISCORD_WEBHOOK}
PTO_DISCORD_BOT_NAME: ${file(./env.yml):${env:STAGE}.DISCORD_BOT_NAME}
events:
- schedule:
name: pto-daily-notification
description: "Notify every 24 hours at 8:30 a.m (UTC-5) from monday to friday"
rate: cron(${file(./env.yml):${env:STAGE}.PTO_SCHEDULER})
enabled: true
Configure the AWS keys:
serverless config credentials --provider aws --key YOUR_KEY --secret YOUR_SECRET
Deploy the project:
STAGE=prod sls deploy --verbose
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Features and bug fixes are listed in the CHANGELOG file.
We welcome everyone to contribute. Make sure you have read the CODE_OF_CONDUCT before.
For information on how to contribute, please refer to our CONTRIBUTING guide.
The gem is licensed under an MIT license. See LICENSE for details.
FAQs
Unknown package
We found that bns demonstrated a healthy version release cadence and project activity because the last version was released less than 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.