Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Third-Party Notices

Electron Fiddle incorporates third-party software components under
various licenses. The following notices are provided in compliance
with those licenses.

---

## Socket Firewall (sfw)

- **npm package (`sfw`)**: <https://www.npmjs.com/package/sfw>
- **License**: MIT
- **Copyright**: Socket Inc.

The `sfw` npm package is an MIT-licensed wrapper that downloads and
manages the Socket Firewall Free binary.

---

## Socket Firewall Free (sfw-free)

- **Source**: <https://github.com/SocketDev/sfw-free>
- **License**: PolyForm Shield License 1.0.0
- **Copyright**: Socket Inc.

The `sfw-free` binary, downloaded at runtime by the `sfw` npm
package, is licensed under the PolyForm Shield License 1.0.0.
The full license text is available at:
<https://polyformproject.org/licenses/shield/1.0.0/>

### PolyForm Shield License 1.0.0

> ## Acceptance
>
> In order to get any license under these terms, you must agree
> to them as both strict obligations and conditions to all
> your licenses.
>
> ## Copyright License
>
> The licensor grants you a copyright license for the
> software to do everything you might do with the software
> that would otherwise infringe the licensor's copyright
> in it for any permitted purpose. However, you may
> only distribute the software according to Distribution
> License and make changes or new works based on the software
> according to Changes and New Works License.
>
> ## Distribution License
>
> The licensor grants you an additional copyright license
> to distribute copies of the software. Your license
> to distribute covers distributing the software with
> changes and new works permitted by Changes and New Works
> License.
>
> ## Notices
>
> You must ensure that anyone who gets a copy of any part of
> the software from you also gets a copy of these terms or the
> URL for them above, as well as copies of any plain-text lines
> beginning with `Required Notice:` that the licensor provided
> with the software.
>
> ## Changes and New Works License
>
> The licensor grants you an additional copyright license to
> make changes and new works based on the software for any
> permitted purpose.
>
> ## Patent License
>
> The licensor grants you a patent license for the software that
> covers patent claims the licensor can license, or becomes able
> to license, that you would infringe by using the software.
>
> ## Noncompete
>
> Any purpose is a permitted purpose, except for providing any
> product that competes with the software or any product the
> licensor or any of its affiliates provides using the software.
>
> ## Competition
>
> Goods and services compete even when they provide functionality
> through different kinds of interfaces or for different technical
> platforms. Applications can compete with services, libraries
> with plugins, frameworks with development tools, and so on,
> even if they're written in different programming languages
> or for different computer architectures. Goods and services
> compete even when provided free of charge. If you market a
> product as a practical substitute for the software or another
> product, it definitely competes.
>
> ## New Products
>
> If you are using the software to provide a product that does
> not compete, but the licensor or any of its affiliates brings
> your product into competition by providing a new version of
> the software or another product using the software, you may
> continue using versions of the software available under these
> terms beforehand to provide your competing product, but not
> any later versions.
>
> ## Discontinued Products
>
> You may begin using the software to compete with a product
> or service that the licensor or any of its affiliates has
> stopped providing, unless the licensor includes a plain-text
> line beginning with `Licensor Line of Business:` with the
> software that mentions that line of business.
>
> ## Sales of Business
>
> If the licensor or any of its affiliates sells a line of
> business developing the software or using the software
> to provide a product, the buyer can also enforce
> Noncompete for that product.
>
> ## Fair Use
>
> You may have "fair use" rights for the software under the
> law. These terms do not limit them.
>
> ## No Other Rights
>
> These terms do not allow you to sublicense or transfer any of
> your licenses to anyone else, or prevent the licensor from
> granting licenses to anyone else. These terms do not imply
> any other licenses.
>
> ## Patent Defense
>
> If you make any written claim that the software infringes or
> contributes to infringement of any patent, your patent license
> for the software granted under these terms ends immediately. If
> your company makes such a claim, your patent license ends
> immediately for work on behalf of your company.
>
> ## Violations
>
> The first time you are notified in writing that you have
> violated any of these terms, or done anything with the software
> not covered by your licenses, your licenses can nonetheless
> continue if you come into full compliance with these terms,
> and take practical steps to correct past violations, within
> 32 days of receiving notice. Otherwise, all your licenses
> end immediately.
>
> ## No Liability
>
> ***As far as the law allows, the software comes as is, without
> any warranty or condition, and the licensor will not be liable
> to you for any damages arising out of these terms or the use
> or nature of the software, under any kind of legal claim.***
5 changes: 4 additions & 1 deletion forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ const config: ForgeConfig = {
packagerConfig: {
name: 'Electron Fiddle',
executableName: 'electron-fiddle',
asar: true,
// Unpack the embedded sfw script so system Node can spawn it — it can't
// be executed from inside an asar archive. The `.webpack` segment must
// be explicit because minimatch globstar skips dot-prefixed directories.
asar: { unpack: '**/.webpack/sfw/**' },
icon: path.resolve(__dirname, 'assets', 'icons', 'fiddle'),
appBundleId: 'com.electron.fiddle',
usageDescription: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"react-mosaic-component": "^4.1.1",
"react-window": "^1.8.10",
"semver": "^7.3.4",
"sfw": "^2.0.4",
"shell-env": "^3.0.1",
"tmp": "0.2.5",
"tslib": "^2.6.0",
Expand Down
2 changes: 1 addition & 1 deletion src/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ declare global {
listener: (types: string, version: string) => void,
): void;
addModules(
{ dir, packageManager }: PMOperationOptions,
{ dir, packageManager, useSocketFirewall }: PMOperationOptions,
...names: Array<string>
): Promise<string>;
arch: string;
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export type IPackageManager = 'npm' | 'yarn';
export interface PMOperationOptions {
dir: string;
packageManager: IPackageManager;
useSocketFirewall?: boolean;
}

export interface GistRevision {
Expand Down Expand Up @@ -243,6 +244,7 @@ export enum GlobalSetting {
isEnablingElectronLogging = 'isEnablingElectronLogging',
isKeepingUserDataDirs = 'isKeepingUserDataDirs',
isPublishingGistAsRevision = 'isPublishingGistAsRevision',
isUsingSocketFirewall = 'isUsingSocketFirewall',
isUsingSystemTheme = 'isUsingSystemTheme',
knownVersion = 'known-electron-versions',
localVersion = 'local-electron-versions',
Expand Down
35 changes: 29 additions & 6 deletions src/main/npm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,45 @@ export async function getIsPackageManagerInstalled(
}
}

/**
* Returns the path to the embedded sfw script.
* The sfw CLI is bundled with the app via webpack CopyPlugin.
* Mirrors the original node_modules/sfw/ layout (dist/sfw.mjs + package.json)
* because sfw.mjs reads "../package.json" at runtime for its version.
* In a packaged app the sfw directory is asar-unpacked (see forge.config.ts)
* so system Node can read it — translate the virtual asar path accordingly.
*/
export function getSfwPath(): string {
return path
.resolve(__dirname, '../sfw/dist/sfw.mjs')
.replace(
`${path.sep}app.asar${path.sep}`,
`${path.sep}app.asar.unpacked${path.sep}`,
);
}

/**
* Installs given modules to a given folder.
*/
export async function addModules(
{ dir, packageManager }: PMOperationOptions,
{ dir, packageManager, useSocketFirewall }: PMOperationOptions,
...names: Array<string>
): Promise<string> {
const cmd = packageManager === 'npm' ? 'npm' : 'yarn';
const args =
const pm = packageManager === 'npm' ? 'npm' : 'yarn';
const pmArgs =
packageManager === 'npm'
? ['install', '-S', ...names]
: names.length > 0
? ['add', ...names]
: ['install'];

return await execFile(dir, cmd, args);
// Use Socket Firewall if enabled
if (useSocketFirewall) {
// Run the embedded sfw script via system node: node sfw.mjs npm install ...
return await execFile(dir, 'node', [getSfwPath(), pm, ...pmArgs]);
}

return await execFile(dir, pm, pmArgs);
}

/**
Expand All @@ -84,9 +107,9 @@ export async function setupNpm() {
IpcEvents.NPM_ADD_MODULES,
(
_: IpcMainInvokeEvent,
{ dir, packageManager }: PMOperationOptions,
{ dir, packageManager, useSocketFirewall }: PMOperationOptions,
...names: Array<string>
) => addModules({ dir, packageManager }, ...names),
) => addModules({ dir, packageManager, useSocketFirewall }, ...names),
);
ipcMainManager.handle(
IpcEvents.NPM_IS_PM_INSTALLED,
Expand Down
4 changes: 2 additions & 2 deletions src/preload/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ export async function setupFiddleGlobal() {
}
},
addModules(
{ dir, packageManager }: PMOperationOptions,
{ dir, packageManager, useSocketFirewall }: PMOperationOptions,
...names: Array<string>
) {
return ipcRenderer.invoke(
IpcEvents.NPM_ADD_MODULES,
{ dir, packageManager },
{ dir, packageManager, useSocketFirewall },
...names,
);
},
Expand Down
42 changes: 40 additions & 2 deletions src/renderer/components/settings-execution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export const ExecutionSettings = observer(
this.handleDeleteDataChange = this.handleDeleteDataChange.bind(this);
this.handleElectronLoggingChange =
this.handleElectronLoggingChange.bind(this);
this.handleSocketFirewallChange =
this.handleSocketFirewallChange.bind(this);

this.handleSettingsItemChange = this.handleSettingsItemChange.bind(this);
this.addNewSettingsItem = this.addNewSettingsItem.bind(this);
Expand Down Expand Up @@ -93,6 +95,16 @@ export const ExecutionSettings = observer(
this.props.appState.isEnablingElectronLogging = checked;
}

/**
* Handles a change on whether or not to use Socket Firewall for npm installs
*/
public handleSocketFirewallChange(
event: React.FormEvent<HTMLInputElement>,
) {
const { checked } = event.currentTarget;
this.props.appState.isUsingSocketFirewall = checked;
}

/**
* Handles a change in the execution flags or environment variables
* run with the Electron executable.
Expand Down Expand Up @@ -243,8 +255,11 @@ export const ExecutionSettings = observer(
}

public render() {
const { isKeepingUserDataDirs, isEnablingElectronLogging } =
this.props.appState;
const {
isKeepingUserDataDirs,
isEnablingElectronLogging,
isUsingSocketFirewall,
} = this.props.appState;

return (
<div>
Expand Down Expand Up @@ -348,6 +363,29 @@ export const ExecutionSettings = observer(
</RadioGroup>
</FormGroup>
</Callout>
<br />
<Callout>
<FormGroup>
<p>
<a
href="https://github.com/SocketDev/sfw-free"
target="_blank"
rel="noreferrer"
>
Socket Firewall
</a>{' '}
protects against supply chain attacks by scanning packages
during installation. When enabled, Fiddle runs npm/yarn installs
through the sfw CLI, which blocks malicious dependencies before
they can execute.
</p>
<Checkbox
checked={isUsingSocketFirewall}
label="Use Socket Firewall for package installation."
onChange={this.handleSocketFirewallChange}
/>
</FormGroup>
</Callout>
</div>
);
}
Expand Down
9 changes: 7 additions & 2 deletions src/renderer/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,12 @@ export class Runner {

const dir = await this.saveToTemp(options);
const packageManager = appState.packageManager;
const useSocketFirewall = appState.isUsingSocketFirewall;

if (!dir) return RunResult.INVALID;

try {
await this.installModules({ dir, packageManager });
await this.installModules({ dir, packageManager, useSocketFirewall });
} catch (error: any) {
console.error('Runner: Could not install modules', error);

Expand Down Expand Up @@ -249,6 +250,7 @@ export class Runner {
pushOutput(`📦 ${strings[0]} current Fiddle...`);

const packageManager = this.appState.packageManager;
const useSocketFirewall = this.appState.isUsingSocketFirewall;
const pmInstalled =
await window.ElectronFiddle.getIsPackageManagerInstalled(packageManager);
if (!pmInstalled) {
Expand All @@ -266,7 +268,10 @@ export class Runner {
if (!dir) return false;

// Files are now saved to temp, let's install Forge and dependencies
if (!(await this.packageInstall({ dir, packageManager }))) return false;
if (
!(await this.packageInstall({ dir, packageManager, useSocketFirewall }))
)
return false;

// Cool, let's run "package"
try {
Expand Down
Loading