Pular para o conteúdo principal

Pipeline CI/CD

Criando uma Security Action (ação de segurança) no GitHub Actions usando Remote Quick Command

Atualmente, as ferramentas SCM não fornecem automação direta e imediata dos processos de desenvolvimento. Em vez disso, as automações são realizadas localmente e integradas com pipelines CI/CD para verificar ou modificar implementações de código enviadas pela equipe de desenvolvimento. O exemplo a seguir mostra como prompts de automação de AI incentivam os engenheiros a revisar o código e realizar operações personalizadas.

Passo a passo

  1. Configuração do Prompt

    • Crie uma Security Action essencial no GitHub Action usando StackSpot AI Remote Quick Command.
    • Escreva um Quick Command que verifique as vulnerabilidades de segurança baseado nos dados recebidos. Confira um exemplo de prompt [em inglês]:
Prompt: 

Check security vulnerabilities, describe them and fix the selected code {{input_data}}

Your answer should just be following the JSON structure below:
[
{
"title": "title",
"severity": "severity",
"correction": "correction",
"lines": "lines"
}
]

Where the "title" would be a string resuming the vulnerability in 15 words maximum.

Where the "severity" would be a string representing the impact of the vulnerability, using critical, high, medium or low.

Where the "correction" would be a code suggestion to resolve the issue identified.

Where the "lines" would represent the file code lines where the vulnerability has been identified.

O prompt requer um objeto JSON como saída, que, neste caso, é um array. Ele também fornece regras detalhadas para cada campo no objeto de resposta.

  1. Autentique-se para chamar o Remote Quick Command. Para mais informações, confira a página de RQC.

Confira um exemplo de uma função chamando uma API em Python:

def get_access_token(account_slug, client_id, client_key):
url = f"https://idm.stackspot.com/{account_slug}/oidc/oauth/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
data = {
'client_id': client_id,
'grant_type': 'client_credentials',
'client_secret': client_key
}
response = requests.post(url, headers=headers, data=data)
response_data = response.json()
return response_data['access_token']
  1. Execute o Remote Quick Command. Confira o exemplo de uma função chamando uma API em Python:
def create_rqc_execution(qc_slug, access_token, input_data):
url = f"https://genai-code-buddy-api.stackspot.com/v1/quick-commands/create-execution/{qc_slug}"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {access_token}'
}
data = {
'input_data': input_data
}
response = requests.post(
url,
headers=headers,
json=data
)

if response.status_code == 200:
decoded_content = response.content.decode('utf-8') # Decode bytes to string
extracted_value = decoded_content.strip('"') # Strip the surrounding quotes
response_data = extracted_value
print('ExecutionID:', response_data)
return response_data
else:
print(response.status_code)
print(response.content)

  1. Verifique o Status da Execução Remota (Execução Remota). O exemplo mostra uma função chamando esta API em Python usando um atraso de 5s entre as solicitações:
def get_execution_status(execution_id, access_token):
url = f"https://genai-code-buddy-api.stackspot.com/v1/quick-commands/callback/{execution_id}"
headers = {'Authorization': f'Bearer {access_token}'}
i = 0
while True:
response = requests.get(
url,
headers=headers
)
response_data = response.json()
status = response_data['progress']['status']
if status in ['COMPLETED', 'FAILED']:
return response_data
else:
print("Status:", f'{status} ({i})')
print("Execution in progress, waiting...")
i+=1
time.sleep(5) # Wait for 5 seconds before polling again
  1. Processar os dados do resultado (Result Data). O result_data é um objeto JSON que pode ser manipulado para qualquer ação. Confirao exemplo em Python usando variáveis de ambiente:
# Replace the placeholders with your actual data
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_KEY = os.getenv("CLIENT_KEY")
ACCOUNT_SLUG = os.getenv("CLIENT_REALM")
QC_SLUG = os.getenv("QC_SLUG")
INPUT_DATA = os.getenv("INPUT_DATA")

# Execute the steps
access_token = get_access_token(ACCOUNT_SLUG, CLIENT_ID, CLIENT_KEY)
execution_id = create_rqc_execution(QC_SLUG, access_token, INPUT_DATA)
execution_status = get_execution_status(execution_id, access_token)

result = execution_status['result']

# Remove the leading and trailing ```json and ``` for correct JSON parsing
if result.startswith("```json"):
result = result[7:-4].strip()

result_data = json.loads(result)
  1. Implementar em uma Pipeline CI/CD usando GitHub Actions

Confira um exemplo de como o script é no arquivo action.yaml:

 - name: Run Remote Quick Command
env:
CLIENT_ID: ${{ inputs.CLIENT_ID }}
CLIENT_KEY: ${{ inputs.CLIENT_KEY }}
CLIENT_REALM: ${{ inputs.CLIENT_REALM }}
QC_SLUG: ${{ inputs.QC_SLUG }}
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: python3 ${{ github.action_path }}/rqc.py
shell: bash

  • Adapte o script Python para extrair a variável CHANGED_FILES e realizar um loop com a lista retornada. Exemplo em Python:
 # Replace the placeholders with your actual data
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_KEY = os.getenv("CLIENT_KEY")
ACCOUNT_SLUG = os.getenv("CLIENT_REALM")
QC_SLUG = os.getenv("QC_SLUG")
CHANGED_FILES = os.getenv("CHANGED_FILES")

print(f'\033[36mFiles to analyze: {CHANGED_FILES}\033[0m')
CHANGED_FILES = ast.literal_eval(CHANGED_FILES)
for file_path in CHANGED_FILES:
print(f'\n\033[36mFile Path: {file_path}\033[0m')
# Open the file and read its content
with open(file_path, 'r') as file:
file_content = file.read()

# Execute the steps
access_token = get_access_token(ACCOUNT_SLUG, CLIENT_ID, CLIENT_KEY)
execution_id = create_rqc_execution(QC_SLUG, access_token, file_content)
execution_status = get_execution_status(execution_id, access_token)
result = execution_status['result']
# Remove the leading and trailing ```json and ``` for correct JSON parsing
if result.startswith("```json"):
result = result[7:-4].strip()

result_data = json.loads(result)

vulnerabilities_amount = len(result_data)

print(f"\n\033[36m{vulnerabilities_amount} item(s) have been found for file {file_path}:\033[0m")

# Iterate through each item and print the required fields
for item in result_data:
print(f"\nTitle: {item['title']}")
print(f"Severity: {item['severity']}")
print(f"Correction: {item['correction']}")
print(f"Lines: {item['lines']}")

A última seção do código extrai os dados do objeto JSON de resultado, de acordo com o que foi configurado durante a etapa de configuração do prompt, para exibir os dados durante a execução da ação.

  1. Exemplo de Workflow
  • Para chamar esta ação, você precisa de um workflow para acionar um evento pull_request. Confira o exemplo:
 name: Action Test

on:
pull_request:

jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: <owner>/<repository>@main
with:
CLIENT_ID: ${{ secrets.CLIENT_ID }}
CLIENT_KEY: ${{ secrets.CLIENT_KEY }}
CLIENT_REALM: ${{ secrets.CLIENT_REALM }}
QC_SLUG: sast-rqc # here is the remote Quick Command slug example

Ao acionar esta pipeline, o workflow será executado e chamará esse script em Python. Ele deve retornar algo como a imagem à seguir para cada arquivo atualizado no PR:

A aba de GitHub Actions no GitHub, com os logs de execução do fluxo de trabalho, destaca a saída da ação com duas vulnerabilidades detectadas para o arquivo test2.py, detalhadas usando os campos título, severidade, correção e linhas definidos no prompt.

A ação está funcionando conforme o esperado. Com base nos resultados, considere adicionar mais recursos para melhorá-la. Confira mais exemplos:

  • Adicionar Knowledge Sources para o Quick Command usar ao verificar vulnerabilidades e mantê-las atualizadas com os dados de segurança mais recentes.
  • Gerar um relatório das vulnerabilidades do repositório, segregado por arquivo.
  • Comentar no pull request quais vulnerabilidades foram encontradas.
  • Abrir automaticamente um pull request usando AI para corrigir as vulnerabilidades detectadas.
  • Executar a ação, verificando todos os arquivos do repositório ocasionalmente para conferir se novas vulnerabilidades foram descobertas.
  • Adicionar mais verificações de vulnerabilidade (SAST, DAST, etc).

Repositório & Blog