diff --git a/README.md b/README.md index 64823211..7d9b7ff0 100644 --- a/README.md +++ b/README.md @@ -81,12 +81,14 @@ See [action.yml](action.yml) # URI identifying the type of the predicate. predicate-type: - # JSON string containing the value for the attestation predicate. Must - # supply exactly one of "predicate-path" or "predicate". + # String containing the value for the attestation predicate. String length + # cannot exceed 16MB. Must supply exactly one of "predicate-path" or + # "predicate". predicate: - # Path to the file which contains the JSON content for the attestation - # predicate. Must supply exactly one of "predicate-path" or "predicate". + # Path to the file which contains the content for the attestation predicate. + # File size cannot exceed 16MB. Must supply exactly one of "predicate-path" + # or "predicate". predicate-path: # Whether to push the attestation to the image registry. Requires that the @@ -124,6 +126,11 @@ processed in batches 50. After the initial group of 50, each subsequent batch will incur an exponentially increasing amount of delay (capped at 1 minute of delay per batch) to avoid overwhelming the attestation API. +### Predicate Limits + +Whether supplied via the `predicate` or `predicatePath` input, the predicate +string cannot exceed 16MB. + ## Examples ### Identify Subject by Path diff --git a/__tests__/predicate.test.ts b/__tests__/predicate.test.ts index 51d52a47..d8062653 100644 --- a/__tests__/predicate.test.ts +++ b/__tests__/predicate.test.ts @@ -78,6 +78,20 @@ describe('subjectFromInputs', () => { }) }) + describe('when specifying a predicate path that does not exist', () => { + const predicateType = 'https://example.com/predicate' + const predicatePath = 'foo' + + it('returns the predicate', () => { + const inputs: PredicateInputs = { + ...blankInputs, + predicateType, + predicatePath + } + expect(() => predicateFromInputs(inputs)).toThrow(/file not found/) + }) + }) + describe('when specifying a predicate value', () => { const predicateType = 'https://example.com/predicate' const content = '{}' @@ -95,4 +109,21 @@ describe('subjectFromInputs', () => { }) }) }) + + describe('when specifying a predicate value exceeding the max size', () => { + const predicateType = 'https://example.com/predicate' + const content = JSON.stringify({ a: 'a'.repeat(16 * 1024 * 1024) }) + + it('throws an error', () => { + const inputs: PredicateInputs = { + ...blankInputs, + predicateType, + predicate: content + } + + expect(() => predicateFromInputs(inputs)).toThrow( + /predicate string exceeds maximum/ + ) + }) + }) }) diff --git a/action.yml b/action.yml index 7ad27b22..028ee4b4 100644 --- a/action.yml +++ b/action.yml @@ -30,13 +30,15 @@ inputs: required: true predicate: description: > - String containing the value for the attestation predicate. Must supply - exactly one of "predicate-path" or "predicate". + String containing the value for the attestation predicate. String length + cannot exceed 16MB. Must supply exactly one of "predicate-path" or + "predicate". required: false predicate-path: description: > Path to the file which contains the content for the attestation predicate. - Must supply exactly one of "predicate-path" or "predicate". + File size cannot exceed 16MB. Must supply exactly one of "predicate-path" + or "predicate". required: false push-to-registry: description: > diff --git a/dist/index.js b/dist/index.js index a9f3349a..2bb0532c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -80209,6 +80209,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.predicateFromInputs = void 0; const fs_1 = __importDefault(__nccwpck_require__(57147)); +const MAX_PREDICATE_SIZE_BYTES = 16 * 1024 * 1024; // Returns the predicate specified by the action's inputs. The predicate value // may be specified as a path to a file or as a string. const predicateFromInputs = (inputs) => { @@ -80222,9 +80223,23 @@ const predicateFromInputs = (inputs) => { if (predicatePath && predicate) { throw new Error('Only one of predicate-path or predicate may be provided'); } - const params = predicatePath - ? fs_1.default.readFileSync(predicatePath, 'utf-8') - : predicate; + let params = predicate; + if (predicatePath) { + if (!fs_1.default.existsSync(predicatePath)) { + throw new Error(`predicate file not found: ${predicatePath}`); + } + /* istanbul ignore next */ + if (fs_1.default.statSync(predicatePath).size > MAX_PREDICATE_SIZE_BYTES) { + throw new Error(`predicate file exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`); + } + params = fs_1.default.readFileSync(predicatePath, 'utf-8'); + } + else { + if (predicate.length > MAX_PREDICATE_SIZE_BYTES) { + throw new Error(`predicate string exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes`); + } + params = predicate; + } return { type: predicateType, params: JSON.parse(params) }; }; exports.predicateFromInputs = predicateFromInputs; diff --git a/src/predicate.ts b/src/predicate.ts index 4e5ce041..5fe04a57 100644 --- a/src/predicate.ts +++ b/src/predicate.ts @@ -7,6 +7,9 @@ export type PredicateInputs = { predicate: string predicatePath: string } + +const MAX_PREDICATE_SIZE_BYTES = 16 * 1024 * 1024 + // Returns the predicate specified by the action's inputs. The predicate value // may be specified as a path to a file or as a string. export const predicateFromInputs = (inputs: PredicateInputs): Predicate => { @@ -24,9 +27,30 @@ export const predicateFromInputs = (inputs: PredicateInputs): Predicate => { throw new Error('Only one of predicate-path or predicate may be provided') } - const params = predicatePath - ? fs.readFileSync(predicatePath, 'utf-8') - : predicate + let params: string = predicate + + if (predicatePath) { + if (!fs.existsSync(predicatePath)) { + throw new Error(`predicate file not found: ${predicatePath}`) + } + + /* istanbul ignore next */ + if (fs.statSync(predicatePath).size > MAX_PREDICATE_SIZE_BYTES) { + throw new Error( + `predicate file exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes` + ) + } + + params = fs.readFileSync(predicatePath, 'utf-8') + } else { + if (predicate.length > MAX_PREDICATE_SIZE_BYTES) { + throw new Error( + `predicate string exceeds maximum allowed size: ${MAX_PREDICATE_SIZE_BYTES} bytes` + ) + } + + params = predicate + } return { type: predicateType, params: JSON.parse(params) } }