Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
4 changes: 4 additions & 0 deletions .gitallowed
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# The following regexes are exceptions for git-secrets - https://github.com/awslabs/git-secrets

# The only AWS account number allowed to be used in tests
ACCOUNT_ID = '123456789012';
1 change: 1 addition & 0 deletions .github/dependency-review/dependency-review-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ allow-licenses:
- 'BSL-1.0'
- 'CC-BY-3.0'
- 'CC-BY-4.0'
- 'CC-BY-SA-4.0'
- 'CC0-1.0'
- 'curl'
- 'ISC'
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/callable-git-secrets-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Git Secrets Check
on: workflow_call

jobs:
git_secrets_check:
name: Scan for secrets
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
path: amplify-js

- name: Install git-secrets
run: |
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets
sudo make install

- name: Register AWS patterns and scan
working-directory: ./amplify-js
run: |
git secrets --register-aws
# Scan only the files in the current checkout (PR merge commit)
git secrets --scan
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:
dependency-review:
needs: prebuild
uses: ./.github/workflows/callable-dependency-review.yml
git-secrets-check:
uses: ./.github/workflows/callable-git-secrets-check.yml
all-unit-tests-pass:
name: Unit and Bundle tests have passed
needs:
Expand All @@ -52,6 +54,7 @@ jobs:
- github-actions-test
- tsc-compliance-test
- dependency-review
- git-secrets-check
runs-on: ubuntu-latest
if: success() # only run when all checks have passed
# store success output flag for ci job
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ packages/**/esm/
packages/**/cjs/
**/.DS_Store
.vscode
.kiro
.idea
*.log
.npm/
Expand Down
24 changes: 24 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env sh

# Pre-push hook to scan for secrets using git-secrets
# This prevents accidentally pushing AWS credentials or other secrets

# Check if git-secrets is installed
if ! command -v git-secrets >/dev/null 2>&1; then
echo ""
echo "ERROR: git-secrets is not installed."
echo ""
echo "Please install git-secrets to continue:"
echo ""
echo " macOS: brew install git-secrets"
echo " Linux: See https://github.com/awslabs/git-secrets#installing-git-secrets"
echo " Windows: See https://github.com/awslabs/git-secrets#installing-git-secrets"
echo ""
echo "After installation, register AWS patterns:"
echo " git secrets --register-aws"
echo ""
exit 1
fi

# Run git-secrets scan and propagate exit code
git secrets --scan
69 changes: 69 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Whether it's a bug report, new feature, correction, or additional documentation,
- [Our Design](#our-design)
- [Development Process](#development-process)
- [Setting up for local development](#setting-up-for-local-development)
- [Setting up git-secrets](#setting-up-git-secrets)
- [Architecture of the codebase](#architecture-of-the-codebase)
- [Steps towards contributions](#steps-towards-contributions)
- [Bug Reports](#bug-reports)
Expand Down Expand Up @@ -68,6 +69,74 @@ yarn build

> Note: Make sure to always [sync your fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/syncing-a-fork) with main branch of `amplify-js`

## Setting up git-secrets

This repository uses [git-secrets](https://github.com/awslabs/git-secrets) to prevent accidentally pushing AWS credentials or other secrets. A pre-push hook will scan your commits before pushing to the remote repository.

### Installing git-secrets

**macOS (using Homebrew):**

```bash
brew install git-secrets
```

**Linux (Debian/Ubuntu):**

```bash
sudo apt-get install git-secrets
```

**Linux (manual installation):**

```bash
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets
sudo make install
```

**Windows (manual installation):**

1. Clone the repository: `git clone https://github.com/awslabs/git-secrets.git`
2. Add the `git-secrets` directory to your PATH
3. Alternatively, copy `git-secrets` to a directory already in your PATH

### Registering AWS patterns

After installing git-secrets, register the AWS secret patterns in your local repository:

```bash
git secrets --register-aws
```

This registers patterns to detect:

- AWS Access Key IDs (e.g., `AKIAIOSFODNN7EXAMPLE`)
- AWS Secret Access Keys
- AWS Session Tokens

You can verify the registered patterns with:

```bash
git secrets --list
```

### Handling false positives

If git-secrets flags a legitimate string as a potential secret (false positive), you can add an allowed pattern:

```bash
# Allow a specific pattern
git secrets --add --allowed 'AKIAIOSFODNN7EXAMPLE'

# Allow patterns matching a regex
git secrets --add --allowed 'my-test-pattern.*'
```

Allowed patterns are stored in `.git/config` and apply only to your local repository.

> **Note:** The pre-push hook will block pushes if git-secrets is not installed. Please install and configure git-secrets before contributing.

## Architecture of the codebase

Amplify JS is a monorepo built with `Yarn` and `Lerna`. All the categories of Amplify live within the `packages` directory in the root. Each category inside packages has its own `src/` and `package.json`.
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"publish:verdaccio": "lerna publish --canary --force-publish --no-push --dist-tag=unstable --preid=unstable --yes",
"generate-metadata": "git rev-parse --short HEAD > packages/core/metadata && git commit -am 'chore(release): Set core metadata [skip release]'",
"ts-coverage": "lerna run ts-coverage",
"prepare": "husky && ./scripts/set-preid-versions.sh"
"prepare": "husky && ./scripts/setup-git-secrets.sh && ./scripts/set-preid-versions.sh"
},
"workspaces": {
"packages": [
Expand Down Expand Up @@ -136,6 +136,7 @@
"webpack-cli": "^5.0.0"
},
"resolutions": {
"@react-native-community/cli": "^18.0.1",
"@types/babel__traverse": "7.20.0",
"path-scurry": "1.10.0",
"**/glob/minipass": "6.0.2",
Expand All @@ -146,9 +147,11 @@
"**/brace-expansion": "1.1.12",
"**/on-headers": "1.1.0",
"**/form-data": "4.0.4",
"tmp": "^0.2.4"
"tmp": "^0.2.4",
"js-yaml": "4.1.1"
},
"overrides": {
"js-yaml": "4.1.1",
"tar": "6.2.1",
"cross-spawn": "7.0.5",
"brace-expansion": "1.1.12",
Expand Down
108 changes: 108 additions & 0 deletions packages/api-rest/__tests__/apis/common/publicApis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -681,5 +681,113 @@ describe('public APIs', () => {
expect(result).toEqual({ retryable: false });
});
});

describe('defaultAuthMode option', () => {
it('should skip credential resolution when defaultAuthMode is "none"', async () => {
mockFetchAuthSession.mockClear();

await fn(mockAmplifyInstance, {
apiName: 'restApi1',
path: '/public',
options: {
defaultAuthMode: 'none',
},
}).response;

expect(mockFetchAuthSession).not.toHaveBeenCalled();
expect(mockUnauthenticatedHandler).toHaveBeenCalled();
expect(mockAuthenticatedHandler).not.toHaveBeenCalled();
});

it('should resolve credentials when defaultAuthMode is "iam"', async () => {
mockFetchAuthSession.mockResolvedValue({
credentials: {
accessKeyId: 'test-key',
secretAccessKey: 'test-secret',
},
});

await fn(mockAmplifyInstance, {
apiName: 'restApi1',
path: '/private',
options: {
defaultAuthMode: 'iam',
},
}).response;

expect(mockFetchAuthSession).toHaveBeenCalled();
expect(mockAuthenticatedHandler).toHaveBeenCalled();
});

it('should maintain default behavior when no defaultAuthMode specified', async () => {
mockFetchAuthSession.mockResolvedValue({
credentials: null,
});

await fn(mockAmplifyInstance, {
apiName: 'restApi1',
path: '/endpoint',
}).response;

expect(mockFetchAuthSession).toHaveBeenCalled();
expect(mockUnauthenticatedHandler).toHaveBeenCalled();
});

it('should use global defaultAuthMode configuration when no local defaultAuthMode is specified', async () => {
const mockAmplifyWithGlobalConfig = {
...mockAmplifyInstance,
libraryOptions: {
...mockAmplifyInstance.libraryOptions,
API: {
...mockAmplifyInstance.libraryOptions?.API,
REST: {
defaultAuthMode: 'none' as const,
},
},
},
} as any as AmplifyClassV6;

mockFetchAuthSession.mockClear();

await fn(mockAmplifyWithGlobalConfig, {
apiName: 'restApi1',
path: '/public',
}).response;

expect(mockFetchAuthSession).not.toHaveBeenCalled();
expect(mockUnauthenticatedHandler).toHaveBeenCalled();
expect(mockAuthenticatedHandler).not.toHaveBeenCalled();
});

it('should override global defaultAuthMode with local defaultAuthMode configuration', async () => {
const mockAmplifyWithGlobalConfig = {
...mockAmplifyInstance,
libraryOptions: {
...mockAmplifyInstance.libraryOptions,
API: {
...mockAmplifyInstance.libraryOptions?.API,
REST: {
defaultAuthMode: 'none' as const,
},
},
},
} as any as AmplifyClassV6;

mockFetchAuthSession.mockClear();
mockFetchAuthSession.mockResolvedValue({ credentials });

await fn(mockAmplifyWithGlobalConfig, {
apiName: 'restApi1',
path: '/private',
options: {
defaultAuthMode: 'iam',
},
}).response;

expect(mockFetchAuthSession).toHaveBeenCalled();
expect(mockAuthenticatedHandler).toHaveBeenCalled();
expect(mockUnauthenticatedHandler).not.toHaveBeenCalled();
});
});
});
});
19 changes: 16 additions & 3 deletions packages/api-rest/src/apis/common/transferHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import {
import {
AWSCredentials,
DocumentType,
RESTAuthMode,
RetryStrategy,
} from '@aws-amplify/core/internals/utils';

import {
logger,
parseRestApiServiceError,
parseSigningInfo,
resolveLibraryOptions,
} from '../../utils';
import { resolveHeaders } from '../../utils/resolveHeaders';
import { RestApiResponse, SigningServiceInfo } from '../../types';
Expand All @@ -30,6 +32,7 @@ type HandlerOptions = Omit<HttpRequest, 'body' | 'headers'> & {
headers?: Headers;
withCredentials?: boolean;
retryStrategy?: RetryStrategy;
defaultAuthMode?: RESTAuthMode;
};

type RetryDecider = RetryOptions['retryDecider'];
Expand Down Expand Up @@ -75,19 +78,29 @@ export const transferHandler = async (
method,
body: resolvedBody,
};
const {
retryStrategy: libraryRetryStrategy,
defaultAuthMode: libraryDefaultAuthMode,
} = resolveLibraryOptions(amplify);
const baseOptions = {
retryDecider: getRetryDeciderFromStrategy(
retryStrategy ?? amplify?.libraryOptions?.API?.REST?.retryStrategy,
retryStrategy ?? libraryRetryStrategy,
),
computeDelay: jitteredBackoff,
withCrossDomainCredentials: withCredentials,
abortSignal,
};

const isIamAuthApplicable = iamAuthApplicable(request, signingServiceInfo);
const defaultAuthMode = options.defaultAuthMode ?? libraryDefaultAuthMode;

let credentials: AWSCredentials | null = null;
if (defaultAuthMode !== 'none') {
credentials = await resolveCredentials(amplify);
}

let response: RestApiResponse;
const credentials = await resolveCredentials(amplify);
const isIamAuthApplicable = iamAuthApplicable(request, signingServiceInfo);

if (isIamAuthApplicable && credentials) {
const signingInfoFromUrl = parseSigningInfo(url);
const signingService =
Expand Down
7 changes: 6 additions & 1 deletion packages/api-rest/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { DocumentType, RetryStrategy } from '@aws-amplify/core/internals/utils';
import {
DocumentType,
RESTAuthMode,
RetryStrategy,
} from '@aws-amplify/core/internals/utils';

export type GetInput = ApiInput<RestApiOptionsBase>;
export type PostInput = ApiInput<RestApiOptionsBase>;
Expand Down Expand Up @@ -41,6 +45,7 @@ export interface RestApiOptionsBase {
* @default ` { strategy: 'jittered-exponential-backoff' } `
*/
retryStrategy?: RetryStrategy;
defaultAuthMode?: RESTAuthMode;
/**
* custom timeout in milliseconds.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/api-rest/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export { createCancellableOperation } from './createCancellableOperation';
export { parseSigningInfo } from './parseSigningInfo';
export { parseRestApiServiceError } from './serviceError';
export { resolveApiUrl } from './resolveApiUrl';
export { resolveLibraryOptions } from './resolveLibraryOptions';
export { logger } from './logger';
Loading
Loading