Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug][devLake config UI] secure PAT is exposed in network tab #7821

Closed
2 of 3 tasks
yurii-chikh-vitech opened this issue Aug 1, 2024 · 11 comments
Closed
2 of 3 tasks
Assignees
Labels
component/config-ui This issue or PR relates to config-ui pr-type/bug-fix This PR fixes a bug severity/p0 This bug blocks key user journey and function type/bug This issue is a bug

Comments

@yurii-chikh-vitech
Copy link

yurii-chikh-vitech commented Aug 1, 2024

Search before asking

  • I had searched in the issues and found no similar issues.

What happened

Secure PAT token exposed in network tab for request http://localhost:4000/api/plugins/gitlab/connections/1/scopes?page=1&pageSize=10&blueprints=true

image

What do you expect to happen

PAT should not be exposed

How to reproduce

Explore network tab on connection settings page.

http://localhost:4000/api/plugins/gitlab/connections/1/scopes?page=1&pageSize=10&blueprints=true

Anything else

No response

Version

v1.0

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

@yurii-chikh-vitech yurii-chikh-vitech added the type/bug This issue is a bug label Aug 1, 2024
@dosubot dosubot bot added component/config-ui This issue or PR relates to config-ui pr-type/bug-fix This PR fixes a bug severity/p1 This bug affects functionality or significantly affect ux labels Aug 1, 2024
Copy link

dosubot bot commented Aug 1, 2024

@yurii-chikh-vitech Hello there! I'm here to help you with any bugs, questions, or contributions you may have. Let's work together to resolve the issue you've encountered.

To prevent the secure Personal Access Token (PAT) from being exposed in the network tab for the request to the URL http://localhost:4000/api/plugins/gitlab/connections/1/scopes?page=1&pageSize=10&blueprints=true in the DevLake configuration UI, you should ensure that the token is not sent directly in the request URL or headers in a way that it can be easily intercepted.

One common approach is to use a backend service to handle the token securely. The frontend can make a request to the backend, which then adds the token and forwards the request to the GitLab API. This way, the token is never exposed to the client-side code or network tab.

Here is a simplified example of how you might implement this:

  1. Backend Service: Create an endpoint in your backend service that handles the request to GitLab.
// backend-service.js
const express = require('express');
const axios = require('axios');
const app = express();

app.get('/api/gitlab/scopes', async (req, res) => {
  const { page, pageSize, blueprints } = req.query;
  const token = process.env.GITLAB_PAT; // Securely stored token

  try {
    const response = await axios.get(`https://gitlab.com/api/v4/projects`, {
      params: { page, pageSize, blueprints },
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
    res.json(response.data);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(4000, () => {
  console.log('Backend service running on port 4000');
});
  1. Frontend Request: Modify the frontend to call your backend service instead of directly calling the GitLab API.
// config.tsx
import axios from 'axios';

const fetchGitLabScopes = async (page, pageSize, blueprints) => {
  try {
    const response = await axios.get(`http://localhost:4000/api/gitlab/scopes`, {
      params: { page, pageSize, blueprints }
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching GitLab scopes:', error);
    throw error;
  }
};

// Usage example
fetchGitLabScopes(1, 10, true).then(data => {
  console.log('GitLab Scopes:', data);
});

Additionally, in the DevLake configuration UI, you can use the ConnectionToken component to handle the token input securely. Here is an example from the Step2 component:

import { useState, useContext, useEffect, useMemo } from 'react';
import { Flex, Button, Tooltip } from 'antd';
import API from '@/api';
import { Markdown } from '@/components';
import { getPluginConfig } from '@/plugins';
import { ConnectionToken } from '@/plugins/components/connection-form/fields/token';
import { operator } from '@/utils';
import { Context } from './context';
import * as S from './styled';

const paramsMap: Record<string, any> = {
  github: {
    authMethod: 'AccessToken',
    endpoint: 'https://api.github.com/',
  },
  gitlab: {
    endpoint: 'https://gitlab.com/api/v4/',
  },
  bitbucket: {
    endpoint: 'https://api.bitbucket.org/2.0/',
  },
  azuredevops: {},
};

export const Step2 = () => {
  const [QA, setQA] = useState('');
  const [operating, setOperating] = useState(false);
  const [testing, setTesting] = useState(false);
  const [testStaus, setTestStatus] = useState(false);
  const [payload, setPayload] = useState<any>({});
  const { step, records, done, projectName, plugin, setStep, setRecords } = useContext(Context);

  const config = useMemo(() => getPluginConfig(plugin as string), [plugin]);

  useEffect(() => {
    fetch(`/onboard/step-2/${plugin}.md`)
      .then((res) => res.text())
      .then((text) => setQA(text));
  }, [plugin]);

  const handleTest = async () => {
    if (!plugin) {
      return;
    }

    const [success] = await operator(
      async () =>
        await API.connection.testOld(plugin, {
          ...paramsMap[plugin],
          ...payload,
        }),
      {
        setOperating: setTesting,
        formatMessage: () => 'Connection success.',
        formatReason: () => 'Connection failed. Please check your token or network.',
      },
    );

    if (success) {
      setTestStatus(true);
    }
  };

  const handleSubmit = async () => {
    if (!plugin) {
      return;
    }

    const [success] = await operator(
      async () => {
        const connection = await API.connection.create(plugin, {
          name: `${plugin}-${Date.now()}`,
          ...paramsMap[plugin],
          ...payload,
        });

        const newRecords = [
          ...records,
          { plugin, connectionId: connection.id, blueprintId: '', pipelineId: '', scopeName: '' },
        ];

        setRecords(newRecords);

        await API.store.set('onboard', {
          step: 3,
          records: newRecords,
          done,
          projectName,
          plugin,
        });
      },
      {
        setOperating,
        hideToast: true,
      },
    );

    if (success) {
      setStep(step + 1);
    }
  };

  if (!plugin) {
    return null;
  }

  return (
    <>
      <S.StepContent>
        {['github', 'gitlab', 'azuredevops'].includes(plugin) && (
          <div className="content">
            <ConnectionToken
              type="create"
              label="Personal Access Token"
              subLabel={`Create a personal access token in ${config.name}`}
              initialValue=""
              value={payload.token}
              setValue={(token) => {
                setPayload({ ...payload, token });
                setTestStatus(false);
              }}
              error=""
              setError={() => {}}
            />
            <Tooltip title="Test Connection">
              <Button
                style={{ marginTop: 16 }}
                type="primary"
                disabled={!payload.token}
                loading={testing}
                onClick={handleTest}
              >
                Connect
              </Button>
            </Tooltip>
          </div>
        )}
        <Markdown className="qa">{QA}</Markdown>
      </S.StepContent>
      <Flex style={{ marginTop: 36 }} justify="space-between">
        <Button ghost type="primary" loading={operating} onClick={() => setStep(step - 1)}>
          Previous Step
        </Button>
        <Button type="primary" loading={operating} disabled={!testStaus} onClick={handleSubmit}>
          Next Step
        </Button>
      </Flex>
    </>
  );
};

By using this approach, the Personal Access Token (PAT) is kept secure on the server side and is not exposed in the network tab of the browser's developer tools [1][2].

To continue talking to Dosu, mention @dosu.

@klesh
Copy link
Contributor

klesh commented Sep 6, 2024

Aah.. I agree....
Did you revoke that token already? Because I can see the whole token 🤣 ...and, with it anyone could list all your private repos...so ... it is no point to mask the other information...

@klesh klesh added severity/p0 This bug blocks key user journey and function and removed severity/p1 This bug affects functionality or significantly affect ux labels Sep 6, 2024
@klesh
Copy link
Contributor

klesh commented Sep 6, 2024

@yurii-chikh-vitech I just removed the screenshot, please revoke the token ASAP if it was not revoked already.

@yurii-chikh-vitech
Copy link
Author

yurii-chikh-vitech commented Sep 6, 2024

@yurii-chikh-vitech I just removed the screenshot, please revoke the token ASAP if it was not revoked already.

@klesh Token is old and revoked.

@d4x1
Copy link
Contributor

d4x1 commented Sep 10, 2024

@yurii-chikh-vitech I think it was fixed in #7767, all codes are cherry picked to v1.0.

which version are you using? I can help to check.

@d4x1
Copy link
Contributor

d4x1 commented Sep 10, 2024

I've tested it with latest code in branch release-v1.0, and it works.
image

@yurii-chikh-vitech
Copy link
Author

@yurii-chikh-vitech I think it was fixed in #7767, all codes are cherry picked to v1.0.

which version are you using? I can help to check.

@d4x1 v1.0

@d4x1
Copy link
Contributor

d4x1 commented Sep 11, 2024

v1.0
@yurii-chikh-vitech
Do you mean https://github.com/apache/incubator-devlake/releases/tag/v1.0.0 ?
DevLake has no version number like v1.0 .

@yurii-chikh-vitech
Copy link
Author

@d4x1 v1.0.0

@d4x1
Copy link
Contributor

d4x1 commented Sep 11, 2024

@d4x1 v1.0.0

@yurii-chikh-vitech You can try the latest version(https://github.com/apache/incubator-devlake/releases/tag/v1.0.1-beta9). It has been fixed.

@d4x1 d4x1 closed this as completed Sep 11, 2024
@oliviertassinari
Copy link

#2800 was similar in the past.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/config-ui This issue or PR relates to config-ui pr-type/bug-fix This PR fixes a bug severity/p0 This bug blocks key user journey and function type/bug This issue is a bug
Projects
None yet
Development

No branches or pull requests

4 participants