64 lines
2.6 KiB
Go
64 lines
2.6 KiB
Go
package research
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.cin.su/m/Assistant/contracts/epistemics"
|
|
)
|
|
|
|
var ErrRepositoryPathRejected = errors.New("repository source path rejected")
|
|
|
|
type RepositoryConnector struct{ Root string }
|
|
|
|
func (c RepositoryConnector) Collect(ctx context.Context, request epistemics.RepositoryResearchRequest, nowUnixMillis int64) (epistemics.ResearchCandidate, error) {
|
|
if err := request.Plan.Validate(); err != nil {
|
|
return epistemics.ResearchCandidate{}, err
|
|
}
|
|
if request.SourceID == "" || strings.TrimSpace(request.Statement) == "" || nowUnixMillis < 1 {
|
|
return epistemics.ResearchCandidate{}, ErrRepositoryPathRejected
|
|
}
|
|
root, err := filepath.Abs(c.Root)
|
|
if err != nil || root == "" {
|
|
return epistemics.ResearchCandidate{}, ErrRepositoryPathRejected
|
|
}
|
|
path, err := filepath.Abs(filepath.Join(root, filepath.FromSlash(request.Path)))
|
|
if err != nil || !withinRoot(root, path) {
|
|
return epistemics.ResearchCandidate{}, ErrRepositoryPathRejected
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return epistemics.ResearchCandidate{}, ctx.Err()
|
|
default:
|
|
}
|
|
content, err := os.ReadFile(path)
|
|
if err != nil || len(content) == 0 {
|
|
return epistemics.ResearchCandidate{}, fmt.Errorf("read repository source: %w", err)
|
|
}
|
|
digest := sha256.Sum256(content)
|
|
digestText := "sha256:" + hex.EncodeToString(digest[:])
|
|
key := sha256.Sum256([]byte(filepath.ToSlash(path) + "\x00" + digestText))
|
|
keyText := hex.EncodeToString(key[:])
|
|
return epistemics.ResearchCandidate{
|
|
PropositionKey: request.PropositionKey,
|
|
Statement: request.Statement,
|
|
Source: epistemics.Source{ID: request.SourceID, Kind: "repository", Locator: "file://" + filepath.ToSlash(path), Title: filepath.Base(path), Publisher: "local-repository"},
|
|
Snapshot: epistemics.SourceSnapshot{ID: epistemics.SourceSnapshotID("snapshot:" + keyText), SourceID: request.SourceID, ContentDigest: digestText, CapturedAtUnixMillis: nowUnixMillis, Freshness: epistemics.FreshnessCurrent},
|
|
Excerpt: epistemics.SourceExcerpt{ID: epistemics.SourceExcerptID("excerpt:" + keyText), SnapshotID: epistemics.SourceSnapshotID("snapshot:" + keyText), Text: string(content), Start: 0, End: int64(len(content))},
|
|
Evidence: epistemics.EvidenceObserved,
|
|
Coverage: epistemics.CoverageComplete,
|
|
Conflict: epistemics.ConflictNone,
|
|
}, nil
|
|
}
|
|
|
|
func withinRoot(root, path string) bool {
|
|
rel, err := filepath.Rel(root, path)
|
|
return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) && !filepath.IsAbs(rel)
|
|
}
|