conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

sigv4.test.js (4101B)


      1 // test/sigv4.test.js - AWS Signature Version 4
      2 //
      3 // The vectors come from the published AWS signature test suite. Getting this
      4 // wrong produces an opaque SignatureDoesNotMatch from the server, so the
      5 // reference cases are worth pinning.
      6 
      7 import test from 'node:test';
      8 import assert from 'node:assert/strict';
      9 import { signRequest, presignUrl, uriEncode, amzDate, EMPTY_SHA256 } from '../src/lib/storage/sigv4.js';
     10 
     11 const CREDS = {
     12   region: 'us-east-1',
     13   service: 'service',
     14   accessKeyId: 'AKIDEXAMPLE',
     15   secretAccessKey: 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY',
     16   date: new Date(Date.UTC(2015, 7, 30, 12, 36, 0)),
     17 };
     18 
     19 test('get-vanilla matches the reference signature', () => {
     20   const headers = signRequest({
     21     method: 'GET',
     22     url: new URL('https://example.amazonaws.com/'),
     23     payloadHash: EMPTY_SHA256,
     24     ...CREDS,
     25   });
     26 
     27   assert.equal(
     28     headers.authorization,
     29     'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, ' +
     30     'SignedHeaders=host;x-amz-date, ' +
     31     'Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31'
     32   );
     33 });
     34 
     35 test('get-vanilla-query-order-key-case matches the reference signature', () => {
     36   const headers = signRequest({
     37     method: 'GET',
     38     url: new URL('https://example.amazonaws.com/?Param2=value2&Param1=value1'),
     39     payloadHash: EMPTY_SHA256,
     40     ...CREDS,
     41   });
     42 
     43   assert.equal(
     44     headers.authorization,
     45     'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, ' +
     46     'SignedHeaders=host;x-amz-date, ' +
     47     'Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500'
     48   );
     49 });
     50 
     51 test('uriEncode percent encodes the characters encodeURIComponent leaves alone', () => {
     52   assert.equal(uriEncode("a b!'()*"), 'a%20b%21%27%28%29%2A');
     53   assert.equal(uriEncode('keep~unreserved-._'), 'keep~unreserved-._');
     54   assert.equal(uriEncode('a/b'), 'a%2Fb');
     55   assert.equal(uriEncode('a/b', false), 'a/b');
     56 });
     57 
     58 test('amzDate produces both required formats', () => {
     59   const { amz, stamp } = amzDate(new Date(Date.UTC(2015, 7, 30, 12, 36, 0)));
     60   assert.equal(amz, '20150830T123600Z');
     61   assert.equal(stamp, '20150830');
     62 });
     63 
     64 test('s3 requests carry the content hash header', () => {
     65   const headers = signRequest({
     66     method: 'PUT',
     67     url: new URL('https://bucket.example.com/key'),
     68     payloadHash: EMPTY_SHA256,
     69     region: 'us-east-1',
     70     service: 's3',
     71     accessKeyId: 'AKIDEXAMPLE',
     72     secretAccessKey: 'secret',
     73   });
     74   assert.equal(headers['x-amz-content-sha256'], EMPTY_SHA256);
     75   assert.ok(headers.authorization.includes('x-amz-content-sha256'));
     76 });
     77 
     78 test('presigned urls carry the signature in the query string', () => {
     79   const url = new URL(presignUrl({
     80     method: 'GET',
     81     url: new URL('https://example.com/bucket/key'),
     82     expires: 900,
     83     region: 'us-east-1',
     84     service: 's3',
     85     accessKeyId: 'AKIDEXAMPLE',
     86     secretAccessKey: 'secret',
     87   }));
     88 
     89   assert.equal(url.searchParams.get('X-Amz-Algorithm'), 'AWS4-HMAC-SHA256');
     90   assert.equal(url.searchParams.get('X-Amz-Expires'), '900');
     91   assert.equal(url.searchParams.get('X-Amz-SignedHeaders'), 'host');
     92   assert.match(url.searchParams.get('X-Amz-Signature'), /^[0-9a-f]{64}$/);
     93 });
     94 
     95 test('an already encoded path is not encoded a second time', () => {
     96   // A key containing a space reaches the signer as %20. Re-encoding it to
     97   // %2520 is the bug this guards against.
     98   const url = new URL('https://example.com/bucket/a%20b');
     99   const headers = signRequest({
    100     method: 'GET',
    101     url,
    102     payloadHash: EMPTY_SHA256,
    103     region: 'us-east-1',
    104     service: 's3',
    105     accessKeyId: 'AKIDEXAMPLE',
    106     secretAccessKey: 'secret',
    107   });
    108 
    109   // Signing the same path written differently must differ, proving the path
    110   // is taken verbatim rather than normalized.
    111   const other = signRequest({
    112     method: 'GET',
    113     url: new URL('https://example.com/bucket/a%2520b'),
    114     payloadHash: EMPTY_SHA256,
    115     region: 'us-east-1',
    116     service: 's3',
    117     accessKeyId: 'AKIDEXAMPLE',
    118     secretAccessKey: 'secret',
    119   });
    120 
    121   assert.notEqual(headers.authorization, other.authorization);
    122 });