2
0

90 Commits

Author SHA1 Message Date
Sergio Padrino
b3b2265ceb Merge pull request #43 from desktop/releases/0.9.11
Bump version to 0.9.11
2025-03-07 15:28:38 +01:00
Sergio Padrino
c8f98cd195 Bump version to 0.9.11 2025-03-07 15:22:49 +01:00
Sergio Padrino
e40bac1b04 Merge pull request #42 from shiftkey/patch-1
Update ci to default to Ubuntu 20.04 for prebuilds
2025-03-07 15:21:28 +01:00
Brendan Forster
81cd97ba31 Update ci to default to Ubuntu 20.04 for prebuilds 2025-02-24 08:10:54 -04:00
Markus Olsson
c8697f12d2 Merge pull request #40 from desktop/releases/v0.9.10
Release v0.9.10
2024-06-04 13:50:29 +02:00
Markus Olsson
8f6ceec51d Bump version 2024-06-04 10:17:10 +02:00
Markus Olsson
34d184fe89 Merge pull request #39 from desktop/invoke-directly
Invoke prebuild directly so we can see what's going on
2024-06-04 10:05:52 +02:00
Markus Olsson
48dcd71dc0 Invoke prebuild directly so we can see what's going on 2024-06-03 17:59:05 +02:00
Markus Olsson
84ce8a80a4 Merge pull request #38 from desktop/fix-include-regex
Fix include regex
2024-06-03 17:57:41 +02:00
Markus Olsson
f3b5b539ee Fix include regex 2024-06-03 17:43:06 +02:00
Markus Olsson
23d3c193e8 Merge pull request #37 from desktop/releases/0.9.9
Release v0.9.9
2024-06-03 17:42:38 +02:00
Markus Olsson
06606c1bf9 Bump version 2024-06-03 17:19:48 +02:00
Markus Olsson
03004d08c2 Merge pull request #36 from desktop/separate-helper-trampoline
Create a separate credential helper trampoline
2024-06-03 17:18:37 +02:00
Markus Olsson
fd06e4cb74 Liiint
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 12:17:18 +02:00
Markus Olsson
458f595f7c Include DESKTOP_TRAMPOLINE_IDENTIFIER alongside environment
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 12:16:25 +02:00
Markus Olsson
70c6ea44ca Use SetEnvironmentVariable on Windows
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:58:08 +02:00
Markus Olsson
eb43b574cb Liiiiiiint
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:48:38 +02:00
Markus Olsson
6052251d32 Test that stdin gets forwarded correctly
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:47:33 +02:00
Markus Olsson
f99853f6f5 Fix test
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:25:59 +02:00
Markus Olsson
e17797f96b We're not proxying identifier any more
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:23:47 +02:00
Markus Olsson
00b634d7c6 Pardon my c
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:23:27 +02:00
Markus Olsson
5634c425d1 Merge branch 'main' into separate-helper-trampoline 2024-06-03 11:21:50 +02:00
Markus Olsson
4d5efafe2b Create credential helper trampoline, send stdin
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:21:37 +02:00
Markus Olsson
f807bd740f Fix import
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:21:34 +02:00
Markus Olsson
50ae7d88df Rename trampoline to desktop-askpass-trampoline
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:21:32 +02:00
Markus Olsson
76a534cf9d Can't share type?
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:21:30 +02:00
Markus Olsson
df291aa89d Try sharing build settings
Co-Authored-By: Sergio Padrino <1083228+sergiou87@users.noreply.github.com>
2024-06-03 11:20:56 +02:00
Markus Olsson
ac8a04f76a Merge pull request #34 from desktop/remove-unused-env-vars
Remove unused env vars
2024-06-03 10:49:12 +02:00
Markus Olsson
d38f3fbfc1 Merge branch 'main' into remove-unused-env-vars 2024-06-03 10:45:06 +02:00
Sergio Padrino
f7b91a102a Merge pull request #35 from desktop/bump-node
Bump build dependencies
2024-05-31 14:59:18 +02:00
Markus Olsson
2bc283620c Fix friendly name in matrix
Ubuntu 18.04 is deprecated
2024-05-30 14:30:51 +02:00
Sergio Padrino
b6a400eed6 Bump prebuild and prebuild-install? 2024-05-30 13:37:03 +02:00
Sergio Padrino
4fbe92d44a Bump node-gyp to stop depending on distutils 2024-05-30 13:30:36 +02:00
Sergio Padrino
6f160ca8ee Replace distutils with setuptools and packaging 2024-05-30 13:27:14 +02:00
Markus Olsson
993ee45d23 Update ci.yml 2024-05-16 15:48:50 +02:00
Markus Olsson
052e8adec6 I don't know what I'm doing 2024-05-16 15:47:47 +02:00
Markus Olsson
d97ea83fc6 setuptools, where art tho? 2024-05-16 15:43:48 +02:00
Markus Olsson
df0985e8b1 One more reference 2024-05-16 15:41:22 +02:00
Markus Olsson
c91e438287 Official arm64 builds are being published now
I can't imagine it'll work without this but let's try
2024-05-16 15:39:51 +02:00
Markus Olsson
328c8e8476 Bump node version to LTS 2024-05-16 15:39:07 +02:00
Markus Olsson
fcb5499d11 18.04 is deprecated 2024-05-16 15:38:35 +02:00
Markus Olsson
666d093cef Fix test 2024-05-16 15:07:19 +02:00
Markus Olsson
8959c70a21 Remove unused env vars 2024-05-16 15:03:54 +02:00
Sergio Padrino
734fbaeffd Merge pull request #17 from desktop/dependabot/npm_and_yarn/tmpl-1.0.5
Bump tmpl from 1.0.4 to 1.0.5
2022-02-08 10:51:17 +01:00
Sergio Padrino
155036b677 Merge pull request #20 from desktop/dependabot/npm_and_yarn/path-parse-1.0.7
Bump path-parse from 1.0.6 to 1.0.7
2022-02-08 10:49:49 +01:00
dependabot[bot]
5f87e4b360 Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-08 09:33:21 +00:00
dependabot[bot]
a0326f9749 Bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-08 09:33:17 +00:00
Sergio Padrino
ddccbacf81 Merge pull request #19 from desktop/bump-dependencies
Bump dependencies
2022-02-08 10:32:35 +01:00
Sergio Padrino
824c6f1364 Use latest macOS runner 2022-02-08 10:27:18 +01:00
Sergio Padrino
2386f9980f Bump dependencies 2022-02-08 10:27:07 +01:00
Sergio Padrino
ee3e96e70a Merge pull request #14 from desktop/releases/0.9.8
Release v0.9.8
2021-08-18 15:35:28 +02:00
Sergio Padrino
cbd3dbb31d Fix prebuilds to include ssh-wrapper
Aside from changing `--include-regex`, I also needed to fix `prebuild` itself to support stripping debug symbols of multiple files: https://github.com/prebuild/prebuild/pull/280

Instead of waiting until that PR is merged, I went ahead and switched our `prebuild` dependency with my fork, containing my changes.
2021-08-17 12:00:02 +02:00
Sergio Padrino
a2cfb8a8f3 Bump version to v0.9.8 2021-08-16 16:18:10 +02:00
Sergio Padrino
8b38cd56a1 Merge pull request #13 from desktop/ssh-wrapper
Add SSH wrapper for macOS
2021-08-16 15:30:06 +02:00
Sergio Padrino
6d672bd70f Skip ssh-wrapper tests on Linux too 2021-08-16 12:23:32 +02:00
Sergio Padrino
c16ca4bad7 Make sure we always build in CI 2021-08-16 11:54:08 +02:00
Sergio Padrino
158101ca4d Fix index.js style 2021-08-16 11:50:53 +02:00
Sergio Padrino
ac5ec3b3d2 Update README.md 2021-08-16 11:37:29 +02:00
Sergio Padrino
6bab396b51 Fix build & test on Windows 2021-08-16 11:32:50 +02:00
Sergio Padrino
0165c9bc33 Add ssh-wrapper with some tests 2021-08-16 11:03:10 +02:00
Sergio Padrino
c39c55721f Merge pull request #11 from desktop/releases/0.9.6 2021-04-29 08:45:33 +02:00
Sergio Padrino
38c590851f Bump version to 0.9.7 2021-04-29 08:39:30 +02:00
Sergio Padrino
ab9cabd08f Bump node version to fix arm64 prebuilds 😒 2021-04-29 08:39:03 +02:00
Sergio Padrino
0f81f6ec9a Bump version to 0.9.6 2021-04-22 15:27:13 +02:00
Sergio Padrino
0ca3d5caef Allow to actually install prebuilt binaries 2021-04-22 15:26:51 +02:00
Sergio Padrino
1dfb44afbe Merge pull request #10 from desktop/releases/0.9.5
Bump version to 0.9.5
2021-04-22 15:25:49 +02:00
Sergio Padrino
ee5cc6e589 Bump version to 0.9.5 2021-04-22 11:36:06 +02:00
Sergio Padrino
6ad1a5079e Merge pull request #9 from desktop/n-api-prebuilds
Make prebuilds using N-API
2021-04-22 11:34:36 +02:00
Sergio Padrino
d66fb5cf16 Make prebuilds using N-API 2021-04-22 11:24:16 +02:00
tidy-dev
28e4f64ae8 Merge pull request #7 from kittenmilk/patch-1
fix typo in README.md
2021-03-11 17:32:34 -05:00
lillian rose
8fb3d4fa9b fix typo in README.md
line 12: beign - being
2021-03-11 13:34:36 -06:00
Sergio Padrino
fa6110530f Merge pull request #6 from desktop/releases/0.9.4
Release 0.9.4
2021-03-11 09:05:38 -08:00
Sergio Padrino
7eabea0f1a Bump version to 0.9.4 2021-03-11 17:50:43 +01:00
Sergio Padrino
82635217c5 Merge pull request #5 from desktop/improve-windows-error-logs 2021-03-11 08:48:56 -08:00
Sergio Padrino
0ff24f8744 Improve error message when the trampoline can't connect to the server 2021-03-11 17:36:00 +01:00
Sergio Padrino
dd13c4b6cc Fix getting WSA error description on Windows 2021-03-11 04:04:25 -08:00
Sergio Padrino
639ed9be89 Show WSA error description on initialization too 2021-03-04 11:08:58 +01:00
Sergio Padrino
ff581e8b44 Get description for Winsock errors 2021-03-04 10:52:04 +01:00
Sergio Padrino
1a8de46fb7 Merge pull request #3 from desktop/releases/0.9.3 2021-02-18 01:15:47 -08:00
Sergio Padrino
ad699b35c0 Update releases.md 2021-02-18 10:08:32 +01:00
Sergio Padrino
31bc7170f6 Bump version to 0.9.3 2021-02-18 10:07:10 +01:00
Sergio Padrino
9cc5d4f9ee Document the release process 2021-02-18 10:07:02 +01:00
Sergio Padrino
731c53d97f Merge pull request #2 from philipturnbull/buffer-off-by-one
Reserve space for the NUL terminator
2021-02-18 00:07:25 -08:00
Phil Turnbull
6669833e3d Reserve space for the NUL terminator
If we read exactly `BUFFER_LENGTH` characters then we will overflow the buffer
when writing the NUL terminator. We need to reserve one extra character for the
NUL terminator.
2021-02-17 18:09:31 -05:00
Sergio Padrino
ca8d10fddb Merge pull request #1 from desktop/releases/0.9.2 2021-02-17 02:50:24 -08:00
Sergio Padrino
c77487f645 Bump package version to 0.9.2 2021-02-17 11:27:56 +01:00
Sergio Padrino
88065c4f2a Use specific OS versions in CI scripts 2021-02-17 11:09:05 +01:00
Sergio Padrino
6ac5a9240d Add more compilation flags to improve security 2021-02-17 11:03:00 +01:00
Sergio Padrino
ca0c849738 Bump package version to 0.9.1 2021-02-02 14:57:28 +01:00
Sergio Padrino
8c44f4b5ea Remove DESKTOP_PORT from the valid env vars
DESKTOP_PORT is only for internal usage, no need to forward it.
2021-02-02 14:57:02 +01:00
16 changed files with 2390 additions and 2102 deletions

View File

@@ -13,41 +13,47 @@ jobs:
name: ${{ matrix.friendlyName }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
node: [12.14.1]
node: [20.12.2]
os: [macos-latest, windows-latest, ubuntu-latest]
include:
- os: macos-latest
friendlyName: macOS
- os: windows-latest
friendlyName: Windows
- os: ubuntu-latest
- os: ubuntu-20.04
friendlyName: Linux
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v1
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install Python setup tools
run: |
python -m pip install --upgrade setuptools packaging
- name: Install and build
run: yarn
run: |
yarn install
yarn build
- name: Lint
run: yarn lint
- name: Test
run: yarn test
shell: bash
- name: Prebuild Node x64
run: yarn prebuild-node
- name: Prebuild Electron x64
run: yarn prebuild-electron
- name: Prebuild Electron arm64
run: yarn prebuild-electron-arm64
- name: Prebuild (x64)
run: npm run prebuild-napi-x64
- name: Prebuild (arm64)
run: npm run prebuild-napi-arm64
if: ${{ matrix.os != 'ubuntu-latest' }}
- name: Prebuild (Windows x86)
run: npm run prebuild-napi-ia32
if: ${{ matrix.os == 'windows-latest' }}
- name: Publish
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
run: yarn upload
env:
GITHUB_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn prebuild --upload-all ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1 +1 @@
12.14.1
20.12.2

View File

@@ -2,14 +2,14 @@
A cross-platform no-dependency C executable trampoline which lets GitHub Desktop
intercede in order to provide Git with any additional info it needs (like
credentials through `GIT_ASKPASS`).
credentials through `GIT_ASKPASS` or `SSH_ASKPASS`).
The intention is to support the same platforms that
[Electron supports](https://www.electronjs.org/docs/tutorial/support#supported-platforms).
## Building
This project is written as a Node project with the C-portions beign compiled by
This project is written as a Node project with the C-portions being compiled by
node-gyp. Installing dependencies and building requires Node.js, and yarn. With
those prerequisites the initial setup should be as easy as running `yarn` and
subsequent builds can be done using `yarn build`. There are some tests available
@@ -60,7 +60,7 @@ The equivalent Bash shell code looks like this:
```sh
# environment variable
GIT_ASKPASS="C:/some/path/to/desktop-trampoline.exe" \
GIT_ASKPASS="C:/some/path/to/desktop-askpass-trampoline.exe" \
# ensure Git doesn't block the process waiting for the user to provide input
GIT_TERMINAL_PROMPT=0 \
git \
@@ -125,3 +125,15 @@ Thanks to this, with only one generic trampoline that forwards everything via
that TCP socket, the implementation for every possible protocol like
`GIT_ASKPASS` can live within the GitHub Desktop codebase instead of having
multiple trampoline executables.
## SSH Wrapper
Along with the trampoline, an SSH wrapper is provided for macOS. The reason for
this is macOS before Monterey include an "old" version of OpenSSH that will
ignore the `SSH_ASKPASS` variable unless it's unable to write to a tty.
This SSH wrapper achieves exactly that: just runs whatever `ssh` exists in the
path in a way that will use `SSH_ASKPASS` when necessary.
More recent versions of OpenSSH (starting with 8.3) don't require this wrapper,
since they added support for a new `SSH_ASKPASS_REQUIRE` environment variable.

View File

@@ -1,33 +1,84 @@
{
'targets': [
{
'target_name': 'desktop-trampoline',
'type': 'executable',
'sources': [
'src/desktop-trampoline.c',
'src/socket.c'
'target_defaults': {
'defines': [
"NAPI_VERSION=<(napi_build_version)",
],
'include_dirs': [
'<!(node -p "require(\'node-addon-api\').include_dir")',
'include'
],
'xcode_settings': {
'OTHER_CFLAGS': [
'-Wall',
'-Werror'
],
'-Werror',
'-Werror=format-security',
'-fPIC',
'-D_FORTIFY_SOURCE=1',
'-fstack-protector-strong'
]
},
'cflags!': [
'-Wall',
'-Werror',
'-fPIC',
'-pie',
'-D_FORTIFY_SOURCE=1',
'-fstack-protector-strong',
'-Werror=format-security',
'-fno-exceptions'
],
'cflags_cc!': [ '-fno-exceptions' ],
'ldflags!': [
'-z relro',
'-z now'
],
'msvs_settings': {
'VCCLCompilerTool': { 'ExceptionHandling': 1 },
},
'conditions': [
['OS=="win"', { 'defines': [ 'WINDOWS' ] }]
]
},
'targets': [
{
'target_name': 'desktop-askpass-trampoline',
'type': 'executable',
'sources': [
'src/desktop-trampoline.c',
'src/socket.c'
],
'conditions': [
['OS=="win"', {
'defines': [ 'WINDOWS' ],
'link_settings': {
'libraries': [ 'Ws2_32.lib' ]
}
}]
]
},
{
'target_name': 'desktop-credential-helper-trampoline',
'type': 'executable',
'defines': [
'CREDENTIAL_HELPER'
],
'sources': [
'src/desktop-trampoline.c',
'src/socket.c'
],
'conditions': [
['OS=="win"', {
'link_settings': {
'libraries': [ 'Ws2_32.lib' ]
}
}]
]
},
{
'target_name': 'ssh-wrapper',
'type': 'executable',
'sources': [
'src/ssh-wrapper.c'
],
},
],
}

25
docs/releases.md Normal file
View File

@@ -0,0 +1,25 @@
# Releases
All releases are published using GitHub releases. Anyone with push access to the
repository can create a new release.
### Release Process
1. Create a branch named `releases/X.Y.Z`, where `X.Y.Z` is the version you want
to release.
1. Update the `version` field in the `package.json` with the new version you're
about to release.
1. Open a Pull Request for that branch.
1. Once the branch is approved, `git tag vX.Y.Z` the version you wish to
publish. **Important:** the version in the tag name must be preceeded by a
`v`.
1. `git push --follow-tags` to ensure all new commits (and the tag) are pushed
to the remote. Pushing the tag will start the release process.
1. Wait a few minutes for the build to finish (look for the build in
https://github.com/desktop/desktop-trampoline/actions)
1. Once the build is complete it will create a new release with all of the
assets and suggested release notes.
1. Update the changelog to whatever makes sense for this release. It should be
focused on user-facing changes.
1. Confirm all assets are uploaded for all the supported platforms.
1. Merge the Pull Request into `main` and you're done :tada:

10
index.d.ts vendored
View File

@@ -1,2 +1,8 @@
export function getDesktopTrampolinePath(): string
export function getDesktopTrampolineFilename(): string
export function getDesktopAskpassTrampolinePath(): string
export function getDesktopAskpassTrampolineFilename(): string
export function getDesktopCredentialHelperTrampolinePath(): string
export function getDesktopCredentialHelperTrampolineFilename(): string
export function getSSHWrapperPath(): string
export function getSSHWrapperFilename(): string

View File

@@ -1,21 +1,48 @@
const Path = require('path')
function getDesktopTrampolinePath() {
function getDesktopAskpassTrampolinePath() {
return Path.join(
__dirname,
'build',
'Release',
getDesktopTrampolineFilename()
getDesktopAskpassTrampolineFilename()
)
}
function getDesktopTrampolineFilename() {
function getDesktopAskpassTrampolineFilename() {
return process.platform === 'win32'
? 'desktop-trampoline.exe'
: 'desktop-trampoline'
? 'desktop-askpass-trampoline.exe'
: 'desktop-askpass-trampoline'
}
function getDesktopCredentialHelperTrampolinePath() {
return Path.join(
__dirname,
'build',
'Release',
getDesktopCredentialHelperTrampolineFilename()
)
}
function getDesktopCredentialHelperTrampolineFilename() {
return process.platform === 'win32'
? 'desktop-credential-helper-trampoline.exe'
: 'desktop-credential-helper-trampoline'
}
function getSSHWrapperPath() {
return Path.join(__dirname, 'build', 'Release', getSSHWrapperFilename())
}
function getSSHWrapperFilename() {
return process.platform === 'win32' ? 'ssh-wrapper.exe' : 'ssh-wrapper'
}
module.exports = {
getDesktopTrampolinePath,
getDesktopTrampolineFilename,
getDesktopAskpassTrampolinePath,
getDesktopAskpassTrampolineFilename,
getDesktopCredentialHelperTrampolinePath,
getDesktopCredentialHelperTrampolineFilename,
getSSHWrapperPath,
getSSHWrapperFilename,
}

View File

@@ -1,6 +1,6 @@
{
"name": "desktop-trampoline",
"version": "0.9.0",
"version": "0.9.11",
"main": "index.js",
"keywords": [],
"author": "",
@@ -15,10 +15,10 @@
"test": "jest",
"lint": "prettier -c **/*.js **/*.md",
"lint:fix": "prettier --write **/*.js **/*.md",
"prebuild-node": "prebuild -t 10.11.0 -t 11.9.0 -t 12.0.0 -t 14.8.0 --strip --include-regex \"desktop-trampoline(\\.exe)?$\"",
"prebuild-electron": "prebuild -t 7.0.0 -t 8.0.0 -t 9.0.0 -t 10.0.0 -t 11.0.0 -r electron --strip --include-regex \"desktop-trampoline(\\.exe)?$\"",
"prebuild-electron-arm64": "prebuild -t 7.0.0 -t 8.0.0 -t 9.0.0 -t 10.0.0 -t 11.0.0 -r electron -a arm64 --strip --include-regex \"desktop-trampoline(\\.exe)?$\"",
"prebuild-all": "yarn prebuild-node && yarn prebuild-electron && yarn prebuild-electron-arm64",
"prebuild-napi-x64": "prebuild -t 3 -r napi -a x64 --strip --include-regex \"(desktop-(askpass|credential-helper)-trampoline|ssh-wrapper)(\\.exe)?$\"",
"prebuild-napi-ia32": "prebuild -t 3 -r napi -a ia32 --strip --include-regex \"(desktop-(askpass|credential-helper)-trampoline|ssh-wrapper)(\\.exe)?$\"",
"prebuild-napi-arm64": "prebuild -t 3 -r napi -a arm64 --strip --include-regex \"(desktop-(askpass|credential-helper)-trampoline|ssh-wrapper)(\\.exe)?$\"",
"prebuild-all": "yarn prebuild-napi-x64 && yarn prebuild-napi-ia32 && yarn prebuild-napi-arm64",
"upload": "node ./script/upload.js"
},
"repository": {
@@ -29,11 +29,24 @@
"url": "https://github.com/desktop/desktop-trampoline/issues"
},
"homepage": "https://github.com/desktop/desktop-trampoline#readme",
"dependencies": {
"node-addon-api": "^4.3.0",
"prebuild-install": "^7.1.2"
},
"devDependencies": {
"jest": "^26.4.2",
"node-gyp": "^7.1.0",
"prebuild": "^10.0.1",
"prettier": "^2.1.2",
"split2": "^3.2.2"
"jest": "^27.5.0",
"node-gyp": "^10.1.0",
"prebuild": "^13.0.1",
"prettier": "^2.5.1",
"split2": "^4.1.0"
},
"binary": {
"napi_versions": [
3
]
},
"config": {
"runtime": "napi",
"target": 3
}
}

View File

@@ -1,14 +0,0 @@
// to ensure that env not in the CI server log
const path = require('path')
const { spawnSync } = require('child_process')
spawnSync(
path.join(
__dirname,
'../node_modules/.bin/prebuild' +
(process.platform === 'win32' ? '.cmd' : '')
),
['--upload-all', process.env.GITHUB_AUTH_TOKEN],
{ stdio: 'inherit' }
)

View File

@@ -9,6 +9,13 @@
#define BUFFER_LENGTH 4096
#define MAXIMUM_NUMBER_LENGTH 33
#ifdef CREDENTIAL_HELPER
#define DESKTOP_TRAMPOLINE_IDENTIFIER "CREDENTIALHELPER"
#else
#define DESKTOP_TRAMPOLINE_IDENTIFIER "ASKPASS"
#endif
#define WRITE_STRING_OR_EXIT(dataName, dataString) \
if (writeSocket(socket, dataString, strlen(dataString) + 1) != 0) { \
printSocketError("ERROR: Couldn't send " dataName); \
@@ -17,13 +24,9 @@ if (writeSocket(socket, dataString, strlen(dataString) + 1) != 0) { \
// This is a list of valid environment variables that GitHub Desktop might
// send or expect to receive.
#define NUMBER_OF_VALID_ENV_VARS 5
#define NUMBER_OF_VALID_ENV_VARS 1
static const char *sValidEnvVars[NUMBER_OF_VALID_ENV_VARS] = {
"DESKTOP_TRAMPOLINE_IDENTIFIER",
"DESKTOP_PORT",
"DESKTOP_TRAMPOLINE_TOKEN",
"DESKTOP_USERNAME",
"DESKTOP_ENDPOINT",
};
/** Returns 1 if a given env variable is valid, 0 otherwise. */
@@ -33,8 +36,8 @@ int isValidEnvVar(char *env) {
// Make sure that not only the passed env var string starts with the
// candidate contesnts, but also that there is a '=' character right after:
// Valid: "DESKTOP_PORT=50"
// Not valid: "DESKTOP_PORT_SOMETHING=50"
// Valid: "DESKTOP_USERNAME=sergiou87"
// Not valid: "DESKTOP_USERNAME_SOMETHING=sergiou87"
if (strncmp(env, candidate, strlen(candidate)) == 0
&& strlen(env) > strlen(candidate)
&& env[strlen(candidate)] == '=') {
@@ -67,7 +70,9 @@ int runTrampolineClient(SOCKET *outSocket, int argc, char **argv, char **envp) {
*outSocket = socket;
if (connectSocket(socket, desktopPort) != 0) {
printSocketError("ERROR: Couldn't connect to 127.0.0.1:%d", desktopPort);
printSocketError("ERROR: Couldn't connect to 127.0.0.1:%d - Please make "
"sure you don't have an antivirus or firewall blocking "
"this connection.", desktopPort);
return 1;
}
@@ -82,8 +87,9 @@ int runTrampolineClient(SOCKET *outSocket, int argc, char **argv, char **envp) {
}
// Get the number of environment variables
char *validEnvVars[NUMBER_OF_VALID_ENV_VARS];
int envc = 0;
char *validEnvVars[NUMBER_OF_VALID_ENV_VARS + 1];
validEnvVars[0] = "DESKTOP_TRAMPOLINE_IDENTIFIER=" DESKTOP_TRAMPOLINE_IDENTIFIER;
int envc = 1;
for (char **env = envp; *env != 0; env++) {
if (isValidEnvVar(*env)) {
validEnvVars[envc] = *env;
@@ -101,9 +107,17 @@ int runTrampolineClient(SOCKET *outSocket, int argc, char **argv, char **envp) {
WRITE_STRING_OR_EXIT("environment variable", validEnvVars[idx]);
}
// TODO: send stdin stuff?
char stdinBuffer[BUFFER_LENGTH + 1];
int stdinBytes = 0;
char buffer[BUFFER_LENGTH];
#ifdef CREDENTIAL_HELPER
stdinBytes = fread(stdinBuffer, sizeof(char), BUFFER_LENGTH, stdin);
#endif
stdinBuffer[stdinBytes] = '\0';
WRITE_STRING_OR_EXIT("stdin", stdinBuffer);
char buffer[BUFFER_LENGTH + 1];
size_t totalBytesRead = 0;
ssize_t bytesRead = 0;

View File

@@ -6,13 +6,30 @@
#include <stdlib.h>
#include <string.h>
#ifdef WINDOWS
#define MAX_WSA_ERROR_DESCRIPTION_LENGTH 4096
void getWSALastErrorDescription(wchar_t *buffer, int bufferLength) {
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)buffer, bufferLength - 1, NULL);
}
#endif
int initializeNetwork(void) {
#ifdef WINDOWS
// Initialize Winsock
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2,2), &wsaData);
if (result != NO_ERROR) {
fprintf(stderr, "ERROR: WSAStartup failed: %d\n", result);
wchar_t errorDescription[MAX_WSA_ERROR_DESCRIPTION_LENGTH];
getWSALastErrorDescription(errorDescription, MAX_WSA_ERROR_DESCRIPTION_LENGTH);
fprintf(stderr, "ERROR: WSAStartup failed (%d). Error %ld: %ls\n",
result, WSAGetLastError(), errorDescription);
return 1;
}
#endif
@@ -55,8 +72,7 @@ int readSocket(SOCKET socket, void *buffer, size_t length) {
return recv(socket, buffer, length, 0);
}
void printSocketError(char *fmt, ...)
{
void printSocketError(char *fmt, ...) {
char formatted_string[4096];
va_list argptr;
@@ -65,7 +81,10 @@ void printSocketError(char *fmt, ...)
va_end(argptr);
#ifdef WINDOWS
fprintf(stderr, "%s: %ld\n", formatted_string, WSAGetLastError());
wchar_t errorDescription[MAX_WSA_ERROR_DESCRIPTION_LENGTH];
getWSALastErrorDescription(errorDescription, MAX_WSA_ERROR_DESCRIPTION_LENGTH);
fprintf(stderr, "%s (%ld): %ls\n", formatted_string, WSAGetLastError(), errorDescription);
#else
fprintf(stderr, "%s (%d): %s\n", formatted_string, errno, strerror(errno));
#endif

37
src/ssh-wrapper.c Normal file
View File

@@ -0,0 +1,37 @@
#ifdef WINDOWS
int main(int argc, char **argv) {
// Not needed on Windows, this will just create a dummy executable
return -1;
}
#else
#include <unistd.h>
#include <stdio.h>
/**
* This is a wrapper for the ssh command. It is used to make sure ssh runs without
* a tty on macOS, allowing GitHub Desktop to intercept different prompts from
* ssh (e.g. passphrase, adding a host to the list of known hosts...).
* This is not necessary on more recent versions of OpenSSH (starting with v8.3)
* which include support for the SSH_ASKPASS_REQUIRE environment variable.
*/
int main(int argc, char **argv) {
pid_t child = fork();
if (child < 0) {
fprintf(stderr, "Failed to fork\n");
return -1;
}
if (child != 0) {
// This is the parent process. Just exit.
return 0;
}
setsid();
return execvp("ssh", argv);
}
#endif

View File

@@ -0,0 +1,153 @@
const { stat, access } = require('fs').promises
const { constants } = require('fs')
const { execFile } = require('child_process')
const { promisify } = require('util')
const {
getDesktopAskpassTrampolinePath,
getDesktopCredentialHelperTrampolinePath,
} = require('../index')
const split2 = require('split2')
const { createServer } = require('net')
const askPassTrampolinePath = getDesktopAskpassTrampolinePath()
const helperTrampolinePath = getDesktopCredentialHelperTrampolinePath()
const run = promisify(execFile)
describe('desktop-trampoline', () => {
it('exists and is a regular file', async () =>
expect((await stat(askPassTrampolinePath)).isFile()).toBe(true))
it('can be executed by current process', () =>
access(askPassTrampolinePath, constants.X_OK))
it('fails when required environment variables are missing', () =>
expect(run(askPassTrampolinePath, ['Username'])).rejects.toThrow())
const captureSession = () => {
const output = []
let resolveOutput = null
const outputPromise = new Promise(resolve => {
resolveOutput = resolve
})
const server = createServer(socket => {
let timeoutId = null
socket.pipe(split2(/\0/)).on('data', data => {
output.push(data.toString('utf8'))
// Hack: consider the session finished after 100ms of inactivity.
// In a real-world scenario, you'd have to parse the data to know when
// the session is finished.
if (timeoutId !== null) {
clearTimeout(timeoutId)
timeoutId = null
}
timeoutId = setTimeout(() => {
resolveOutput(output)
socket.end()
server.close()
}, 100)
})
})
const serverPortPromise = new Promise((resolve, reject) => {
server.on('error', e => reject(e))
server.listen(0, '127.0.0.1', () => {
resolve(server.address().port)
})
})
return [serverPortPromise, outputPromise]
}
it('forwards arguments and valid environment variables correctly', async () => {
const [portPromise, outputPromise] = captureSession()
const port = await portPromise
const env = {
DESKTOP_TRAMPOLINE_TOKEN: '123456',
DESKTOP_PORT: port,
INVALID_VARIABLE: 'foo bar',
}
const opts = { env }
await run(askPassTrampolinePath, ['baz'], opts)
const output = await outputPromise
const outputArguments = output.slice(1, 2)
expect(outputArguments).toStrictEqual(['baz'])
// output[2] is the number of env variables
const envc = parseInt(output[2])
const outputEnv = output.slice(3, 3 + envc)
expect(outputEnv).toHaveLength(2)
expect(outputEnv).toContain('DESKTOP_TRAMPOLINE_TOKEN=123456')
expect(outputEnv).toContain('DESKTOP_TRAMPOLINE_IDENTIFIER=ASKPASS')
})
it('forwards stdin when running in credential-helper mode', async () => {
const [portPromise, outputPromise] = captureSession()
const port = await portPromise
const cp = run(helperTrampolinePath, ['get'], {
env: { DESKTOP_PORT: port },
})
cp.child.stdin.end('oh hai\n')
await cp
const output = await outputPromise
expect(output.at(-1)).toBe('oh hai\n')
})
it("doesn't forward stdin when running in askpass mode", async () => {
const [portPromise, outputPromise] = captureSession()
const port = await portPromise
const cp = run(askPassTrampolinePath, ['get'], {
env: { DESKTOP_PORT: port },
})
cp.child.stdin.end('oh hai\n')
await cp
const output = await outputPromise
expect(output.at(-1)).toBe('')
})
it('askpass handler ignores the DESKTOP_TRAMPOLINE_IDENTIFIER env var', async () => {
const [portPromise, outputPromise] = captureSession()
const port = await portPromise
const cp = run(askPassTrampolinePath, ['get'], {
env: { DESKTOP_PORT: port, DESKTOP_TRAMPOLINE_IDENTIFIER: 'foo' },
})
cp.child.stdin.end('oh hai\n')
await cp
const output = await outputPromise
const envc = parseInt(output[2])
const outputEnv = output.slice(3, 3 + envc)
expect(outputEnv).toContain('DESKTOP_TRAMPOLINE_IDENTIFIER=ASKPASS')
})
it('credential handler ignores the DESKTOP_TRAMPOLINE_IDENTIFIER env var', async () => {
const [portPromise, outputPromise] = captureSession()
const port = await portPromise
const cp = run(helperTrampolinePath, ['get'], {
env: { DESKTOP_PORT: port, DESKTOP_TRAMPOLINE_IDENTIFIER: 'foo' },
})
cp.child.stdin.end('oh hai\n')
await cp
const output = await outputPromise
const envc = parseInt(output[2])
const outputEnv = output.slice(3, 3 + envc)
expect(outputEnv).toContain(
'DESKTOP_TRAMPOLINE_IDENTIFIER=CREDENTIALHELPER'
)
})
})

View File

@@ -1,64 +0,0 @@
const { stat, access } = require('fs').promises
const { constants } = require('fs')
const { execFile } = require('child_process')
const { promisify } = require('util')
const { getDesktopTrampolinePath } = require('../index')
const split2 = require('split2')
const { createServer } = require('net')
const trampolinePath = getDesktopTrampolinePath()
const run = promisify(execFile)
describe('desktop-trampoline', () => {
it('exists and is a regular file', async () =>
expect((await stat(trampolinePath)).isFile()).toBe(true))
it('can be executed by current process', () =>
access(trampolinePath, constants.X_OK))
it('fails when required environment variables are missing', () =>
expect(run(trampolinePath, ['Username'])).rejects.toThrow())
it('forwards arguments and valid environment variables correctly', async () => {
const output = []
const server = createServer(socket => {
socket.pipe(split2(/\0/)).on('data', data => {
output.push(data.toString('utf8'))
})
// Don't send anything and just close the socket after the trampoline is
// done forwarding data.
socket.end()
})
const startTrampolineServer = async () => {
return new Promise((resolve, reject) => {
server.on('error', e => reject(e))
server.listen(0, '127.0.0.1', () => {
resolve(server.address().port)
})
})
}
const port = await startTrampolineServer()
const env = {
DESKTOP_PORT_FAKE: 32123,
DESKTOP_TRAMPOLINE_IDENTIFIER: '123456',
DESKTOP_PORT: port,
INVALID_VARIABLE: 'foo bar',
}
const opts = { env }
await run(trampolinePath, ['baz'], opts)
const outputArguments = output.slice(1, 2)
expect(outputArguments).toStrictEqual(['baz'])
// output[2] is the number of env variables
const outputEnv = output.slice(3)
expect(outputEnv).toHaveLength(2)
expect(outputEnv).toContain('DESKTOP_TRAMPOLINE_IDENTIFIER=123456')
expect(outputEnv).toContain(`DESKTOP_PORT=${port}`)
server.close()
})
})

42
test/ssh-wrapper.test.js Normal file
View File

@@ -0,0 +1,42 @@
const { stat, access } = require('fs').promises
const { constants } = require('fs')
const { execFile } = require('child_process')
const { promisify } = require('util')
const { getSSHWrapperPath } = require('../index')
const sshWrapperPath = getSSHWrapperPath()
const run = promisify(execFile)
describe('ssh-wrapper', () => {
it('exists and is a regular file', async () =>
expect((await stat(sshWrapperPath)).isFile()).toBe(true))
// On Windows, the binary generated is just useless, so no point to test it.
// Also, this won't be used on Linux (for now at least), so don't bother to
// run the tests there.
if (process.platform !== 'darwin') {
return
}
it('can be executed by current process', () =>
access(sshWrapperPath, constants.X_OK))
it('attempts to use ssh-askpass program', async () => {
// Try to connect to github.com with a non-existent known_hosts file to force
// ssh to prompt the user and use askpass.
const result = await run(
sshWrapperPath,
['-o', 'UserKnownHostsFile=/path/to/fake/known_hosts', 'git@github.com'],
{
env: {
SSH_ASKPASS: '/path/to/fake/ssh-askpass',
DISPLAY: '.',
},
}
)
expect(result.stderr).toMatch(
/ssh_askpass: exec\(\/path\/to\/fake\/ssh-askpass\): No such file or directory/
)
})
})

3883
yarn.lock
View File

File diff suppressed because it is too large Load Diff