{"action":"created","comment":{"url":"https://api.github.com/repos/saltstack/salt/pulls/comments/24848959","id":24848959,"diff_hunk":"@@ -0,0 +1,531 @@\n+# -*- coding: utf-8 -*-\n+'''\n+This module makes it simple to include encrypted passwords in pillars.\n+This is most helpfull if you wish to store your pillars in source control or\n+share your pillar data with others.\n+\n+    salt-call cipher.enc mypasstoprotect\n+    75wCAZw2Dn-VEebq1TdWAC4RI-48HLDknIquXOxJ858=\n+\n+    pillarexample:\n+        user: root\n+        password: {{ salt.cipher.dec('75wCAdWAC4HLDknIquXOxJ858=') }}\n+\n+\n+The default encode and decode password is the md5sum of:\n+   `/etc/salt/pki/master/master.pem` or the keyfile contents pending on cipher.\n+\n+If you rather supply your own key use the argument `key` or `keyfile`\n+Echo adds a newline character. To stop that, use the `-n`\n+\n+    echo -n 'decipher_password' > /root/.saltpass\n+    salt-call cipher.enc mypass keyfile=/root/.saltpass\n+    salt-call cipher.enc mypass key=decipher_password\n+\n+For most ciphers the `key` and `keyfile` is limited to the first 32 bytes.\n+Anything longer will cause the first 32bytes of the md5 of the key to be used.\n+This is helpfull on 'pem' files because many have the same first 32bytes.\n+\n+If you use the default key remember to keep a safe backup!\n+\n+The following optional configurations can be defined in the\n+minion, master config or pillar.\n+\n+    cipher.config:\n+        key: None\n+        keyfile: /root/.saltcipher\n+\n+Also every call can override the above defaults:\n+If a `key` is defined it is used over a `keyfile`.\n+\n+\n+    salt-call cipher.enc 'mypassonlymastercandec' keyfile='/root/.saltcipher'\n+'''\n+import base64\n+import hashlib\n+import os\n+import salt.utils\n+\n+REQ_ERROR = {\n+    'crypto': None,\n+    'crypto_rsa': None,\n+    'openssl': None,\n+    'libnacl': None,\n+}\n+\n+__virtualname__ = 'cipher'\n+\n+try:\n+    import libnacl.secret\n+except ImportError as e:\n+    REQ_ERROR['libnacl'] = 'libnacl import error, perhaps missing python libnacl package'\n+\n+try:\n+    from Crypto.Cipher import AES\n+    from Crypto.Cipher import XOR\n+except ImportError as e:\n+    try:\n+        # windows and mac might have lowercase crypto ?\n+        from crypto.cipher import AES\n+    except ImportError as e:\n+        REQ_ERROR['crypto'] = 'import error, missing python Crypto package (install pycrypto)'\n+\n+try:\n+    from Crypto.PublicKey import RSA\n+    if 'importKey' not in dir(RSA):\n+        REQ_ERROR['crypto_rsa'] = 'python lib pycrypto is missing requirements. Likly need new version.'\n+except ImportError as e:\n+    REQ_ERROR['crypto_rsa'] = 'import error, missing python Crypto package'\n+\n+\n+if salt.utils.is_windows() or salt.utils.which('openssl') is None:\n+    REQ_ERROR['openssl'] = 'missing binary openssl'\n+\n+\n+def _get_config(**kwargs):\n+    '''\n+    Return configuration\n+    '''\n+    config = {\n+        'key': None,\n+        'keyfile': '/etc/salt/pki/master/master.pem',\n+    }\n+    if '__salt__' in globals():\n+        config_key = '{0}.config'.format(__virtualname__)\n+        config.update(__salt__['config.get'](config_key, {}))\n+    for k in set(config.keys()) & set(kwargs.keys()):\n+        config[k] = kwargs[k]\n+    return config\n+\n+\n+def _get_key(size_limit=None, trim_newline=False, **kwargs):\n+    config = _get_config(**kwargs)\n+    key = config['key']\n+    if not key:\n+        with open(config['keyfile'], 'rb') as keyf:\n+            key = keyf.read()\n+            if trim_newline:\n+                key = key.rstrip('\\n')\n+    key = str(key)\n+    #key = key.encode('ascii','ignore')\n+    if size_limit and len(key) > size_limit:\n+        #m = base64.b64encode(hashlib.sha256(key).digest())\n+        m = base64.b64encode(hashlib.md5(key).digest())\n+        key = m[:size_limit]\n+    return key\n+\n+\n+def enc(data, **kwargs):\n+    '''\n+    alias for aes_enc\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.enc\n+    '''\n+    return aes_enc(data=data, **kwargs)\n+\n+\n+def dec(data, **kwargs):\n+    '''\n+    alias for aes_dec\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.dec\n+    '''\n+    return aes_dec(data=data, **kwargs)\n+\n+\n+def libnacl_keygen():\n+    '''\n+    Use libnacl to generate a private key\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call --out=newline_values_only cipher.libnacl_keygen > /root/.naclkey\n+    '''\n+    if REQ_ERROR['libnacl']:\n+        raise Exception(REQ_ERROR['libnacl'])\n+    b = libnacl.secret.SecretBox()\n+    return base64.b64encode(b.sk)\n+\n+\n+def libnacl_enc(data, **kwargs):\n+    '''\n+    Takes a key generated from `cipher.libnacl_keygen` and encrypt some data.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.libnacl_enc datatoenc keyfile=/root/.naclkey\n+    '''\n+    if REQ_ERROR['libnacl']:\n+        raise Exception(REQ_ERROR['libnacl'])\n+    key = _get_key(**kwargs)\n+    sk = base64.b64decode(key)\n+    b = libnacl.secret.SecretBox(key=sk)\n+    return base64.b64encode(b.encrypt(data))\n+\n+\n+def libnacl_dec(data, **kwargs):\n+    '''\n+    Takes a key generated from `cipher.libnacl_keygen` and decrypt some data.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.libnacl_dec pEXHQM6cuaF7A= keyfile=/root/.naclkey\n+    '''\n+    if REQ_ERROR['libnacl']:\n+        raise Exception(REQ_ERROR['libnacl'])\n+    key = _get_key(**kwargs)\n+    sk = base64.b64decode(key)\n+    b = libnacl.secret.SecretBox(key=sk)\n+    return b.decrypt(base64.b64decode(data))\n+\n+\n+def rsa_keygen(keysize=1024, passphrase=None):\n+    '''\n+    Use PyCrypto to generate a RSA PEM.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call --out=newline_values_only cipher.rsa_keygen > /root/.rsakey\n+        salt-call --out=newline_values_only cipher.rsa_keygen keysize=1024 > /root/.rsakey\n+        salt-call --out=newline_values_only cipher.rsa_keygen passphrase=pp > /root/.rsakey\n+    '''\n+    if REQ_ERROR['crypto_rsa']:\n+        raise Exception(REQ_ERROR['crypto_rsa'])\n+    key = RSA.generate(keysize, os.urandom)\n+    return key.exportKey('PEM', passphrase=passphrase)\n+\n+\n+def rsa_enc(data, passphrase=None, **kwargs):\n+    '''\n+    Takes a key generated from `cipher.rsa_keygen` and encrypt data.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.rsa_enc datatoenc keyfile=/root/.rsakey\n+        salt-call cipher.rsa_enc datatoenc passphrase=pp keyfile=/root/.rsakey\n+    '''\n+    if REQ_ERROR['crypto_rsa']:\n+        raise Exception(REQ_ERROR['crypto_rsa'])\n+    key = _get_key(**kwargs)\n+    #conf = _get_config(**kwargs)\n+    rsakey = RSA.importKey(key, passphrase=passphrase)\n+    rsapubkey = rsakey.publickey()\n+    c = rsapubkey.encrypt(data, None)[0]\n+    return base64.b64encode(c)\n+\n+\n+def rsa_dec(data, passphrase=None, **kwargs):\n+    '''\n+    Takes a key generated from `cipher.rsa_keygen` and decrypt data.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.rsa_dec pEXHQM6cuaF7A= keyfile=/root/.rsakey\n+    '''\n+    if REQ_ERROR['crypto_rsa']:\n+        raise Exception(REQ_ERROR['crypto_rsa'])\n+    key = _get_key(**kwargs)\n+    data = base64.b64decode(data)\n+    conf = _get_config(**kwargs)\n+    key = RSA.importKey(conf['keyfile'], passphrase=passphrase)\n+    return key.decrypt(data)\n+\n+\n+def aes_enc(data, **kwargs):\n+    '''\n+    Use PyCrypto to encrypt data as AES.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.aes_enc datatoenc\n+        salt-call cipher.aes_enc datatoenc key=nevertell\n+        salt-call cipher.aes_enc datatoenc keyfile=/root/.strkey\n+    '''\n+    # https://github.com/ryanlim/totp-manager/blob/master/totp.py\n+    if REQ_ERROR['crypto']:\n+        raise Exception(REQ_ERROR['crypto'])\n+    key = _get_key(**kwargs)\n+    key = hashlib.sha256(key).digest()\n+    data_pad = 16 - len(data) % 16\n+    data = data + (data_pad * chr(data_pad))\n+    iv_bytes = os.urandom(16)\n+    cypher = AES.new(key, AES.MODE_CBC, iv_bytes)\n+    data = iv_bytes + cypher.encrypt(data)\n+    return base64.b64encode(data)\n+\n+\n+def aes_dec(data, **kwargs):\n+    '''\n+    Use PyCrypto to decrypt AES data.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.aes_dec U2FsdGVkX1mDwGjiVk3wpt3uQ=\n+        salt-call cipher.aes_dec data='U2FsdGVkX1+uNjiVk3wpt3uQ=' keyfile=/root/.strkey\n+        salt-call cipher.aes_dec data='KZQFJEAoUQBIAsyNpxXX57Gs=' key=nevertell\n+    '''\n+    if REQ_ERROR['crypto']:\n+        raise Exception(REQ_ERROR['crypto'])\n+    key = _get_key(**kwargs)\n+    key = hashlib.sha256(key).digest()\n+    data = base64.b64decode(data)\n+    iv_bytes = data[:16]\n+    data = data[16:]\n+    cypher = AES.new(key, AES.MODE_CBC, iv_bytes)\n+    data = cypher.decrypt(data)\n+    return data[:-ord(data[-1])]\n+\n+\n+def _openssl_get_key_and_iv(password, salt, klen=32, ilen=16):\n+    '''\n+    CITATION: http://stackoverflow.com/questions/13907841/implement-openssl-aes-encryption-in-python\n+    '''\n+    maxlen = klen + ilen\n+    keyiv = hashlib.md5(password + salt).digest()\n+    tmp = [keyiv]\n+    while len(tmp) < maxlen:\n+        tmp.append(hashlib.md5(tmp[-1] + password + salt).digest())\n+        keyiv += tmp[-1]\n+        key = keyiv[:klen]\n+        iv = keyiv[klen:klen+ilen]\n+    return key, iv\n+\n+\n+def aes_openssl_enc(data, **kwargs):\n+    '''\n+    Use PyCrypto to encrypt data in openssl aes-256-cbc compatible format.\n+\n+    If the `key` is larger than 32 bytes.\n+    The md5sum of the key will be used as the password.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.aes_openssl_enc datatoenc\n+        salt-call cipher.aes_openssl_enc datatoenc key=nevertell\n+        salt-call cipher.aes_openssl_enc datatoenc keyfile=/root/.strkey\n+    '''\n+    # https://github.com/ryanlim/totp-manager/blob/master/totp.py\n+    if REQ_ERROR['crypto']:\n+        raise Exception(REQ_ERROR['crypto'])\n+    key = _get_key(size_limit=32, trim_newline=True, **kwargs)\n+    salt = os.urandom(8)\n+    key, iv = _openssl_get_key_and_iv(key, salt)\n+    if key is None:\n+        return None\n+    data_pad = 16 - len(data) % 16\n+    data = data + (data_pad * chr(data_pad))\n+    cypher = AES.new(key, AES.MODE_CBC, iv)\n+    data = 'Salted__' + salt + cypher.encrypt(data)\n+    return base64.b64encode(data)\n+\n+\n+def aes_openssl_dec(data, **kwargs):\n+    '''\n+    Use PyCrypto to decrypt data in openssl aes-256-cbc format.\n+\n+    If the `key` is larger than 32 bytes.\n+    The md5sum of the key will be used as the password.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.aes_openssl_dec U2FsdGVkX1DhmDwGjiVk3wpt3uQ=\n+        salt-call cipher.aes_openssl_dec data='U2FsdGVkX1+uNmDwGjiVk3wpt3uQ=' keyfile=/root/.strkey\n+        salt-call cipher.aes_openssl_dec data='KZQFJEAoUQBIA6iOxsyNpxXX57Gs=' key=nevertell\n+    '''\n+    if REQ_ERROR['crypto']:\n+        raise Exception(REQ_ERROR['crypto'])\n+    key = _get_key(size_limit=32, trim_newline=True, **kwargs)\n+    data = base64.b64decode(data)\n+    assert data[:8] == 'Salted__'\n+    salt = data[8:16]\n+    key, iv = _openssl_get_key_and_iv(key, salt)\n+    if key is None:\n+        return None\n+    ciphertext = data[16:]\n+    cipher = AES.new(key, AES.MODE_CBC, iv)\n+    padded_plaintext = cipher.decrypt(ciphertext)\n+    padding_len = ord(padded_plaintext[-1])\n+    plaintext = padded_plaintext[:-padding_len]\n+    return plaintext\n+\n+\n+def openssl_enc(data, **kwargs):\n+    '''\n+    Use openssl to encrypt AES data.\n+    Prefer `cipher.aes_enc`\n+\n+    If the `key` is larger than 32 bytes.\n+    The md5sum of the key will be used as the password.\n+    If the key is not of block size 16 use PKCS#5 padding\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.openssl_enc datatoenc\n+        salt-call cipher.openssl_enc datatoenc key=nevertell\n+        salt-call cipher.openssl_enc datatoenc keyfile=/root/.strkey\n+    '''\n+    if REQ_ERROR['openssl']:\n+        raise Exception(REQ_ERROR['openssl'])\n+    key = _get_key(size_limit=32, trim_newline=True, **kwargs)\n+    cmd = 'openssl enc -e -base64 -aes-256-cbc -pass env:passwd'\n+    r = __salt__['cmd.run'](env={'passwd': key}, stdin=data, cmd=cmd)\n+    return r\n+\n+\n+def openssl_dec(data, **kwargs):\n+    '''\n+    Use openssl to decrypt AES data.\n+    Prefer `cipher.aes_dec`\n+\n+    If the `key` is larger than 32 bytes.\n+    The md5sum of the key will be used as the password.\n+\n+    CLI Examples:\n+\n+    .. code-block:: bash\n+\n+        salt-call cipher.openssl_dec U2FsdGVkX13uQ=\n+        salt-call cipher.openssl_dec data='U2FsdGVkX13uQ=' key=nevertell\n+        salt-call cipher.openssl_dec data='U2FsdGVkX13uQ=' keyfile=/root/.strkey\n+    '''\n+    if REQ_ERROR['openssl']:\n+        raise Exception(REQ_ERROR['openssl'])\n+    key = _get_key(size_limit=32, trim_newline=True, **kwargs)\n+    #cmd = 'openssl enc -d -base64 -aes-256-cbc -pass env:passwd'\n+    #r = __salt__['cmd.run'](env={'passwd': key}, stdin=data, cmd=cmd)\n+    ## TDO: stdin gives .. error reading input file\n+    cmd = ' echo -e \"{0}\" | openssl enc -d -base64 -aes-256-cbc -pass env:passwd'","path":"salt/modules/cipher.py","position":427,"original_position":427,"commit_id":"87eeb23782e6b4f25ea18dc00224818e0a136e74","original_commit_id":"87eeb23782e6b4f25ea18dc00224818e0a136e74","user":{"login":"steverweber","id":1800619,"avatar_url":"https://avatars.githubusercontent.com/u/1800619?v=3","gravatar_id":"","url":"https://api.github.com/users/steverweber","html_url":"https://github.com/steverweber","followers_url":"https://api.github.com/users/steverweber/followers","following_url":"https://api.github.com/users/steverweber/following{/other_user}","gists_url":"https://api.github.com/users/steverweber/gists{/gist_id}","starred_url":"https://api.github.com/users/steverweber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steverweber/subscriptions","organizations_url":"https://api.github.com/users/steverweber/orgs","repos_url":"https://api.github.com/users/steverweber/repos","events_url":"https://api.github.com/users/steverweber/events{/privacy}","received_events_url":"https://api.github.com/users/steverweber/received_events","type":"User","site_admin":false},"body":"it dont seem to work for openssl... if you run the command manually.. ctrl+d does not seem to end the stdin stream.. perhaps that's related to the issue when using stdin=data","created_at":"2015-02-17T20:06:38Z","updated_at":"2015-02-17T20:06:38Z","html_url":"https://github.com/saltstack/salt/pull/20718#discussion_r24848959","pull_request_url":"https://api.github.com/repos/saltstack/salt/pulls/20718","_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments/24848959"},"html":{"href":"https://github.com/saltstack/salt/pull/20718#discussion_r24848959"},"pull_request":{"href":"https://api.github.com/repos/saltstack/salt/pulls/20718"}}},"pull_request":{"url":"https://api.github.com/repos/saltstack/salt/pulls/20718","id":29344409,"html_url":"https://github.com/saltstack/salt/pull/20718","diff_url":"https://github.com/saltstack/salt/pull/20718.diff","patch_url":"https://github.com/saltstack/salt/pull/20718.patch","issue_url":"https://api.github.com/repos/saltstack/salt/issues/20718","number":20718,"state":"open","locked":false,"title":"add cipher module","user":{"login":"steverweber","id":1800619,"avatar_url":"https://avatars.githubusercontent.com/u/1800619?v=3","gravatar_id":"","url":"https://api.github.com/users/steverweber","html_url":"https://github.com/steverweber","followers_url":"https://api.github.com/users/steverweber/followers","following_url":"https://api.github.com/users/steverweber/following{/other_user}","gists_url":"https://api.github.com/users/steverweber/gists{/gist_id}","starred_url":"https://api.github.com/users/steverweber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steverweber/subscriptions","organizations_url":"https://api.github.com/users/steverweber/orgs","repos_url":"https://api.github.com/users/steverweber/repos","events_url":"https://api.github.com/users/steverweber/events{/privacy}","received_events_url":"https://api.github.com/users/steverweber/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-02-16T00:32:26Z","updated_at":"2015-02-17T20:06:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d4c3069a8d882b28b7e1a53666fe45658827224e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/saltstack/salt/pulls/20718/commits","review_comments_url":"https://api.github.com/repos/saltstack/salt/pulls/20718/comments","review_comment_url":"https://api.github.com/repos/saltstack/salt/pulls/comments/{number}","comments_url":"https://api.github.com/repos/saltstack/salt/issues/20718/comments","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/87eeb23782e6b4f25ea18dc00224818e0a136e74","head":{"label":"steverweber:cipher","ref":"cipher","sha":"87eeb23782e6b4f25ea18dc00224818e0a136e74","user":{"login":"steverweber","id":1800619,"avatar_url":"https://avatars.githubusercontent.com/u/1800619?v=3","gravatar_id":"","url":"https://api.github.com/users/steverweber","html_url":"https://github.com/steverweber","followers_url":"https://api.github.com/users/steverweber/followers","following_url":"https://api.github.com/users/steverweber/following{/other_user}","gists_url":"https://api.github.com/users/steverweber/gists{/gist_id}","starred_url":"https://api.github.com/users/steverweber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steverweber/subscriptions","organizations_url":"https://api.github.com/users/steverweber/orgs","repos_url":"https://api.github.com/users/steverweber/repos","events_url":"https://api.github.com/users/steverweber/events{/privacy}","received_events_url":"https://api.github.com/users/steverweber/received_events","type":"User","site_admin":false},"repo":{"id":30805653,"name":"salt","full_name":"steverweber/salt","owner":{"login":"steverweber","id":1800619,"avatar_url":"https://avatars.githubusercontent.com/u/1800619?v=3","gravatar_id":"","url":"https://api.github.com/users/steverweber","html_url":"https://github.com/steverweber","followers_url":"https://api.github.com/users/steverweber/followers","following_url":"https://api.github.com/users/steverweber/following{/other_user}","gists_url":"https://api.github.com/users/steverweber/gists{/gist_id}","starred_url":"https://api.github.com/users/steverweber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steverweber/subscriptions","organizations_url":"https://api.github.com/users/steverweber/orgs","repos_url":"https://api.github.com/users/steverweber/repos","events_url":"https://api.github.com/users/steverweber/events{/privacy}","received_events_url":"https://api.github.com/users/steverweber/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/steverweber/salt","description":"Infrastructure automation and management system","fork":true,"url":"https://api.github.com/repos/steverweber/salt","forks_url":"https://api.github.com/repos/steverweber/salt/forks","keys_url":"https://api.github.com/repos/steverweber/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/steverweber/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/steverweber/salt/teams","hooks_url":"https://api.github.com/repos/steverweber/salt/hooks","issue_events_url":"https://api.github.com/repos/steverweber/salt/issues/events{/number}","events_url":"https://api.github.com/repos/steverweber/salt/events","assignees_url":"https://api.github.com/repos/steverweber/salt/assignees{/user}","branches_url":"https://api.github.com/repos/steverweber/salt/branches{/branch}","tags_url":"https://api.github.com/repos/steverweber/salt/tags","blobs_url":"https://api.github.com/repos/steverweber/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/steverweber/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/steverweber/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/steverweber/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/steverweber/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/steverweber/salt/languages","stargazers_url":"https://api.github.com/repos/steverweber/salt/stargazers","contributors_url":"https://api.github.com/repos/steverweber/salt/contributors","subscribers_url":"https://api.github.com/repos/steverweber/salt/subscribers","subscription_url":"https://api.github.com/repos/steverweber/salt/subscription","commits_url":"https://api.github.com/repos/steverweber/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/steverweber/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/steverweber/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/steverweber/salt/issues/comments/{number}","contents_url":"https://api.github.com/repos/steverweber/salt/contents/{+path}","compare_url":"https://api.github.com/repos/steverweber/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/steverweber/salt/merges","archive_url":"https://api.github.com/repos/steverweber/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/steverweber/salt/downloads","issues_url":"https://api.github.com/repos/steverweber/salt/issues{/number}","pulls_url":"https://api.github.com/repos/steverweber/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/steverweber/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/steverweber/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/steverweber/salt/labels{/name}","releases_url":"https://api.github.com/repos/steverweber/salt/releases{/id}","created_at":"2015-02-14T19:03:30Z","updated_at":"2015-02-14T19:03:39Z","pushed_at":"2015-02-17T18:38:30Z","git_url":"git://github.com/steverweber/salt.git","ssh_url":"git@github.com:steverweber/salt.git","clone_url":"https://github.com/steverweber/salt.git","svn_url":"https://github.com/steverweber/salt","homepage":"saltstack.com","size":83932,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"saltstack:develop","ref":"develop","sha":"d2ec0e0f8d90f78a478e5dca81fcab1a1c65e9cf","user":{"login":"saltstack","id":1147473,"avatar_url":"https://avatars.githubusercontent.com/u/1147473?v=3","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"repo":{"id":1390248,"name":"salt","full_name":"saltstack/salt","owner":{"login":"saltstack","id":1147473,"avatar_url":"https://avatars.githubusercontent.com/u/1147473?v=3","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/saltstack/salt","description":"Infrastructure automation and management system","fork":false,"url":"https://api.github.com/repos/saltstack/salt","forks_url":"https://api.github.com/repos/saltstack/salt/forks","keys_url":"https://api.github.com/repos/saltstack/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/saltstack/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/saltstack/salt/teams","hooks_url":"https://api.github.com/repos/saltstack/salt/hooks","issue_events_url":"https://api.github.com/repos/saltstack/salt/issues/events{/number}","events_url":"https://api.github.com/repos/saltstack/salt/events","assignees_url":"https://api.github.com/repos/saltstack/salt/assignees{/user}","branches_url":"https://api.github.com/repos/saltstack/salt/branches{/branch}","tags_url":"https://api.github.com/repos/saltstack/salt/tags","blobs_url":"https://api.github.com/repos/saltstack/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/saltstack/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/saltstack/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/saltstack/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/saltstack/salt/languages","stargazers_url":"https://api.github.com/repos/saltstack/salt/stargazers","contributors_url":"https://api.github.com/repos/saltstack/salt/contributors","subscribers_url":"https://api.github.com/repos/saltstack/salt/subscribers","subscription_url":"https://api.github.com/repos/saltstack/salt/subscription","commits_url":"https://api.github.com/repos/saltstack/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/saltstack/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/saltstack/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/saltstack/salt/issues/comments/{number}","contents_url":"https://api.github.com/repos/saltstack/salt/contents/{+path}","compare_url":"https://api.github.com/repos/saltstack/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/saltstack/salt/merges","archive_url":"https://api.github.com/repos/saltstack/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/saltstack/salt/downloads","issues_url":"https://api.github.com/repos/saltstack/salt/issues{/number}","pulls_url":"https://api.github.com/repos/saltstack/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/saltstack/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/saltstack/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/saltstack/salt/labels{/name}","releases_url":"https://api.github.com/repos/saltstack/salt/releases{/id}","created_at":"2011-02-20T20:16:56Z","updated_at":"2015-02-17T20:02:44Z","pushed_at":"2015-02-17T20:02:44Z","git_url":"git://github.com/saltstack/salt.git","ssh_url":"git@github.com:saltstack/salt.git","clone_url":"https://github.com/saltstack/salt.git","svn_url":"https://github.com/saltstack/salt","homepage":"saltstack.com","size":181199,"stargazers_count":4712,"watchers_count":4712,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1932,"mirror_url":null,"open_issues_count":2239,"forks":1932,"open_issues":2239,"watchers":4712,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/20718"},"html":{"href":"https://github.com/saltstack/salt/pull/20718"},"issue":{"href":"https://api.github.com/repos/saltstack/salt/issues/20718"},"comments":{"href":"https://api.github.com/repos/saltstack/salt/issues/20718/comments"},"review_comments":{"href":"https://api.github.com/repos/saltstack/salt/pulls/20718/comments"},"review_comment":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/saltstack/salt/pulls/20718/commits"},"statuses":{"href":"https://api.github.com/repos/saltstack/salt/statuses/87eeb23782e6b4f25ea18dc00224818e0a136e74"}}}}