
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
@joewagner/mockipfs
Advanced tools
Part of HTTP Toolkit: powerful tools for building, testing & debugging HTTP(S), Ethereum, IPFS, and more
MockIPFS lets you build a fake IPFS node, or proxy traffic to a real IPFS node, and inspect & mock the interactions made by your IPFS API client (e.g. ipfs-http-client).
:warning: MockIPFS is still new & rapidly developing! :warning:
Everything described here works today, but there's lots more to come, and some advanced use cases may run into rough edges. If you hit any problems or missing features, please open an issue.
More specifically, MockIPFS lets you:
Let's write our first automated test with MockIPFS. To test IPFS-based code, you will typically need to:
A simple example of that, testing code based on ipfs-http-client, might look like this:
// Standard packages to make IPFS requests:
import * as IPFS from "ipfs-http-client";
import itAll from 'it-all';
import {
concat as uint8ArrayConcat,
toString as uint8ToString
} from 'uint8arrays';
// Import MockIPFS and create a fake node:
import * as MockIPFS from 'mockipfs'
const mockNode = MockIPFS.getLocal();
describe("MockIPFS", () => {
// Start & stop your mock node to reset state between tests
beforeEach(() => mockNode.start());
afterEach(() => mockNode.stop());
it("lets you mock behaviour and assert on node interactions", async () => {
const ipfsPath = "/ipfs/a-fake-IPFS-id";
// Mock some node endpoints:
await mockNode.forCat(ipfsPath).thenReturn("Mock content");
// Lookup some content with a real IPFS client:
const ipfsClient = IPFS.create(mockNode.ipfsOptions);
const content = await itAll(ipfsClient.cat(ipfsPath));
// Assert on the response:
const contentText = uint8ToString(uint8ArrayConcat(content));
expect(contentText).to.equal("Mock content");
// Assert that we saw the requests we expected
const catRequests = await mockNode.getQueriedContent();
expect(catRequests).to.deep.equal([
{ path: ipfsPath }
]);
});
});
First, install MockIPFS:
npm install --save-dev mockipfs
Once you've installed the library, you'll want to use it in your test or automation code. To do so you need to:
To create a node in Node.js, you can simply call MockIPFS.getLocal()
and you're done.
In many cases though, to test a web application you'll want to run your tests inside a browser, and create & manage your mock IPFS node there too. It's not possible to launch a node from inside a browser, but MockIPFS provides a separate admin server you can run, which will host your mock IPFS node externally.
Once your admin server is running, you can use the exact same code as for Node.js, but each method call is transparently turned into a remote-control call to the admin server.
To do this, you just need to run the admin server before you start your tests, and stop it afterwards. You can do that in one of two ways:
mockipfs -c <your test command>
This will start & stop the admin server automatically before and after your tests.import * as MockIPFS from 'mockipfs';
const adminServer = MockIPFS.getAdminServer();
adminServer.start().then(() =>
console.log('Admin server started')
);
Note that as this is a universal library (it works in Node.js & browsers) this code does reference some Node.js modules & globals in a couple of places. If you're using MockIPFS from inside a browser, this needs to be handled by your bundler. In many bundlers this will be handled automatically, but if it's not you may need to enable node polyfills for this. In Webpack that usually means enabling node-polyfill-webpack-plugin, or in ESBuild you'll want the @esbuild-plugins/node-modules-polyfill
and @esbuild-plugins/node-globals-polyfill
plugins.
Once you have an admin server running, you can call MockIPFS.getLocal()
in the browser in exactly the same way as in Node.js, and it will automatically find & use the local admin server to create your mock IPFS node.
Nodes expose .start()
and .stop()
methods to start & stop the node. You should call .start()
before you use the node, call .stop()
when you're done with it, and in both cases wait for the promise that's returned to ensure everything is completed before continuing.
In automation, you'll want to create the node and start it immediately, and only stop it at shutdown. In testing environments it's usually better to start & stop the node between tests, like so:
import * as MockIPFS from 'mockipfs';
const mockNode = MockIPFS.getLocal();
describe("A suite of tests", () => {
beforeEach(async () => {
await mockNode.start();
});
afterEach(async () => {
await mockNode.stop();
});
it("A single test", () => {
// ...
});
});
To use your MockIPFS node instead of connecting to a real IPFS node and the real network, you just need to use the mock node's address for your IPFS HTTP client. For ipfs-http-client
you can pass the exposed ipfsOptions
object directly, like so:
import * as IPFS from "ipfs-http-client";
const ipfsClient = IPFS.create(mockNode.ipfsOptions);
// Now use ipfsClient as normal, and all interactions will be sent to the mock node instead of
// any real IPFS node, and so will not touch the real IPFS network (unless you explicitly proxy
// them - see 'Proxying IPFS Traffic' below).
MockIPFS provides an API that defines rules for interactions with the IPFS node. For example, you can:
ipfs cat
data, so real IPFS API calls return custom content for any id you request.ipfs cat
for a given CID times out entirely.All in an isolated environment that can be set up & torn down in <1ms.
MockIPFS allows you to define any behaviours you like for a wide variety of IPFS interactions, to simulate everything from normal IPFS add/cat interactions, to tricky to test failure cases (like timeouts or unpinned content disappearing due to garbage collection as your code runs), to scenarios that are difficult to impossible to intentionally create with real IPFS (incorrect content for a hash, low-level connection errors, or )
By default, for all supported 'submit' methods in IPFS (e.g. ipfs add
and ipfs name publish
) MockIPFS will accept the submitted data, record the request, and send a successful response, but without changing any real state. Meanwhile all query methods behave by default as if the requested content was not found/not available for all requests.
To change this, you define mock rules, with a chain of method calls on your MockIPFS node (typically created with MockIPFS.getLocal()
).
Defining a rule always starts with mockNode.forX()
for some X that you want to change (e.g. .forCat('QmcKQ...')
), followed by further calls for advanced configuration, and ending with a .thenY()
method for some Y result you want to set.
For example, you can call mockNode.forCat('QmcKQ...').thenReturn('Fake IPFS content')
to
After calling any .thenY()
method, the new rule will take effect. In Node.js with a local node this happens synchronously, while in browsers or using remote mock nodes this may be asynchronous. All methods return promises regardless, so that you can easily write consistent await-based code that works in both environments.
The full list of methods available is:
forCat(ipfsPath?: string)
- Mock ipfs cat
for a specific path (or all paths, if no path is provided)
thenReturn(rawData: string
) - The mock data to returnthenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforGet(ipfsPath?: string)
- Mock ipfs get
for a specific path (or all paths, if no path is provided)
thenReturn(rawData: string
) - The mock data to returnthenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforAdd()
- Mock content publishing (ipfs add
) for all content
thenAcceptPublish()
- Return a successful result with mock CID valuesthenAcceptPublishAs(result: string | Array<string | { Name, Hash, Size? }>)
- Return a successful result with the given result valuesthenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforAddIncluding(...content: Array<string | Uint8Array | { path, content? })
- Mock IPFS content publishing for all matching requests.
forAdd()
are supported.forNameResolve(ipnsName?: string)
- Mock IPNS name resolution for a given name (or all names, if no name is provided)
thenResolveTo(path: string)
- Return a successful paththenFailToResolve()
- Return a 'not found' failurethenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforNamePublish(ipfsName?: string)
- Mock IPNS name publishing for a given name (or all names, if no name is provided)
withContent(cid)
- Make this rule match only requests that publish this CIDthenAcceptPublish()
- Return a successful result with a mock namethenAcceptPublishAs(name: string)
- Return a successful result with the given namethenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforPinAdd(cid?: string)
- Mock pin addition for a given CID (or all CIDs, if no CID is provided)
thenPinSuccessfully()
- Returns a successful result for the pinned contentthenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforPinRemoteAdd(cid?: string)
- Mock remote pin addition for a given CID (or all CIDs, if no CID is provided)
thenPinSuccessfully()
- Returns a successful result for the pinned contentthenFailWith(err: Error | string)
- fail with the provided Error instance, or an Error with the provided messagethenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforPinRm(cid?: string)
- Mock pin removal for a given CID (or all CIDs, if no CID is provided)
thenRemoveSuccessfully()
- Return a successful removal result for the contentthenFailAsMissing()
- Return an error, as if the content was not currently pinnedthenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforPinLs(cid?: string)
- Mock pin listing for a given CID (or all CIDs, if no CID is provided)
thenReturn(values: Array<{ type, cid }>)
- Return a given list of pins.thenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorforPinRemoteLs()
- Mock listing registered remote pinning services
thenReturn(values: Array<{ service: string, endpoint: URL, stat?: Stat }>)
- Return a given list of remote services.thenTimeout()
- Wait forever, returning no responsethenCloseConnection()
- Kills the TCP connection, causing a network errorOnce you have made some IPFS requests to your mock IPFS node, you can then query the seen requests using a few methods:
mockNode.getQueriedContent()
mockNode.getAddedContent()
mockNode.getIPNSQueries()
mockNode.getIPNSPublications()
mockNode.getAddedPins()
mockNode.getRemovedPins()
Each of these will return an array summarizing the details of the calls made since the node was started, allowing you to assert on all IPFS interactions made during your tests.
MockIPFS can also proxy IPFS traffic to a real IPFS node. This allows two advanced use cases:
To do this, pass unmatchedRequests: { proxyTo: "a-real-ipfs-node-HTTP-url" }
as an option when creating your mock IPFS node. This will disable the default stub responses, and proxy all unmatched requests to the given node instead. For example:
import * as MockIPFS from 'mockipfs'
const mockNode = MockIPFS.getLocal({
unmatchedRequests: { proxyTo: "http://localhost:5001" }
});
mockNode.start();
This only changes the unmatched request behaviour, and all other methods will continue to define behaviour and query seen request data as normal.
For more details, see the MockIPFS reference docs.
This project has received funding from the European Union’s Horizon 2020 research and innovation programme within the framework of the NGI-POINTER Project funded under grant agreement No 871528.
FAQs
Powerful friendly IPFS mock node & proxy
We found that @joewagner/mockipfs 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.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.