-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[eas-json] validate EAS Submit inputs better (#2198)
* [eas-json] sanitize EAS Submit inputs better * add link to docs * update CHANGELOG.md * validate app specific password * use Joi schema for validation * fix typo * minor improvements * Update packages/eas-cli/src/submit/ios/__tests__/IosSubmitCommand-test.ts Co-authored-by: Stanisław Chmiela <[email protected]> * make uuid validation less strict * apply suggested changes * fix tests --------- Co-authored-by: Stanisław Chmiela <[email protected]>
- Loading branch information
1 parent
b5f2f52
commit 126c562
Showing
8 changed files
with
244 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,6 +78,43 @@ describe(IosSubmitCommand, () => { | |
jest.mocked(getOwnerAccountForProjectIdAsync).mockResolvedValue(mockJester.accounts[0]); | ||
}); | ||
|
||
it('throws an error if using app specific password in invalid format', async () => { | ||
const projectId = uuidv4(); | ||
const graphqlClient = {} as any as ExpoGraphqlClient; | ||
const analytics = instance(mock<Analytics>()); | ||
jest | ||
.mocked(getArchiveAsync) | ||
.mockImplementation(jest.requireActual('../../ArchiveSource').getArchiveAsync); | ||
|
||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'ls -la'; | ||
|
||
const ctx = await createSubmissionContextAsync({ | ||
platform: Platform.IOS, | ||
projectDir: testProject.projectRoot, | ||
archiveFlags: { | ||
url: 'http://expo.dev/fake.ipa', | ||
}, | ||
profile: { | ||
language: 'en-US', | ||
appleId: '[email protected]', | ||
ascAppId: '12345678', | ||
}, | ||
nonInteractive: false, | ||
actor: mockJester, | ||
graphqlClient, | ||
analytics, | ||
exp: testProject.appJSON.expo, | ||
projectId, | ||
vcsClient, | ||
}); | ||
const command = new IosSubmitCommand(ctx); | ||
await expect(command.runAsync()).rejects.toThrow( | ||
'EXPO_APPLE_APP_SPECIFIC_PASSWORD must be in the format XXXX-XXXX-XXXX-XXXX, where X is a lowercase letter.' | ||
); | ||
|
||
delete process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD; | ||
}); | ||
|
||
describe('non-interactive mode', () => { | ||
it("throws error if didn't provide appleId and ascAppId in the submit profile", async () => { | ||
const projectId = uuidv4(); | ||
|
@@ -118,7 +155,7 @@ describe(IosSubmitCommand, () => { | |
.mocked(getArchiveAsync) | ||
.mockImplementation(jest.requireActual('../../ArchiveSource').getArchiveAsync); | ||
|
||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'supersecret'; | ||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'abcd-abcd-abcd-abcd'; | ||
|
||
const ctx = await createSubmissionContextAsync({ | ||
platform: Platform.IOS, | ||
|
@@ -147,7 +184,7 @@ describe(IosSubmitCommand, () => { | |
archiveSource: { type: SubmissionArchiveSourceType.Url, url: 'http://expo.dev/fake.ipa' }, | ||
config: { | ||
appleIdUsername: '[email protected]', | ||
appleAppSpecificPassword: 'supersecret', | ||
appleAppSpecificPassword: 'abcd-abcd-abcd-abcd', | ||
ascAppIdentifier: '12345678', | ||
}, | ||
}); | ||
|
@@ -182,7 +219,7 @@ describe(IosSubmitCommand, () => { | |
return ctx; | ||
}); | ||
|
||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'supersecret'; | ||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'abcd-abcd-abcd-abcd'; | ||
|
||
const ctx = await createSubmissionContextAsync({ | ||
platform: Platform.IOS, | ||
|
@@ -209,7 +246,7 @@ describe(IosSubmitCommand, () => { | |
submittedBuildId: selectedBuild.id, | ||
config: { | ||
appleIdUsername: '[email protected]', | ||
appleAppSpecificPassword: 'supersecret', | ||
appleAppSpecificPassword: 'abcd-abcd-abcd-abcd', | ||
ascAppIdentifier: '87654321', | ||
}, | ||
archiveSource: undefined, | ||
|
@@ -239,7 +276,7 @@ describe(IosSubmitCommand, () => { | |
return ctx; | ||
}); | ||
|
||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'supersecret'; | ||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'abcd-abcd-abcd-abcd'; | ||
|
||
const ctx = await createSubmissionContextAsync({ | ||
platform: Platform.IOS, | ||
|
@@ -266,7 +303,7 @@ describe(IosSubmitCommand, () => { | |
submittedBuildId: selectedBuild.id, | ||
config: { | ||
appleIdUsername: '[email protected]', | ||
appleAppSpecificPassword: 'supersecret', | ||
appleAppSpecificPassword: 'abcd-abcd-abcd-abcd', | ||
ascAppIdentifier: '12345678', | ||
}, | ||
archiveSource: undefined, | ||
|
@@ -301,7 +338,7 @@ describe(IosSubmitCommand, () => { | |
return ctx; | ||
}); | ||
|
||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'supersecret'; | ||
process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD = 'abcd-abcd-abcd-abcd'; | ||
|
||
const ctx = await createSubmissionContextAsync({ | ||
platform: Platform.IOS, | ||
|
@@ -329,7 +366,7 @@ describe(IosSubmitCommand, () => { | |
submittedBuildId: selectedBuild.id, | ||
config: { | ||
appleIdUsername: '[email protected]', | ||
appleAppSpecificPassword: 'supersecret', | ||
appleAppSpecificPassword: 'abcd-abcd-abcd-abcd', | ||
ascAppIdentifier: '12345678', | ||
}, | ||
archiveSource: undefined, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,10 +107,10 @@ test('ios config with all required values', async () => { | |
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'QWERTY', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'abc-123-def-456', | ||
ascApiKeyId: 'ABCD', | ||
ascApiKeyIssuerId: 'b4d78f58-48c6-4f2c-96cb-94d8cd76970a', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}, | ||
}, | ||
}, | ||
|
@@ -121,11 +121,11 @@ test('ios config with all required values', async () => { | |
|
||
expect(iosProfile).toEqual({ | ||
appleId: '[email protected]', | ||
appleTeamId: 'QWERTY', | ||
appleTeamId: 'AB32CDE81F', | ||
ascAppId: '1223423523', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'abc-123-def-456', | ||
ascApiKeyId: 'ABCD', | ||
ascApiKeyIssuerId: 'b4d78f58-48c6-4f2c-96cb-94d8cd76970a', | ||
ascApiKeyId: 'AB32CDE81F', | ||
language: 'en-US', | ||
}); | ||
}); | ||
|
@@ -137,7 +137,7 @@ test('ios config with ascApiKey fields set to env var', async () => { | |
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'QWERTY', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: '$ASC_API_KEY_PATH', | ||
ascApiKeyIssuerId: '$ASC_API_KEY_ISSUER_ID', | ||
ascApiKeyId: '$ASC_API_KEY_ID', | ||
|
@@ -148,18 +148,18 @@ test('ios config with ascApiKey fields set to env var', async () => { | |
|
||
try { | ||
process.env.ASC_API_KEY_PATH = './path-ABCD.p8'; | ||
process.env.ASC_API_KEY_ISSUER_ID = 'abc-123-def-456'; | ||
process.env.ASC_API_KEY_ID = 'ABCD'; | ||
process.env.ASC_API_KEY_ISSUER_ID = 'b4d78f58-48c6-4f2c-96cb-94d8cd76970a'; | ||
process.env.ASC_API_KEY_ID = 'AB32CDE81F'; | ||
const accessor = EasJsonAccessor.fromProjectPath('/project'); | ||
const iosProfile = await EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release'); | ||
|
||
expect(iosProfile).toEqual({ | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'QWERTY', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'abc-123-def-456', | ||
ascApiKeyId: 'ABCD', | ||
ascApiKeyIssuerId: 'b4d78f58-48c6-4f2c-96cb-94d8cd76970a', | ||
ascApiKeyId: 'AB32CDE81F', | ||
language: 'en-US', | ||
}); | ||
} finally { | ||
|
@@ -176,16 +176,16 @@ test('valid profile extending other profile', async () => { | |
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'QWERTY', | ||
appleTeamId: 'AB32CDE81F', | ||
}, | ||
}, | ||
extension: { | ||
extends: 'base', | ||
ios: { | ||
appleTeamId: 'ABCDEF', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'abc-123-def-456', | ||
ascApiKeyId: 'ABCD', | ||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}, | ||
}, | ||
}, | ||
|
@@ -202,19 +202,134 @@ test('valid profile extending other profile', async () => { | |
language: 'en-US', | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'QWERTY', | ||
appleTeamId: 'AB32CDE81F', | ||
}); | ||
expect(extendedProfile).toEqual({ | ||
language: 'en-US', | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'ABCDEF', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'abc-123-def-456', | ||
ascApiKeyId: 'ABCD', | ||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}); | ||
}); | ||
|
||
test('ios config with with invalid appleId', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
submit: { | ||
release: { | ||
ios: { | ||
appleId: '| /bin/bash echo "hello"', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const accessor = EasJsonAccessor.fromProjectPath('/project'); | ||
const promise = EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release'); | ||
await expect(promise).rejects.toThrow( | ||
'Invalid Apple ID was specified. It should be a valid email address. Example: "[email protected]".' | ||
); | ||
}); | ||
|
||
test('ios config with with invalid ascAppId', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
submit: { | ||
release: { | ||
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: 'othervalue', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const accessor = EasJsonAccessor.fromProjectPath('/project'); | ||
const promise = EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release'); | ||
await expect(promise).rejects.toThrow( | ||
'Invalid Apple App Store Connect App ID ("ascAppId") was specified. It should consist of 10 digits. Example: "1234567891". Learn more: https://expo.fyi/asc-app-id.md.' | ||
); | ||
}); | ||
|
||
test('ios config with with invalid appleTeamId', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
submit: { | ||
release: { | ||
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'ls -la', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const accessor = EasJsonAccessor.fromProjectPath('/project'); | ||
const promise = EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release'); | ||
await expect(promise).rejects.toThrow( | ||
'Invalid Apple Team ID was specified. It should consist of 10 uppercase letters or digits. Example: "AB32CDE81F".' | ||
); | ||
}); | ||
|
||
test('ios config with with invalid ascApiKeyIssuerId', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
submit: { | ||
release: { | ||
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'notanuuid', | ||
ascApiKeyId: 'AB32CDE81F', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const accessor = EasJsonAccessor.fromProjectPath('/project'); | ||
const promise = EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release'); | ||
await expect(promise).rejects.toThrow( | ||
'Invalid Apple App Store Connect API Key Issuer ID ("ascApiKeyIssuerId") was specified. It should be a valid UUID. Example: "b4d78f58-48c6-4f2c-96cb-94d8cd76970a". Learn more: https://expo.fyi/creating-asc-api-key.' | ||
); | ||
}); | ||
|
||
test('ios config with with invalid ascApiKeyId', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
submit: { | ||
release: { | ||
ios: { | ||
appleId: '[email protected]', | ||
ascAppId: '1223423523', | ||
appleTeamId: 'AB32CDE81F', | ||
ascApiKeyPath: './path-ABCD.p8', | ||
ascApiKeyIssuerId: 'b4d78f58-48c6-4f2c-96cb-94d8cd76970a', | ||
ascApiKeyId: 'wrong value', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
const accessor = EasJsonAccessor.fromProjectPath('/project'); | ||
const promise = EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release'); | ||
await expect(promise).rejects.toThrow( | ||
`Invalid Apple App Store Connect API Key ID ("ascApiKeyId") was specified. It should consist of 10 uppercase letters or digits. Example: "AB32CDE81F". Learn more: https://expo.fyi/creating-asc-api-key.` | ||
); | ||
}); | ||
|
||
test('get profile names', async () => { | ||
await fs.writeJson('/project/eas.json', { | ||
submit: { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.