
Research
/Security News
10 npm Typosquatted Packages Deploy Multi-Stage Credential Harvester
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.
@code-dot-org/snack-sdk
Advanced tools
The Expo Snack SDK. Use this to create a custom web interface for https://snack.expo.io/. For brevity, these docs assume you are targeting the most recent version of Expo SDK
Have a problem with the code in this repository? Please file issues & bug reports at https://github.com/expo/expo. Thanks!
Interested in integrating the Snack SDK into your project? Check out https://forums.expo.io/c/snack for assistance and to receive updates about any upgrades to the SDK.
import { SnackSession } from 'snack-sdk';
let session = new SnackSession({
files?: ExpoSnackFiles,
sdkVersion?: SDKVersion,
sessionId?: string,
verbose?: boolean,
});
await session.startAsync();
files is a map of all of the files included in the project. The filenames should be the full path from the project root.
sdkVersion determines what version of React Native is used on the mobile client. Defaults to 15.0.0 which maps to React Native 0.42.0. If you specify a different version, make sure to save that version along with the code. Code from one SDK version is not guaranteed to work on others. To use multiple files, you must use SDK 21 or above.
sessionId can be specified if you want a consistent url. This is a global namespace so make sure to use a UUID or scope it somehow if you use this.
let url = await session.getUrlAsync();
This url will open the current Snack Session in the Expo client when opened on a phone. You can create a QR code from this link or send it to the phone in another way. See example/ for how to turn this into a QR code.
The Expo App includes an ID displayed at the bottom of the Projects tab. Once this ID has been reported to the session, your project will appear in the "Recently in Development" section on the Projects tab for as long as the session remains active.
Example:
session.setDeviceId('XXXX-XXXX')
const files = {
'App.js': { contents: 'code here, this is entry point', type: 'CODE'},
'folder/file.js': { contents: 'this file is in /folder', type: 'CODE'},
'image.png': { content: 'remote location of asset', type: 'ASSET'},
}
await session.sendCodeAsync(files: Object);
This will push the new code to each connected mobile client. Any new clients that connect will also get the new code.
For SDK 21 and above, you can have multiple files in a Snack. This includes support for folders and relative path imports. See the example above on sending data. The entry point for running a Snack is app.js, which is required to be included in any project provided to the SnackSession constructor or sendCodeAsync calls.
You'll also be able to send Assets (images, fonts, etc.). To do this include the asset in the files object, with key being file name and value being the remote location where this asset is stored.
const remoteAddressOfAssetFile = await session.uploadAssetAsync(file);
where file is a Javascript File object.
When using Exp SDK 25 and above, you'll be able to specify arbitrary NPM modules to use with your app.
Example:
await session.addDependencies({"lodash": "4.17.10"}); // add a specific version
await session.addDependencies({"lodash": "*"}); // or resolve to the most recent
To detect all dependencies needed to evaluate a file,
Example:
import { dependencyUtils } from 'snack-sdk';
let dependencies = Object.keys(dependencyUtils.findModuleDependencies('import lodash from "lodash";'));
// ['lodash']
When developing a project on your local file system, you are probably used to:
npm install which will copy the published package to your local filesystemwhich means that once we add lodash to our dependencies these are the same
import zip from 'lodash/zip';
import { zip } from 'lodash';
With Snack, we have prioritized taking an arbitrary dependency and have it available in the running experience as quickly as possible. To achieve that, only the parts of the package that are exported in the module's main entry point are available. Tthe process looks like:
session.addDependency({'my-package': 'version'})which means that
import zip from 'lodash/zip';
is not available once you import lodash You will need to import 'lodash/zip' to a new package, which will give you a package identical to lodash-zip
let saveResult = await session.saveAsync();
console.log(saveResult);
// This will print: `{"id":"abc123","url":"https://expo.io/@snack/abc123"}`
This will upload the current code to Expo's servers and return a url that points to that version of the code.
const downloadURL = await session.downloadAsync();
console.log(downloadURL);
// This will print: { url: "https://expo.io/--/api/v2/snack/download/snackIDHere" }
This will return a link to our server with a .zip of your Snack project. You'll be able to run this exported project using exp or XDE.
Here are the Flow types for the error, log, and presence listeners:
type SnackSession = {
addErrorListener: (listener: ExpoErrorListener) => ExpoSubscription,
addLogListener: (listener: ExpoLogListener) => ExpoSubscription,
addPresenceListener: (listener: ExpoPresenceListener) => ExpoSubscription,
};
type ExpoSubscription = {
remove: () => void,
};
// Called with empty array if errors have been resolved
type ExpoErrorListener = (errors: Array<ExpoError>) => void;
type ExpoLogListener = (log: ExpoDeviceLog) => void;
type ExpoPresenceListener = (event: ExpoPresenceEvent) => void;
type ExpoError = {
device?: ExpoDevice,
startLine?: number,
endLine?: number,
startColumn?: number,
endColumn?: number,
message: string,
stack?: string,
};
// `console.log`, `console.warn`, `console.error`
type ExpoDeviceLog = {
device: ExpoDevice,
method: 'log' | 'warn' | 'error',
message: string,
arguments: any, // the raw fields that were passed to the console.* call
};
type ExpoDevice = {
name: string,
id: string,
};
type ExpoPresenceStatus = 'join' | 'leave';
type ExpoPresenceEvent = {
device: ExpoDevice,
status: ExpoPresenceStatus,
};
Each of these listeners is optional. Here's an example of using a log listener:
let logSubscription = session.addLogListener((log) => {
console.log(JSON.stringify(log));
// This will print: `{"device":{"id":"b9384faf-504f-4c41-a7ab-6344f0102456","name":"SM-G930U"},"method":"log","message":"hello!","arguments":["hello!"]}`
// on the web if `console.log('hello!')` is run from the code on the phone.
});
// later on...
logSubscription.remove();
// future `console.log`s on the phone will not trigger the listener
An error listener:
let errorSubscription = session.addErrorListener((error) => {
console.log(JSON.stringify(error));
// This will print:
// `[]`
// when there is no error and
// `[{"message":"unknown: Unexpected token (7:7)...","endLine":7,"startLine":7,"endColumn":7,"startColumn":7}]`
// when there is an error. The `message` field is truncated in this document.
});
// later on...
errorSubscription.remove();
A presence listener:
let presenceSubscription = session.addPresenceListener((presence) => {
console.log(JSON.stringify(presence));
// This will print:
// `{"device":{"id":"b9384faf-504f-4c41-a7ab-6344f0102456","name":"SM-G930U"},"status":"join"}`
// when a device is connected and
// `{"device":{"id":"b9384faf-504f-4c41-a7ab-6344f0102456","name":"SM-G930U"},"status":"leave"}`
// when a device disconnects.
});
// later on...
presenceSubscription.remove();
Please read the Flow types above for all possible fields returned in these listeners.
await session.stopAsync();
This will shut down the PubNub connection.
FAQs
The Expo Snack SDK
We found that @code-dot-org/snack-sdk demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 12 open source maintainers 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.

Research
/Security News
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authorities’ publishing activity, highlighting trends and transparency across the CVE ecosystem.